From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 155FA43081; Wed, 16 Aug 2023 16:00:06 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 01DE743255; Wed, 16 Aug 2023 16:00:06 +0200 (CEST) Received: from egress-ip4b.ess.de.barracuda.com (egress-ip4b.ess.de.barracuda.com [18.185.115.208]) by mails.dpdk.org (Postfix) with ESMTP id EED9140ED3 for ; Wed, 16 Aug 2023 16:00:04 +0200 (CEST) Received: from EUR04-HE1-obe.outbound.protection.outlook.com (mail-he1eur04lp2056.outbound.protection.outlook.com [104.47.13.56]) by mx-outbound45-49.eu-central-1c.ess.aws.cudaops.com (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Wed, 16 Aug 2023 14:00:03 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=XqvsSnYFNh2IaImCQ9Qq3/Ru2WrH+BJLjlXCqVuaKETV5P5Q5PxXYkqklz13/oDFpJoZho72UmV7VMO/SaU72q7OJciHQOFhAqzs0Ktq4vYIsKWpPsqPb+2Pw14YQ4NHRl9VD3YKu6R0dl5jhYFHoaD0RbgYvxqTYYJU+GkCzzMrjeSFkwbRtxCtvyyoN0Gv4hzIg3TDT4ed2dxOvylxjVa9O0AvnFj7XyK3Jl6rUTDVY62+dQ0vyz2ycx8b143h0E6qNhtvN8jHzc8l0xUHMlzQPXs/3XdsVscfAgtXfGkyCvI74+CzYIvA4t39RoLSSr0nbIkUsujmeNscE26BOA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=WHqw4R7Ru+Pqi5eZWDSduF1WmPUca+Fn/ng1z16rkZI=; b=FACv8M1o+iEKLeGgJR4hByMpyb81IwUAz2CUHEmC2cyjuDRVdezk8amW3CJFIDW5lj9o+Yl1UuPgZGKCZlEOfpwsrDnu56TJTgBjwKZGwHiG0xcahqpjMbe4kvQWCjNuM6yrnriCA9CY6QdPGPh5xDCc+YQAPVkNvA5JCxoemahKKlKFH+cWr7ItCJmlhbnJCUamGtz1D8aal6w3snD358AeLMbqxJSUtOlE6NSEafCGQiYD66drqRZl8Y6CjypvwForW/1VqltKezyWHaAEc6s7ShpDLtOUBgCZt9Rf6TtaVzqYl6WEbmqvmTeI/b1bmoatG3Q4JTxiGFpIOcSImQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 178.72.21.4) smtp.rcpttodomain=dpdk.org smtp.mailfrom=napatech.com; dmarc=fail (p=none sp=none pct=100) action=none header.from=napatech.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=napatech.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WHqw4R7Ru+Pqi5eZWDSduF1WmPUca+Fn/ng1z16rkZI=; b=nIrkbBQE4dH6OZW8thlfg2We0VsKHx6jlX9UZasW29T4ocvj+h5mWi7nUvVMy5hbnPhwt/yQ1u7S3OBSxWZ8W5TE07Gz/rMacnnNkKwNsc1m2uEp0sbW2mwcP0MR4vgdGdsSLylLv7c1yPe+7BZsOImaQGWbHmbU8WfgWIB16jw= Received: from DUZPR01CA0135.eurprd01.prod.exchangelabs.com (2603:10a6:10:4bc::6) by DBAP190MB1000.EURP190.PROD.OUTLOOK.COM (2603:10a6:10:1a1::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6678.29; Wed, 16 Aug 2023 13:25:52 +0000 Received: from DB1PEPF00039234.eurprd03.prod.outlook.com (2603:10a6:10:4bc:cafe::d4) by DUZPR01CA0135.outlook.office365.com (2603:10a6:10:4bc::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6652.33 via Frontend Transport; Wed, 16 Aug 2023 13:25:52 +0000 X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 178.72.21.4) smtp.mailfrom=napatech.com; dkim=none (message not signed) header.d=none;dmarc=fail action=none header.from=napatech.com; Received-SPF: Fail (protection.outlook.com: domain of napatech.com does not designate 178.72.21.4 as permitted sender) receiver=protection.outlook.com; client-ip=178.72.21.4; helo=localhost.localdomain.com; Received: from localhost.localdomain.com (178.72.21.4) by DB1PEPF00039234.mail.protection.outlook.com (10.167.8.107) with Microsoft SMTP Server id 15.20.6699.15 via Frontend Transport; Wed, 16 Aug 2023 13:25:52 +0000 From: Mykola Kostenok To: dev@dpdk.org Cc: Christian Koue Muf Subject: [PATCH 3/8] net/ntnic: adds NT200A02 adapter support Date: Wed, 16 Aug 2023 15:25:47 +0200 Message-Id: <20230816132552.2483752-3-mko-plv@napatech.com> X-Mailer: git-send-email 2.39.3 In-Reply-To: <20230816132552.2483752-1-mko-plv@napatech.com> References: <20230816132552.2483752-1-mko-plv@napatech.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DB1PEPF00039234:EE_|DBAP190MB1000:EE_ X-MS-Office365-Filtering-Correlation-Id: 69f47866-e419-44bc-d5c5-08db9e5c5050 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: bLvWX7OZbCXO/2PWPMwkjIiuMD53/ox2IaaURJzGKVq0x0ZeoNysQ5ZFxPMtGR7xM4Gy6k1Z7RTeYPQw+ySxxr/AZYJf5oteRLC0zdkJ4j1x74QusgHYFU7urwn7nqr08C8APYXlLkOJdY8n2WyFudXF5d+74aGVsdAM0mzVsPLodcZID9Er/i7usDsMnWh/f4owyw+DAt1i1idUA7LN7gzMhgehX/uC+1oJHTvvsgR8jFqgVh8fDs90vQx+KXCexzSoUBT0NkWIL0rptuivH0R/jDiB/KY76Pkp/ZpYva7MCVyHRTCZfH1vJ7Kr76GJQihPkrBIN/9cENvQrqdPKghrExb52UrzuSfXr9irq13U9ZwNEEGjPod8uH8bWyeMBESScRjZ92u/5PgjFRBU5of3mkRTaJDIPV/enLLM2ViT/nh3W7+uCXL8Mf/Jgn1Hb+lbGocaB3yyoyWRe7UA0PBIu0cUzFC07fEY/6a+R8jFCM3WnZFhX2MdiO2RxpOHFlV7JB/CKKKOuCjnAzzzCE/s9kwAekzo5BpI12vJtsqidq7PM42lIXUZEa7ERkfjzG3qC4BnYeVtH9WnC4qgD4aZADTVdH0RXEL2jg1RE1HEgRkhjrmcfNfbh+x1Pe2VDuNkBvY/9DzdOnp987qdqDkoi23Zaeixz51/3mrmkZgPN9u+WoPxcD1g/ZiQjab4oj2J7TUGFyUaBhdi1ObjkOzUfWRNrtgiOcS8Fdk2Rv3gaURGVQ21/kCwrc3irdXZA3JI2R3bjWIyXz906NUtwg== X-Forefront-Antispam-Report: CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:localhost.localdomain.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230031)(6069001)(396003)(39830400003)(346002)(376002)(136003)(1800799009)(451199024)(186009)(82310400011)(46966006)(36840700001)(316002)(356005)(6916009)(81166007)(70586007)(70206006)(36736006)(36860700001)(41300700001)(5660300002)(47076005)(34020700004)(8676002)(4326008)(8936002)(66899024)(30864003)(2906002)(118246002)(19627235002)(83380400001)(26005)(40480700001)(478600001)(336012)(86362001)(6512007)(9316004)(107886003)(6506007)(36756003)(6666004)(1076003)(2616005)(956004)(6486002)(36900700001)(559001)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: NHJBrsMTG3ls+UA2Gf6yXYE0f4lP0mUR12rQrQ2OeO2MU3n82iF6TXylSSMc3rr2VSNMkW0HErs6EyL4Mr53QioO2SOXZf6CJnTaxGSM/gNXHLqjmv0YMSkOfweSUw5A1ndMEnefiJhgmtlgjrq/VJzicvgDIKN257KdK+jhLweLvqs28THaLW2wQLCrYBIugJrEMBbO9SnBWamryRb+huHTiyx9S7bgKVA3WK+rz4mFm1Hp9tTd8G5Lja+ZLGT5iLsISqUw0ET2omH1qUajhkWajj3Gmm0XC2kVEtS5gNawoyGuaHUW/iMUIrcdtBkXiiLznp14V22yg/Jbuh+DZFZxqwVREwh8p4JQc3pAUELfOSBe0++wpUizvge8Z9CPSDZAtYSDoB+IPpol2hOJn1h058zYWXS+qhWX+iv9go0vI23ddC5aTNrSc4Z8KrK9VAppEFndNjG9LCXSWpHMhb6lwBLp/uP4nN8fkWnWynErjSl4yO6frHHLdHx0s5HEZe6gTHG8+yAALArMWc4hORFy7flvNhdopUhN9vIexjkq7FEoWTtfjXMauRlU2heNl1X25U4wlmGMgRoHjSM7pikNP5PmoTZiKfoB7jyQFvKbrCQMCGRC8WxPfCQckVwBujIVKyUyrF/yo1Pysxz1ifyx4oYg5HlijlFzhNYJTcusYPwHlSozhr4AY0KtX//+0IgrivRX6Q7sQn1tCWgwI5AtFMY0tLrflMG5bdC/JSQDlLFYjYaTiu9QcFT+lCWLfxII97JaVdEWfjoUHdHgAkIAJd+83byalfUhm7ClpbU= X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Aug 2023 13:25:52.3305 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 69f47866-e419-44bc-d5c5-08db9e5c5050 X-MS-Exchange-CrossTenant-Id: c4540d0b-728a-4233-9da5-9ea30c7ec3ed X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=c4540d0b-728a-4233-9da5-9ea30c7ec3ed; Ip=[178.72.21.4]; Helo=[localhost.localdomain.com] X-MS-Exchange-CrossTenant-AuthSource: DB1PEPF00039234.eurprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DBAP190MB1000 X-OriginatorOrg: napatech.com X-BESS-ID: 1692194403-311569-12603-25684-1 X-BESS-VER: 2019.1_20230807.1901 X-BESS-Apparent-Source-IP: 104.47.13.56 X-BESS-Parts: H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVkYWBsYmJkB2BlA4xTwpKSklJd nYPMnELNHQwNwgJTXRMjnVJM0k0cQiMVWpNhYABPtUVEMAAAA= X-BESS-Outbound-Spam-Score: 0.00 X-BESS-Outbound-Spam-Report: Code version 3.2, rules version 3.2.2.250184 [from cloudscan18-218.eu-central-1b.ess.aws.cudaops.com] Rule breakdown below pts rule name description ---- ---------------------- -------------------------------- 0.00 LARGE_BODY_SHORTCUT META: X-BESS-Outbound-Spam-Status: SCORE=0.00 using account:ESS113687 scores of KILL_LEVEL=7.0 tests=LARGE_BODY_SHORTCUT X-BESS-BRTS-Status: 1 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Christian Koue Muf The PMD is designed to support multiple different adapters, and this commit adds support for NT200A02 2x100G. Sensor and NIM code is included. Signed-off-by: Christian Koue Muf Reviewed-by: Mykola Kostenok --- .../net/ntnic/adapter/common_adapter_defs.h | 14 + drivers/net/ntnic/adapter/nt4ga_adapter.c | 477 ++++ drivers/net/ntnic/adapter/nt4ga_adapter.h | 108 + drivers/net/ntnic/adapter/nt4ga_filter.h | 15 + drivers/net/ntnic/adapter/nt4ga_link.c | 178 ++ drivers/net/ntnic/adapter/nt4ga_link.h | 179 ++ drivers/net/ntnic/adapter/nt4ga_link_100g.c | 825 +++++++ drivers/net/ntnic/adapter/nt4ga_link_100g.h | 12 + drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c | 598 +++++ drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h | 41 + drivers/net/ntnic/adapter/nt4ga_stat.c | 705 ++++++ drivers/net/ntnic/adapter/nt4ga_stat.h | 202 ++ drivers/net/ntnic/meson.build | 24 + drivers/net/ntnic/nim/i2c_nim.c | 1974 +++++++++++++++++ drivers/net/ntnic/nim/i2c_nim.h | 122 + drivers/net/ntnic/nim/nim_defines.h | 146 ++ drivers/net/ntnic/nim/nt_link_speed.c | 105 + drivers/net/ntnic/nim/nt_link_speed.h | 34 + drivers/net/ntnic/nim/qsfp_registers.h | 57 + drivers/net/ntnic/nim/qsfp_sensors.c | 174 ++ drivers/net/ntnic/nim/qsfp_sensors.h | 18 + drivers/net/ntnic/nim/sfp_p_registers.h | 100 + drivers/net/ntnic/nim/sfp_sensors.c | 288 +++ drivers/net/ntnic/nim/sfp_sensors.h | 18 + .../net/ntnic/nthw/core/nthw_clock_profiles.c | 11 +- drivers/net/ntnic/nthw/core/nthw_core.h | 2 + drivers/net/ntnic/nthw/core/nthw_gmf.c | 290 +++ drivers/net/ntnic/nthw/core/nthw_gmf.h | 93 + .../nthw/core/nthw_nt200a02_u23_si5340_v5.h | 344 +++ drivers/net/ntnic/nthw/core/nthw_rmc.c | 156 ++ drivers/net/ntnic/nthw/core/nthw_rmc.h | 57 + .../ntnic/sensors/avr_sensors/avr_sensors.c | 104 + .../ntnic/sensors/avr_sensors/avr_sensors.h | 22 + .../sensors/board_sensors/board_sensors.c | 48 + .../sensors/board_sensors/board_sensors.h | 18 + .../net/ntnic/sensors/board_sensors/tempmon.c | 42 + .../net/ntnic/sensors/board_sensors/tempmon.h | 16 + .../ntnic/sensors/nim_sensors/nim_sensors.c | 54 + .../ntnic/sensors/nim_sensors/nim_sensors.h | 19 + drivers/net/ntnic/sensors/ntavr/avr_intf.h | 89 + drivers/net/ntnic/sensors/ntavr/ntavr.c | 78 + drivers/net/ntnic/sensors/ntavr/ntavr.h | 32 + drivers/net/ntnic/sensors/sensor_types.h | 259 +++ drivers/net/ntnic/sensors/sensors.c | 273 +++ drivers/net/ntnic/sensors/sensors.h | 127 ++ drivers/net/ntnic/sensors/stream_info.h | 86 + 46 files changed, 8632 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ntnic/adapter/common_adapter_defs.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_adapter.c create mode 100644 drivers/net/ntnic/adapter/nt4ga_adapter.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_filter.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_link.c create mode 100644 drivers/net/ntnic/adapter/nt4ga_link.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_link_100g.c create mode 100644 drivers/net/ntnic/adapter/nt4ga_link_100g.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c create mode 100644 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h create mode 100644 drivers/net/ntnic/adapter/nt4ga_stat.c create mode 100644 drivers/net/ntnic/adapter/nt4ga_stat.h create mode 100644 drivers/net/ntnic/nim/i2c_nim.c create mode 100644 drivers/net/ntnic/nim/i2c_nim.h create mode 100644 drivers/net/ntnic/nim/nim_defines.h create mode 100644 drivers/net/ntnic/nim/nt_link_speed.c create mode 100644 drivers/net/ntnic/nim/nt_link_speed.h create mode 100644 drivers/net/ntnic/nim/qsfp_registers.h create mode 100644 drivers/net/ntnic/nim/qsfp_sensors.c create mode 100644 drivers/net/ntnic/nim/qsfp_sensors.h create mode 100644 drivers/net/ntnic/nim/sfp_p_registers.h create mode 100644 drivers/net/ntnic/nim/sfp_sensors.c create mode 100644 drivers/net/ntnic/nim/sfp_sensors.h create mode 100644 drivers/net/ntnic/nthw/core/nthw_gmf.c create mode 100644 drivers/net/ntnic/nthw/core/nthw_gmf.h create mode 100644 drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h create mode 100644 drivers/net/ntnic/nthw/core/nthw_rmc.c create mode 100644 drivers/net/ntnic/nthw/core/nthw_rmc.h create mode 100644 drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c create mode 100644 drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h create mode 100644 drivers/net/ntnic/sensors/board_sensors/board_sensors.c create mode 100644 drivers/net/ntnic/sensors/board_sensors/board_sensors.h create mode 100644 drivers/net/ntnic/sensors/board_sensors/tempmon.c create mode 100644 drivers/net/ntnic/sensors/board_sensors/tempmon.h create mode 100644 drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c create mode 100644 drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h create mode 100644 drivers/net/ntnic/sensors/ntavr/avr_intf.h create mode 100644 drivers/net/ntnic/sensors/ntavr/ntavr.c create mode 100644 drivers/net/ntnic/sensors/ntavr/ntavr.h create mode 100644 drivers/net/ntnic/sensors/sensor_types.h create mode 100644 drivers/net/ntnic/sensors/sensors.c create mode 100644 drivers/net/ntnic/sensors/sensors.h create mode 100644 drivers/net/ntnic/sensors/stream_info.h diff --git a/drivers/net/ntnic/adapter/common_adapter_defs.h b/drivers/net/ntnic/adapter/common_adapter_defs.h new file mode 100644 index 0000000000..79167806f1 --- /dev/null +++ b/drivers/net/ntnic/adapter/common_adapter_defs.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _COMMON_ADAPTER_DEFS_H_ +#define _COMMON_ADAPTER_DEFS_H_ + +/* + * Declarations shared by NT adapter types. + */ +#define NUM_ADAPTER_MAX (8) +#define NUM_ADAPTER_PORTS_MAX (128) + +#endif /* _COMMON_ADAPTER_DEFS_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_adapter.c b/drivers/net/ntnic/adapter/nt4ga_adapter.c new file mode 100644 index 0000000000..259aae2831 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_adapter.c @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_fpga.h" +#include "nt4ga_adapter.h" +#include "nt4ga_pci_ta_tg.h" +#include "nt4ga_link_100g.h" + +/* Sensors includes */ +#include "board_sensors.h" +#include "avr_sensors.h" + +/* + * Global variables shared by NT adapter types + */ +pthread_t monitor_tasks[NUM_ADAPTER_MAX]; +volatile int monitor_task_is_running[NUM_ADAPTER_MAX]; + +/* + * Signal-handler to stop all monitor threads + */ +static void stop_monitor_tasks(int signum) +{ + const size_t n = ARRAY_SIZE(monitor_task_is_running); + size_t i; + + /* Stop all monitor tasks */ + for (i = 0; i < n; i++) { + const int is_running = monitor_task_is_running[i]; + + monitor_task_is_running[i] = 0; + if (signum == -1 && is_running != 0) { + void *ret_val = NULL; + + pthread_join(monitor_tasks[i], &ret_val); + memset(&monitor_tasks[i], 0, sizeof(monitor_tasks[0])); + } + } +} + +int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *pfh) +{ + const char *const p_dev_name = p_adapter_info->p_dev_name; + const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str; + fpga_info_t *p_fpga_info = &p_adapter_info->fpga_info; + hw_info_t *p_hw_info = &p_adapter_info->hw_info; + char a_pci_ident_str[32]; + + snprintf(a_pci_ident_str, sizeof(a_pci_ident_str), "" PCIIDENT_PRINT_STR "", + PCIIDENT_TO_DOMAIN(p_fpga_info->pciident), + PCIIDENT_TO_BUSNR(p_fpga_info->pciident), + PCIIDENT_TO_DEVNR(p_fpga_info->pciident), + PCIIDENT_TO_FUNCNR(p_fpga_info->pciident)); + + fprintf(pfh, "%s: DeviceName: %s\n", p_adapter_id_str, + (p_dev_name ? p_dev_name : "NA")); + fprintf(pfh, "%s: PCI Details:\n", p_adapter_id_str); + fprintf(pfh, "%s: %s: %08X: %04X:%04X %04X:%04X\n", p_adapter_id_str, + a_pci_ident_str, p_fpga_info->pciident, p_hw_info->pci_vendor_id, + p_hw_info->pci_device_id, p_hw_info->pci_sub_vendor_id, + p_hw_info->pci_sub_device_id); + fprintf(pfh, "%s: FPGA Details:\n", p_adapter_id_str); + fprintf(pfh, "%s: %03d-%04d-%02d-%02d [%016" PRIX64 "] (%08X)\n", + p_adapter_id_str, p_fpga_info->n_fpga_type_id, p_fpga_info->n_fpga_prod_id, + p_fpga_info->n_fpga_ver_id, p_fpga_info->n_fpga_rev_id, + p_fpga_info->n_fpga_ident, p_fpga_info->n_fpga_build_time); + fprintf(pfh, "%s: FpgaDebugMode=0x%x\n", p_adapter_id_str, + p_fpga_info->n_fpga_debug_mode); + fprintf(pfh, + "%s: Nims=%d PhyPorts=%d PhyQuads=%d RxPorts=%d TxPorts=%d\n", + p_adapter_id_str, p_fpga_info->n_nims, p_fpga_info->n_phy_ports, + p_fpga_info->n_phy_quads, p_fpga_info->n_rx_ports, p_fpga_info->n_tx_ports); + fprintf(pfh, "%s: Hw=0x%02X_rev%d: %s\n", p_adapter_id_str, + p_hw_info->hw_platform_id, p_fpga_info->nthw_hw_info.hw_id, + p_fpga_info->nthw_hw_info.hw_plat_id_str); + + nt4ga_stat_dump(p_adapter_info, pfh); + + return 0; +} + +/* + * SPI for sensors initialization + */ +static nthw_spi_v3_t *new_sensors_s_spi(struct nt_fpga_s *p_fpga) +{ + nthw_spi_v3_t *sensors_s_spi = nthw_spi_v3_new(); + + if (sensors_s_spi == NULL) { + NT_LOG(ERR, ETHDEV, "%s: SPI allocation error\n", __func__); + return NULL; + } + + if (nthw_spi_v3_init(sensors_s_spi, p_fpga, 0)) { + NT_LOG(ERR, ETHDEV, "%s: SPI initialization error\n", __func__); + nthw_spi_v3_delete(sensors_s_spi); + return NULL; + } + + return sensors_s_spi; +} + +/* + * SPI for sensors reading + */ +nthw_spis_t *new_sensors_t_spi(struct nt_fpga_s *p_fpga) +{ + nthw_spis_t *sensors_t_spi = nthw_spis_new(); + /* init SPI for sensor initialization process */ + if (sensors_t_spi == NULL) { + NT_LOG(ERR, ETHDEV, "%s: SPI allocation error\n", __func__); + return NULL; + } + + if (nthw_spis_init(sensors_t_spi, p_fpga, 0)) { + NT_LOG(ERR, ETHDEV, "%s: SPI initialization error\n", __func__); + nthw_spis_delete(sensors_t_spi); + return NULL; + } + + return sensors_t_spi; +} + +static void adapter_sensor_setup(hw_info_t *p_hw_info, struct adapter_info_s *adapter) +{ + struct nt_fpga_s *p_fpga = adapter->fpga_info.mp_fpga; + struct nt_sensor_group *sensors_list_ptr = NULL; + nthw_spi_v3_t *sensors_s_spi = new_sensors_s_spi(p_fpga); + + adapter->adapter_sensors_cnt = 0; + + /* FPGA */ + adapter->adapter_sensors = fpga_temperature_sensor_init(p_hw_info->n_nthw_adapter_id, + NT_SENSOR_FPGA_TEMP, p_fpga); + sensors_list_ptr = adapter->adapter_sensors; + adapter->adapter_sensors_cnt++; + + /* AVR */ + if (sensors_s_spi) { + if (nt_avr_sensor_mon_ctrl(sensors_s_spi, + SENSOR_MON_CTRL_REM_ALL_SENSORS) != 0) { + /* stop sensor monitoring */ + NT_LOG(ERR, ETHDEV, + "Failed to stop AVR sensors monitoring\n"); + } else { + NT_LOG(DBG, ETHDEV, "AVR sensors init started\n"); + + sensors_list_ptr->next = avr_sensor_init(sensors_s_spi, + p_hw_info->n_nthw_adapter_id, + "FAN0", + NT_SENSOR_SOURCE_ADAPTER, + NT_SENSOR_TYPE_FAN, + NT_SENSOR_NT200E3_FAN_SPEED, + SENSOR_MON_FAN, 0, + SENSOR_MON_BIG_ENDIAN, + SENSOR_MON_UNSIGNED, + &fan, 0xFFFF); + sensors_list_ptr = sensors_list_ptr->next; + adapter->adapter_sensors_cnt++; + + sensors_list_ptr->next = avr_sensor_init(sensors_s_spi, + p_hw_info->n_nthw_adapter_id, + "PSU0", + NT_SENSOR_SOURCE_ADAPTER, + NT_SENSOR_TYPE_TEMPERATURE, + NT_SENSOR_NT200E3_PSU0_TEMP, + SENSOR_MON_PSU_EXAR_7724_0, 0x15, + SENSOR_MON_LITTLE_ENDIAN, + SENSOR_MON_UNSIGNED, + &exar7724_tj, 0xFFFF); + sensors_list_ptr = sensors_list_ptr->next; + adapter->adapter_sensors_cnt++; + + sensors_list_ptr->next = avr_sensor_init(sensors_s_spi, + p_hw_info->n_nthw_adapter_id, + "PSU1", + NT_SENSOR_SOURCE_ADAPTER, + NT_SENSOR_TYPE_TEMPERATURE, + NT_SENSOR_NT200A02_PSU1_TEMP, + SENSOR_MON_MP2886A, 0x8d, + SENSOR_MON_BIG_ENDIAN, + SENSOR_MON_UNSIGNED, + &mp2886a_tj, 0xFFFF); + sensors_list_ptr = sensors_list_ptr->next; + adapter->adapter_sensors_cnt++; + + sensors_list_ptr->next = avr_sensor_init(sensors_s_spi, + p_hw_info->n_nthw_adapter_id, + "PCB", + NT_SENSOR_SOURCE_ADAPTER, + NT_SENSOR_TYPE_TEMPERATURE, + NT_SENSOR_NT200E3_PCB_TEMP, + SENSOR_MON_DS1775, 0, + SENSOR_MON_LITTLE_ENDIAN, + SENSOR_MON_SIGNED, + &ds1775_t, 0xFFFF); + sensors_list_ptr = sensors_list_ptr->next; + adapter->adapter_sensors_cnt++; + + NT_LOG(DBG, ETHDEV, "AVR sensors init finished\n"); + + if (nt_avr_sensor_mon_ctrl(sensors_s_spi, + SENSOR_MON_CTRL_RUN) != 0) { + /* start sensor monitoring */ + NT_LOG(ERR, ETHDEV, + "Failed to start AVR sensors monitoring\n"); + } else { + NT_LOG(DBG, ETHDEV, + "AVR sensors monitoring starteed\n"); + } + } + + nthw_spi_v3_delete(sensors_s_spi); + } +} + +int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info) +{ + char *const p_dev_name = malloc(24); + char *const p_adapter_id_str = malloc(24); + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + hw_info_t *p_hw_info = &p_adapter_info->hw_info; + + /* + * IMPORTANT: Most variables cannot be determined before fpga model is instantiated + * (nthw_fpga_init()) + */ + int n_phy_ports = -1; + int n_nim_ports = -1; + int res = -1; + nt_fpga_t *p_fpga = NULL; + + (void)n_nim_ports; /* currently UNUSED - prevent warning */ + + p_hw_info->n_nthw_adapter_id = + nthw_platform_get_nthw_adapter_id(p_hw_info->pci_device_id); + + fpga_info->n_nthw_adapter_id = p_hw_info->n_nthw_adapter_id; + p_hw_info->hw_product_type = p_hw_info->pci_device_id & + 0x000f; /* ref: DN-0060 section 9 */ + /* ref: DN-0060 section 9 */ + p_hw_info->hw_platform_id = (p_hw_info->pci_device_id >> 4) & 0x00ff; + /* ref: DN-0060 section 9 */ + p_hw_info->hw_reserved1 = (p_hw_info->pci_device_id >> 12) & 0x000f; + + /* mp_dev_name */ + p_adapter_info->p_dev_name = p_dev_name; + if (p_dev_name) { + snprintf(p_dev_name, 24, "" PCIIDENT_PRINT_STR "", + PCIIDENT_TO_DOMAIN(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_BUSNR(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_DEVNR(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_FUNCNR(p_adapter_info->fpga_info.pciident)); + NT_LOG(DBG, ETHDEV, "%s: (0x%08X)\n", p_dev_name, + p_adapter_info->fpga_info.pciident); + } + + /* mp_adapter_id_str */ + p_adapter_info->mp_adapter_id_str = p_adapter_id_str; + + p_adapter_info->fpga_info.mp_adapter_id_str = p_adapter_id_str; + + if (p_adapter_id_str) { + snprintf(p_adapter_id_str, 24, "PCI:" PCIIDENT_PRINT_STR "", + PCIIDENT_TO_DOMAIN(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_BUSNR(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_DEVNR(p_adapter_info->fpga_info.pciident), + PCIIDENT_TO_FUNCNR(p_adapter_info->fpga_info.pciident)); + NT_LOG(DBG, ETHDEV, "%s: %s\n", p_adapter_id_str, p_dev_name); + } + + { + int i; + + for (i = 0; i < (int)ARRAY_SIZE(p_adapter_info->mp_port_id_str); + i++) { + char *p = malloc(32); + + if (p) { + snprintf(p, 32, "%s:intf_%d", + (p_adapter_id_str ? p_adapter_id_str : "NA"), + i); + NT_LOG(DBG, ETHDEV, "%s\n", p); + } + p_adapter_info->mp_port_id_str[i] = p; + } + } + + res = nthw_fpga_init(&p_adapter_info->fpga_info); + if (res) { + NT_LOG(ERR, ETHDEV, "%s: %s: FPGA=%04d res=x%08X [%s:%u]\n", + p_adapter_id_str, p_dev_name, fpga_info->n_fpga_prod_id, res, + __func__, __LINE__); + return res; + } + + assert(fpga_info); + p_fpga = fpga_info->mp_fpga; + assert(p_fpga); + n_phy_ports = fpga_info->n_phy_ports; + assert(n_phy_ports >= 1); + n_nim_ports = fpga_info->n_nims; + assert(n_nim_ports >= 1); + + /* + * HIF/PCI TA/TG + */ + { + res = nt4ga_pci_ta_tg_init(p_adapter_info); + if (res == 0) { + nt4ga_pci_ta_tg_measure_throughput_main(p_adapter_info, + 0, 0, + TG_PKT_SIZE, + TG_NUM_PACKETS, + TG_DELAY); + } else { + NT_LOG(WRN, ETHDEV, + "%s: PCI TA/TG is not available - skipping\n", + p_adapter_id_str); + } + } + + adapter_sensor_setup(p_hw_info, p_adapter_info); + + { + int i; + + assert(fpga_info->n_fpga_prod_id > 0); + for (i = 0; i < NUM_ADAPTER_PORTS_MAX; i++) { + /* Disable all ports. Must be enabled later */ + p_adapter_info->nt4ga_link.port_action[i].port_disable = + true; + } + switch (fpga_info->n_fpga_prod_id) { + /* NT200A02: 2x100G */ + case 9563: /* NT200A02 */ + res = nt4ga_link_100g_ports_init(p_adapter_info, p_fpga); + break; + default: + NT_LOG(ERR, ETHDEV, + "%s: Unsupported FPGA product: %04d\n", __func__, + fpga_info->n_fpga_prod_id); + res = -1; + break; + } + + if (res) { + NT_LOG(ERR, ETHDEV, + "%s: %s: %s: %u: FPGA=%04d res=x%08X\n", + p_adapter_id_str, p_dev_name, __func__, __LINE__, + fpga_info->n_fpga_prod_id, res); + return res; + } + } + + /* + * HostBuffer Systems + */ + p_adapter_info->n_rx_host_buffers = 0; + p_adapter_info->n_tx_host_buffers = 0; + + p_adapter_info->fpga_info.mp_nthw_epp = NULL; + if (nthw_epp_present(p_adapter_info->fpga_info.mp_fpga, 0)) { + p_adapter_info->fpga_info.mp_nthw_epp = nthw_epp_new(); + if (p_adapter_info->fpga_info.mp_nthw_epp == NULL) { + NT_LOG(ERR, ETHDEV, "%s: Cannot create EPP\n", + p_adapter_id_str); + return -1; + } + + res = nthw_epp_init(p_adapter_info->fpga_info.mp_nthw_epp, + p_adapter_info->fpga_info.mp_fpga, 0); + if (res != 0) { + NT_LOG(ERR, ETHDEV, "%s: Cannot initialize EPP\n", + p_adapter_id_str); + return res; + } + NT_LOG(DBG, ETHDEV, "%s: Initialized EPP\n", + p_adapter_id_str); + + res = nthw_epp_setup(p_adapter_info->fpga_info.mp_nthw_epp); + if (res != 0) { + NT_LOG(ERR, ETHDEV, "%s: Cannot setup EPP\n", + p_adapter_id_str); + return res; + } + } + + /* Nt4ga Stat init/setup */ + res = nt4ga_stat_init(p_adapter_info); + if (res != 0) { + NT_LOG(ERR, ETHDEV, + "%s: Cannot initialize the statistics module\n", + p_adapter_id_str); + return res; + } + res = nt4ga_stat_setup(p_adapter_info); + if (res != 0) { + NT_LOG(ERR, ETHDEV, "%s: Cannot setup the statistics module\n", + p_adapter_id_str); + return res; + } + + return 0; +} + +int nt4ga_adapter_deinit(struct adapter_info_s *p_adapter_info) +{ + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + int i; + int res; + struct nt_sensor_group *cur_adapter_sensor = NULL; + struct nt_sensor_group *next_adapter_sensor = NULL; + struct nim_sensor_group *cur_nim_sensor = NULL; + struct nim_sensor_group *next_nim_sensor = NULL; + + stop_monitor_tasks(-1); + + nt4ga_stat_stop(p_adapter_info); + + nthw_fpga_shutdown(&p_adapter_info->fpga_info); + + /* Rac rab reset flip flop */ + res = nthw_rac_rab_reset(fpga_info->mp_nthw_rac); + + /* Free adapter port ident strings */ + for (i = 0; i < fpga_info->n_phy_ports; i++) { + if (p_adapter_info->mp_port_id_str[i]) { + free(p_adapter_info->mp_port_id_str[i]); + p_adapter_info->mp_port_id_str[i] = NULL; + } + } + + /* Free adapter ident string */ + if (p_adapter_info->mp_adapter_id_str) { + free(p_adapter_info->mp_adapter_id_str); + p_adapter_info->mp_adapter_id_str = NULL; + } + + /* Free devname ident string */ + if (p_adapter_info->p_dev_name) { + free(p_adapter_info->p_dev_name); + p_adapter_info->p_dev_name = NULL; + } + + /* Free adapter sensors */ + if (p_adapter_info->adapter_sensors != NULL) { + do { + cur_adapter_sensor = p_adapter_info->adapter_sensors; + next_adapter_sensor = + p_adapter_info->adapter_sensors->next; + p_adapter_info->adapter_sensors = next_adapter_sensor; + + sensor_deinit(cur_adapter_sensor); + } while (next_adapter_sensor != NULL); + } + + /* Free NIM sensors */ + for (i = 0; i < fpga_info->n_phy_ports; i++) { + if (p_adapter_info->nim_sensors[i] != NULL) { + do { + cur_nim_sensor = p_adapter_info->nim_sensors[i]; + next_nim_sensor = + p_adapter_info->nim_sensors[i]->next; + p_adapter_info->nim_sensors[i] = next_nim_sensor; + free(cur_nim_sensor->sensor); + free(cur_nim_sensor); + } while (next_nim_sensor != NULL); + } + } + + return res; +} diff --git a/drivers/net/ntnic/adapter/nt4ga_adapter.h b/drivers/net/ntnic/adapter/nt4ga_adapter.h new file mode 100644 index 0000000000..6ae78a3743 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_adapter.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NT4GA_ADAPTER_H_ +#define _NT4GA_ADAPTER_H_ + +#include "common_adapter_defs.h" + +struct adapter_info_s; + +/* + * DN-0060 section 9 + */ +typedef struct hw_info_s { + /* pciids */ + uint16_t pci_vendor_id; + uint16_t pci_device_id; + uint16_t pci_sub_vendor_id; + uint16_t pci_sub_device_id; + uint16_t pci_class_id; + + /* Derived from pciid */ + nthw_adapter_id_t n_nthw_adapter_id; + int hw_platform_id; + int hw_product_type; + int hw_reserved1; +} hw_info_t; + +/* + * Services provided by the adapter module + */ +#include "nt4ga_pci_ta_tg.h" +#include "nt4ga_filter.h" +#include "nt4ga_stat.h" +#include "nt4ga_link.h" + +#include "sensors.h" +#include "i2c_nim.h" +#include "sensor_types.h" + +typedef struct adapter_info_s { + struct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg; + struct nt4ga_stat_s nt4ga_stat; + struct nt4ga_filter_s nt4ga_filter; + struct nt4ga_link_s nt4ga_link; + + struct hw_info_s hw_info; + struct fpga_info_s fpga_info; + + uint16_t adapter_sensors_cnt; + uint16_t nim_sensors_cnt[NUM_ADAPTER_PORTS_MAX]; + struct nt_sensor_group *adapter_sensors; + struct nim_sensor_group *nim_sensors[NUM_ADAPTER_PORTS_MAX]; + + char *mp_port_id_str[NUM_ADAPTER_PORTS_MAX]; + char *mp_adapter_id_str; + char *p_dev_name; + volatile bool *pb_shutdown; + + int adapter_no; + int n_rx_host_buffers; + int n_tx_host_buffers; +} adapter_info_t; + +/* + * Monitor task operations. This structure defines the management hooks for + * Napatech network devices. The following hooks can be defined; unless noted + * otherwise, they are optional and can be filled with a null pointer. + * + * int (*mto_open)(int adapter, int port); + * The function to call when a network device transitions to the up state, + * e.g., `ip link set up`. + * + * int (*mto_stop)(int adapter, int port); + * The function to call when a network device transitions to the down state, + * e.g., `ip link set down`. + */ +struct monitor_task_ops { + int (*mto_open)(int adapter, int port); + int (*mto_stop)(int adapter, int port); +}; + +#include +#include + +/* The file nt4ga_adapter.c defines the next four variables. */ +extern pthread_t monitor_tasks[NUM_ADAPTER_MAX]; +extern volatile int monitor_task_is_running[NUM_ADAPTER_MAX]; + +/* + * Function that sets up signal handler(s) that stop the monitoring tasks. + */ +int set_up_signal_handlers_to_stop_monitoring_tasks(void); + +int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info); +int nt4ga_adapter_deinit(struct adapter_info_s *p_adapter_info); + +int nt4ga_adapter_status(struct adapter_info_s *p_adapter_info); +int nt4ga_adapter_transmit_packet(struct adapter_info_s *p_adapter_info, + int n_intf_no, uint8_t *p_pkt, int n_pkt_len); + +int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *pfh); + +/* SPI for sensors reading */ +nthw_spis_t *new_sensors_t_spi(struct nt_fpga_s *p_fpga); + +#endif /* _NT4GA_ADAPTER_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_filter.h b/drivers/net/ntnic/adapter/nt4ga_filter.h new file mode 100644 index 0000000000..ad7e7d8c71 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_filter.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NT4GA_FILTER_H_ +#define NT4GA_FILTER_H_ + +typedef struct nt4ga_filter_s { + int n_intf_cnt; + int n_queues_per_intf_cnt; + + struct flow_nic_dev *mp_flow_device; +} nt4ga_filter_t; + +#endif /* NT4GA_FILTER_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_link.c b/drivers/net/ntnic/adapter/nt4ga_link.c new file mode 100644 index 0000000000..7fbdb72897 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_link.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include +#include +#include +#include + +#include "ntlog.h" +#include "nthw_drv.h" +#include "nt4ga_adapter.h" + +#include "nt4ga_link.h" +#include "nt_util.h" + +/* + * port: speed capabilitoes + * This is actually an adapter capability mapped onto every port + */ +uint32_t nt4ga_port_get_link_speed_capabilities(struct adapter_info_s *p _unused, + int port _unused) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + const uint32_t nt_link_speed_capa = p_link->speed_capa; + return nt_link_speed_capa; +} + +/* + * port: nim present + */ +bool nt4ga_port_get_nim_present(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + const bool nim_present = p_link->link_state[port].nim_present; + return nim_present; +} + +/* + * port: link mode + */ +void nt4ga_port_set_adm_state(struct adapter_info_s *p, int port, bool adm_state) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + + p_link->port_action[port].port_disable = !adm_state; +} + +bool nt4ga_port_get_adm_state(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + const bool adm_state = !p_link->port_action[port].port_disable; + return adm_state; +} + +/* + * port: link status + */ +void nt4ga_port_set_link_status(struct adapter_info_s *p, int port, + bool link_status) +{ + /* Setting link state/status is (currently) the same as controlling the port adm state */ + nt4ga_port_set_adm_state(p, port, link_status); +} + +bool nt4ga_port_get_link_status(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + bool status = p_link->link_state[port].link_up; + return status; +} + +/* + * port: link speed + */ +void nt4ga_port_set_link_speed(struct adapter_info_s *p, int port, + nt_link_speed_t speed) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + + p_link->port_action[port].port_speed = speed; + p_link->link_info[port].link_speed = speed; +} + +nt_link_speed_t nt4ga_port_get_link_speed(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + nt_link_speed_t speed = p_link->link_info[port].link_speed; + return speed; +} + +/* + * port: link autoneg + * Currently not fully supported by link code + */ +void nt4ga_port_set_link_autoneg(struct adapter_info_s *p _unused, + int port _unused, bool autoneg _unused) +{ + nt4ga_link_t *const p_link _unused = &p->nt4ga_link; +} + +bool nt4ga_port_get_link_autoneg(struct adapter_info_s *p _unused, + int port _unused) +{ + nt4ga_link_t *const p_link _unused = &p->nt4ga_link; + return true; +} + +/* + * port: link duplex + * Currently not fully supported by link code + */ +void nt4ga_port_set_link_duplex(struct adapter_info_s *p, int port, + nt_link_duplex_t duplex) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + + p_link->port_action[port].port_duplex = duplex; +} + +nt_link_duplex_t nt4ga_port_get_link_duplex(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + nt_link_duplex_t duplex = p_link->link_info[port].link_duplex; + return duplex; +} + +/* + * port: loopback mode + */ +void nt4ga_port_set_loopback_mode(struct adapter_info_s *p, int port, + uint32_t mode) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + + p_link->port_action[port].port_lpbk_mode = mode; +} + +uint32_t nt4ga_port_get_loopback_mode(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + + return p_link->port_action[port].port_lpbk_mode; +} + +/* + * port: nim capabilities + */ +nim_i2c_ctx_t nt4ga_port_get_nim_capabilities(struct adapter_info_s *p, int port) +{ + nt4ga_link_t *const p_link = &p->nt4ga_link; + nim_i2c_ctx_t nim_ctx = p_link->u.var100g.nim_ctx[port]; + return nim_ctx; +} + +/* + * port: tx power + */ +int nt4ga_port_tx_power(struct adapter_info_s *p, int port, bool disable) +{ + nt4ga_link_t *link_info = &p->nt4ga_link; + + if (link_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28_SR4 || + link_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28 || + link_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28_LR4) { + nim_i2c_ctx_t *nim_ctx = &link_info->u.var100g.nim_ctx[port]; + + if (!nim_ctx->specific_u.qsfp.rx_only) { + if (nim_qsfp_plus_nim_set_tx_laser_disable(nim_ctx, disable, + -1) != 0) + return 1; + } + } else { + return -1; + } + return 0; +} diff --git a/drivers/net/ntnic/adapter/nt4ga_link.h b/drivers/net/ntnic/adapter/nt4ga_link.h new file mode 100644 index 0000000000..2be9f49075 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_link.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NT4GA_LINK_H_ +#define NT4GA_LINK_H_ + +#include "common_adapter_defs.h" +#include "nthw_drv.h" +#include "i2c_nim.h" +#include "nthw_fpga_rst_nt200a0x.h" + +/* + * Link state.\n + * Just after start of ntservice the link state might be unknown since the + * monitoring routine is busy reading NIM state and NIM data. This might also + * be the case after a NIM is plugged into an interface. + * The error state indicates a HW reading error. + */ +enum nt_link_state_e { + NT_LINK_STATE_UNKNOWN = 0, /* The link state has not been read yet */ + NT_LINK_STATE_DOWN = 1, /* The link state is DOWN */ + NT_LINK_STATE_UP = 2, /* The link state is UP */ + NT_LINK_STATE_ERROR = 3 /* The link state could not be read */ +}; + +typedef enum nt_link_state_e nt_link_state_t, *nt_link_state_p; + +/* + * Link duplex mode + */ +enum nt_link_duplex_e { + NT_LINK_DUPLEX_UNKNOWN = 0, + NT_LINK_DUPLEX_HALF = 0x01, /* Half duplex */ + NT_LINK_DUPLEX_FULL = 0x02, /* Full duplex */ +}; + +typedef enum nt_link_duplex_e nt_link_duplex_t; + +/* + * Link loopback mode + */ +enum nt_link_loopback_e { + NT_LINK_LOOPBACK_OFF = 0, + NT_LINK_LOOPBACK_HOST = 0x01, /* Host loopback mode */ + NT_LINK_LOOPBACK_LINE = 0x02, /* Line loopback mode */ +}; + +/* + * Link MDI mode + */ +enum nt_link_mdi_e { + NT_LINK_MDI_NA = 0, + NT_LINK_MDI_AUTO = 0x01, /* MDI auto */ + NT_LINK_MDI_MDI = 0x02, /* MDI mode */ + NT_LINK_MDI_MDIX = 0x04, /* MDIX mode */ +}; + +typedef enum nt_link_mdi_e nt_link_mdi_t; + +/* + * Link Auto/Manual mode + */ +enum nt_link_auto_neg_e { + NT_LINK_AUTONEG_NA = 0, + NT_LINK_AUTONEG_MANUAL = 0x01, + NT_LINK_AUTONEG_OFF = NT_LINK_AUTONEG_MANUAL, /* Auto negotiation OFF */ + NT_LINK_AUTONEG_AUTO = 0x02, + NT_LINK_AUTONEG_ON = NT_LINK_AUTONEG_AUTO, /* Auto negotiation ON */ +}; + +typedef enum nt_link_auto_neg_e nt_link_auto_neg_t; + +/* + * Callback functions to setup mac, pcs and phy + */ +typedef struct link_state_s { + bool link_disabled; + bool nim_present; + bool lh_nim_absent; + bool link_up; + enum nt_link_state_e link_state; + enum nt_link_state_e link_state_latched; +} link_state_t; + +typedef struct link_info_s { + enum nt_link_speed_e link_speed; + enum nt_link_duplex_e link_duplex; + enum nt_link_auto_neg_e link_auto_neg; +} link_info_t; + +typedef struct port_action_s { + bool port_disable; + enum nt_link_speed_e port_speed; + enum nt_link_duplex_e port_duplex; + uint32_t port_lpbk_mode; +} port_action_t; + +typedef struct adapter_100g_s { + nim_i2c_ctx_t + nim_ctx[NUM_ADAPTER_PORTS_MAX]; /* Should be the first field */ + nthw_mac_pcs_t mac_pcs100g[NUM_ADAPTER_PORTS_MAX]; + nthw_gpio_phy_t gpio_phy[NUM_ADAPTER_PORTS_MAX]; +} adapter_100g_t; + +typedef union adapter_var_s { + nim_i2c_ctx_t nim_ctx + [NUM_ADAPTER_PORTS_MAX]; /* First field in all the adaptors type */ + adapter_100g_t var100g; +} adapter_var_u; + +typedef struct nt4ga_link_s { + link_state_t link_state[NUM_ADAPTER_PORTS_MAX]; + link_info_t link_info[NUM_ADAPTER_PORTS_MAX]; + port_action_t port_action[NUM_ADAPTER_PORTS_MAX]; + uint32_t speed_capa; + /* */ + bool variables_initialized; + adapter_var_u u; +} nt4ga_link_t; + +bool nt4ga_port_get_nim_present(struct adapter_info_s *p, int port); + +/* + * port:s link mode + */ +void nt4ga_port_set_adm_state(struct adapter_info_s *p, int port, + bool adm_state); +bool nt4ga_port_get_adm_state(struct adapter_info_s *p, int port); + +/* + * port:s link status + */ +void nt4ga_port_set_link_status(struct adapter_info_s *p, int port, bool status); +bool nt4ga_port_get_link_status(struct adapter_info_s *p, int port); + +/* + * port: link autoneg + */ +void nt4ga_port_set_link_autoneg(struct adapter_info_s *p, int port, + bool autoneg); +bool nt4ga_port_get_link_autoneg(struct adapter_info_s *p, int port); + +/* + * port: link speed + */ +void nt4ga_port_set_link_speed(struct adapter_info_s *p, int port, + nt_link_speed_t speed); +nt_link_speed_t nt4ga_port_get_link_speed(struct adapter_info_s *p, int port); + +/* + * port: link duplex + */ +void nt4ga_port_set_link_duplex(struct adapter_info_s *p, int port, + nt_link_duplex_t duplex); +nt_link_duplex_t nt4ga_port_get_link_duplex(struct adapter_info_s *p, int port); + +/* + * port: loopback mode + */ +void nt4ga_port_set_loopback_mode(struct adapter_info_s *p, int port, + uint32_t mode); +uint32_t nt4ga_port_get_loopback_mode(struct adapter_info_s *p, int port); + +uint32_t nt4ga_port_get_link_speed_capabilities(struct adapter_info_s *p, + int port); + +/* + * port: nim capabilities + */ +nim_i2c_ctx_t nt4ga_port_get_nim_capabilities(struct adapter_info_s *p, + int port); + +/* + * port: tx power + */ +int nt4ga_port_tx_power(struct adapter_info_s *p, int port, bool disable); + +#endif /* NT4GA_LINK_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_link_100g.c b/drivers/net/ntnic/adapter/nt4ga_link_100g.c new file mode 100644 index 0000000000..8465b6a341 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_link_100g.c @@ -0,0 +1,825 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "nt_util.h" +#include "ntlog.h" +#include "i2c_nim.h" +#include "nt4ga_adapter.h" +#include "nt4ga_link_100g.h" + +#include /* memcmp, memset */ + +/* + * Prototypes + */ +static int swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs, + int port, bool swap); +static int reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs); + +/* + * Structs and types definitions + */ +enum link_up_state { + RESET, /* A valid signal is detected by NO local faults. */ + EXPECT_NO_LF, /* After that we check NO latched local fault bit before */ + /* de-asserting Remote fault indication. */ + WAIT_STABLE_LINK, /* Now we expect the link is up. */ + MONITOR_LINK /* After link-up we monitor link state. */ +}; + +typedef struct _monitoring_state { + /* Fields below are set by monitoring thread */ + enum link_up_state m_link_up_state; + enum nt_link_state_e link_state; + enum nt_link_state_e latch_link_state; + int m_time_out; +} monitoring_state_t, *monitoring_state_p; + +/* + * Global variables + */ + +/* + * External state, to be set by the network driver. + */ + +/* + * Utility functions + */ + +static void set_loopback(struct adapter_info_s *p_adapter_info, + nthw_mac_pcs_t *mac_pcs, int intf_no, uint32_t mode, + uint32_t last_mode) +{ + bool swap_polerity = true; + + switch (mode) { + case 1: + NT_LOG(INF, ETHDEV, "%s: Applying host loopback\n", + p_adapter_info->mp_port_id_str[intf_no]); + nthw_mac_pcs_set_fec(mac_pcs, true); + nthw_mac_pcs_set_host_loopback(mac_pcs, true); + swap_polerity = false; + break; + case 2: + NT_LOG(INF, ETHDEV, "%s: Applying line loopback\n", + p_adapter_info->mp_port_id_str[intf_no]); + nthw_mac_pcs_set_line_loopback(mac_pcs, true); + break; + default: + switch (last_mode) { + case 1: + NT_LOG(INF, ETHDEV, "%s: Removing host loopback\n", + p_adapter_info->mp_port_id_str[intf_no]); + nthw_mac_pcs_set_host_loopback(mac_pcs, false); + break; + case 2: + NT_LOG(INF, ETHDEV, "%s: Removing line loopback\n", + p_adapter_info->mp_port_id_str[intf_no]); + nthw_mac_pcs_set_line_loopback(mac_pcs, false); + break; + default: + /* Do nothing */ + break; + } + break; + } + + if ((p_adapter_info->fpga_info.nthw_hw_info.hw_id == 2 && + p_adapter_info->hw_info.n_nthw_adapter_id == NT_HW_ADAPTER_ID_NT200A01) || + p_adapter_info->hw_info.n_nthw_adapter_id == NT_HW_ADAPTER_ID_NT200A02) { + (void)swap_tx_rx_polarity(p_adapter_info, mac_pcs, intf_no, + swap_polerity); + } + + /* After changing the loopback the system must be properly reset */ + reset_rx(p_adapter_info, mac_pcs); + + NT_OS_WAIT_USEC(10000); /* 10ms - arbitrary choice */ + + if (!nthw_mac_pcs_is_rx_path_rst(mac_pcs)) { + nthw_mac_pcs_reset_bip_counters(mac_pcs); + if (!nthw_mac_pcs_get_fec_bypass(mac_pcs)) + nthw_mac_pcs_reset_fec_counters(mac_pcs); + } +} + +/* + * Function to retrieve the current state of a link (for one port) + */ +static int link_state_build(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs, + nthw_gpio_phy_t *gpio_phy, int port, + link_state_t *state, bool is_port_disabled) +{ + uint32_t abs; + uint32_t phy_link_state; + uint32_t lh_abs; + uint32_t ll_phy_link_state; + uint32_t link_down_cnt; + uint32_t nim_interr; + uint32_t lh_local_fault; + uint32_t lh_remote_fault; + uint32_t lh_internal_local_fault; + uint32_t lh_received_local_fault; + + memset(state, 0, sizeof(*state)); + state->link_disabled = is_port_disabled; + nthw_mac_pcs_get_link_summary(mac_pcs, &abs, &phy_link_state, &lh_abs, + &ll_phy_link_state, &link_down_cnt, + &nim_interr, &lh_local_fault, + &lh_remote_fault, &lh_internal_local_fault, + &lh_received_local_fault); + + assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX); + state->nim_present = + nthw_gpio_phy_is_module_present(gpio_phy, (uint8_t)port); + state->lh_nim_absent = !state->nim_present; + state->link_up = phy_link_state ? true : false; + + { + static char lsbuf[NUM_ADAPTER_MAX][NUM_ADAPTER_PORTS_MAX][256]; + char buf[255]; + const int adapter_no = drv->adapter_no; + + snprintf(buf, sizeof(buf), + "%s: Port = %d: abs = %u, phy_link_state = %u, lh_abs = %u, " + "ll_phy_link_state = %u, link_down_cnt = %u, nim_interr = %u, " + "lh_local_fault = %u, lh_remote_fault = %u, lh_internal_local_fault = %u, " + "lh_received_local_fault = %u", + drv->mp_adapter_id_str, mac_pcs->mn_instance, abs, + phy_link_state, lh_abs, ll_phy_link_state, + link_down_cnt, nim_interr, lh_local_fault, + lh_remote_fault, lh_internal_local_fault, + lh_received_local_fault); + if (strcmp(lsbuf[adapter_no][port], buf) != 0) { + rte_strscpy(lsbuf[adapter_no][port], buf, + sizeof(lsbuf[adapter_no][port]) - 1U); + lsbuf[adapter_no][port] + [sizeof(lsbuf[adapter_no][port]) - 1U] = '\0'; + NT_LOG(DBG, ETHDEV, "%s\n", lsbuf[adapter_no][port]); + } + } + return 0; +} + +/* + * Check whether a NIM module is present + */ +static bool nim_is_present(nthw_gpio_phy_t *gpio_phy, uint8_t if_no) +{ + assert(if_no < NUM_ADAPTER_PORTS_MAX); + + return nthw_gpio_phy_is_module_present(gpio_phy, if_no); +} + +/* + * Enable RX + */ +static int enable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + (void)drv; /* unused */ + nthw_mac_pcs_set_rx_enable(mac_pcs, true); + return 0; +} + +/* + * Enable TX + */ +static int enable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + (void)drv; /* unused */ + nthw_mac_pcs_set_tx_enable(mac_pcs, true); + nthw_mac_pcs_set_tx_sel_host(mac_pcs, true); + return 0; +} + +/* + * Disable RX + */ +static int disable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + (void)drv; /* unused */ + nthw_mac_pcs_set_rx_enable(mac_pcs, false); + return 0; +} + +/* + * Disable TX + */ +static int disable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + (void)drv; /* unused */ + nthw_mac_pcs_set_tx_enable(mac_pcs, false); + nthw_mac_pcs_set_tx_sel_host(mac_pcs, false); + return 0; +} + +/* + * Reset RX + */ +static int reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + (void)drv; + + nthw_mac_pcs_rx_path_rst(mac_pcs, true); + NT_OS_WAIT_USEC(10000); /* 10ms */ + nthw_mac_pcs_rx_path_rst(mac_pcs, false); + NT_OS_WAIT_USEC(10000); /* 10ms */ + + return 0; +} + +/* + * Reset TX + */ + +/* + * Swap tx/rx polarity + */ +static int swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs, + int port, bool swap) +{ + const bool tx_polarity_swap[2][4] = { { true, true, false, false }, + { false, true, false, false } + }; + const bool rx_polarity_swap[2][4] = { { false, true, true, true }, + { false, true, true, false } + }; + uint8_t lane; + + (void)drv; + for (lane = 0U; lane < 4U; lane++) { + if (swap) { + nthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane, + tx_polarity_swap[port][lane]); + nthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane, + rx_polarity_swap[port][lane]); + } else { + nthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane, false); + nthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane, false); + } + } + return 0; +} + +/* + * Check link once NIM is installed and link can be expected. + */ +static int check_link_state(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs) +{ + bool rst_required; + bool ber; + bool fec_all_locked; + + rst_required = nthw_mac_pcs_reset_required(mac_pcs); + + ber = nthw_mac_pcs_get_hi_ber(mac_pcs); + + fec_all_locked = nthw_mac_pcs_get_fec_stat_all_am_locked(mac_pcs); + + if (rst_required || ber || !fec_all_locked) + reset_rx(drv, mac_pcs); + + return 0; +} + +/* + * Initialize NIM, Code based on nt200e3_2_ptp.cpp: MyPort::createNim() + */ +static int create_nim(adapter_info_t *drv, nt_fpga_t *fpga, int port, + bool enable) +{ + int res = 0; + const uint8_t valid_nim_id = 17U; + nthw_gpio_phy_t *gpio_phy; + nim_i2c_ctx_t *nim_ctx; + sfp_nim_state_t nim; + nt4ga_link_t *link_info = &drv->nt4ga_link; + nthw_mac_pcs_t *mac_pcs = &link_info->u.var100g.mac_pcs100g[port]; + + (void)fpga; /* unused */ + assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX); + assert(link_info->variables_initialized); + + gpio_phy = &link_info->u.var100g.gpio_phy[port]; + nim_ctx = &link_info->u.var100g.nim_ctx[port]; + + /* + * Check NIM is present before doing GPIO PHY reset. + */ + if (!nim_is_present(gpio_phy, (uint8_t)port)) { + NT_LOG(INF, ETHDEV, "%s: NIM module is absent\n", + drv->mp_port_id_str[port]); + return 0; + } + + if (!enable) { + disable_rx(drv, mac_pcs); + disable_tx(drv, mac_pcs); + reset_rx(drv, mac_pcs); + } + + /* + * Perform PHY reset. + */ + NT_LOG(DBG, ETHDEV, "%s: Performing NIM reset\n", + drv->mp_port_id_str[port]); + nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, true); + NT_OS_WAIT_USEC(100000); /* pause 0.1s */ + nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, false); + + /* + * Wait a little after a module has been inserted before trying to access I2C + * data, otherwise the module will not respond correctly. + */ + NT_OS_WAIT_USEC(1000000); /* pause 1.0s */ + + if (!nim_is_present(gpio_phy, (uint8_t)port)) { + NT_LOG(DBG, ETHDEV, "%s: NIM module is no longer absent!\n", + drv->mp_port_id_str[port]); + return -1; + } + + res = construct_and_preinit_nim(nim_ctx, NULL, port, + ((struct adapter_info_s *)drv)->nim_sensors, + &((struct adapter_info_s *)drv)->nim_sensors_cnt[port]); + if (res) + return res; + + res = nim_state_build(nim_ctx, &nim); + if (res) + return res; + + NT_LOG(DBG, NTHW, + "%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'\n", + drv->mp_port_id_str[port], nim_ctx->nim_id, + nim_id_to_text(nim_ctx->nim_id), nim.br, nim_ctx->vendor_name, + nim_ctx->prod_no, nim_ctx->serial_no); + + /* + * Does the driver support the NIM module type? + */ + if (nim_ctx->nim_id != valid_nim_id) { + NT_LOG(ERR, NTHW, + "%s: The driver does not support the NIM module type %s\n", + drv->mp_port_id_str[port], nim_id_to_text(nim_ctx->nim_id)); + NT_LOG(DBG, NTHW, + "%s: The driver supports the NIM module type %s\n", + drv->mp_port_id_str[port], nim_id_to_text(valid_nim_id)); + return -1; + } + + if (enable) { + NT_LOG(DBG, ETHDEV, "%s: De-asserting low power\n", + drv->mp_port_id_str[port]); + nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, false); + } else { + NT_LOG(DBG, ETHDEV, "%s: Asserting low power\n", + drv->mp_port_id_str[port]); + nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, true); + } + + return res; +} + +/* + * Initialize one 100 Gbps port. + * The function shall not assume anything about the state of the adapter + * and/or port. + */ +static int port_init(adapter_info_t *drv, nt_fpga_t *fpga, int port) +{ + int adapter_id; + int hw_id; + int res; + nt4ga_link_t *link_info = &drv->nt4ga_link; + + nthw_mac_pcs_t *mac_pcs; + + assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX); + assert(link_info->variables_initialized); + + if (fpga && fpga->p_fpga_info) { + adapter_id = fpga->p_fpga_info->n_nthw_adapter_id; + hw_id = fpga->p_fpga_info->nthw_hw_info.hw_id; + } else { + adapter_id = -1; + hw_id = -1; + } + + mac_pcs = &link_info->u.var100g.mac_pcs100g[port]; + + /* + * Phase 1. Pre-state machine (`port init` functions) + * 1.1) Nt4gaAdapter::portInit() + */ + + /* No adapter set-up here, only state variables */ + + /* 1.2) MyPort::init() */ + link_info->link_info[port].link_speed = NT_LINK_SPEED_100G; + link_info->link_info[port].link_duplex = NT_LINK_DUPLEX_FULL; + link_info->link_info[port].link_auto_neg = NT_LINK_AUTONEG_OFF; + link_info->speed_capa |= NT_LINK_SPEED_100G; + nthw_mac_pcs_set_led_mode(mac_pcs, NTHW_MAC_PCS_LED_AUTO); + nthw_mac_pcs_set_receiver_equalization_mode(mac_pcs, + nthw_mac_pcs_receiver_mode_lpm); + + /* + * NT200A01 build 2 HW and NT200A02 that require GTY polarity swap + * if (adapter is `NT200A01 build 2 HW or NT200A02`) + */ + if (adapter_id == NT_HW_ADAPTER_ID_NT200A02 || + (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && hw_id == 2)) + (void)swap_tx_rx_polarity(drv, mac_pcs, port, true); + + nthw_mac_pcs_set_ts_eop(mac_pcs, true); /* end-of-frame timestamping */ + + /* Work in ABSOLUTE timing mode, don't set IFG mode. */ + + /* Phase 2. Pre-state machine (`setup` functions) */ + + /* 2.1) nt200a0x.cpp:Myport::setup() */ + NT_LOG(DBG, ETHDEV, "%s: Setting up port %d\n", drv->mp_port_id_str[port], + port); + + NT_LOG(DBG, ETHDEV, "%s: Port %d: PHY TX enable\n", + drv->mp_port_id_str[port], port); + enable_tx(drv, mac_pcs); + reset_rx(drv, mac_pcs); + + /* 2.2) Nt4gaPort::setup() */ + if (nthw_gmf_init(NULL, fpga, port) == 0) { + nthw_gmf_t gmf; + + if (nthw_gmf_init(&gmf, fpga, port) == 0) + nthw_gmf_set_enable(&gmf, true); + } + + /* Phase 3. Link state machine steps */ + + /* 3.1) Create NIM, ::createNim() */ + res = create_nim(drv, fpga, port, true); + + if (res) { + NT_LOG(WRN, ETHDEV, "%s: NIM initialization failed\n", + drv->mp_port_id_str[port]); + return res; + } + + NT_LOG(DBG, ETHDEV, "%s: NIM initialized\n", drv->mp_port_id_str[port]); + + /* 3.2) MyPort::nimReady() */ + + /* 3.3) MyPort::nimReady100Gb() */ + + /* Setting FEC resets the lane counter in one half of the GMF */ + nthw_mac_pcs_set_fec(mac_pcs, true); + NT_LOG(DBG, ETHDEV, "%s: Port %d: HOST FEC enabled\n", + drv->mp_port_id_str[port], port); + + if (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && hw_id == 1) { + const uint8_t tuning_s_r4[2][4][3] = { { { 8, 15, 8 }, + { 8, 15, 9 }, + { 7, 15, 9 }, + { 6, 15, 8 } + }, + { { 6, 15, 8 }, + { 3, 15, 12 }, + { 7, 15, 9 }, + { 7, 15, 8 } + } + }; + + uint8_t lane = 0; + + for (lane = 0; lane < 4; lane++) { + uint8_t pre, diff, post; + + /* Use short-range tuning values */ + pre = tuning_s_r4[port][lane][0]; + diff = tuning_s_r4[port][lane][1]; + post = tuning_s_r4[port][lane][2]; + + nthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, diff, + post); + } + } else if ((adapter_id == NT_HW_ADAPTER_ID_NT200A02) || + ((adapter_id == NT_HW_ADAPTER_ID_NT200A01) && + (hw_id == 2))) { + const uint8_t pre = 5; + const uint8_t diff = 25; + const uint8_t post = 12; + + uint8_t lane = 0; + + for (lane = 0; lane < 4; lane++) { + nthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, diff, + post); + } + } else { + NT_LOG(ERR, ETHDEV, + "%s: Unhandled AdapterId/HwId: %02x_hwid%d\n", __func__, + adapter_id, hw_id); + assert(0); + } + reset_rx(drv, mac_pcs); + + /* + * 3.4) MyPort::setLinkState() + * + * Compensation = 1640 - dly + * CMAC-core dly 188 ns + * FEC no correction 87 ns + * FEC active correction 211 + */ + if (nthw_mac_pcs_get_fec_valid(mac_pcs)) + nthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 211)); + + else + nthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 87)); + + /* 3.5) uint32_t MyPort::macConfig(nt_link_state_t link_state) */ + enable_rx(drv, mac_pcs); + + nthw_mac_pcs_set_host_loopback(mac_pcs, false); + + return res; +} + +/* + * State machine shared between kernel and userland + */ +static int common_ptp_nim_state_machine(void *data) +{ + adapter_info_t *drv = (adapter_info_t *)data; + fpga_info_t *fpga_info = &drv->fpga_info; + nt4ga_link_t *link_info = &drv->nt4ga_link; + nt_fpga_t *fpga = fpga_info->mp_fpga; + const int adapter_no = drv->adapter_no; + const int nb_ports = fpga_info->n_phy_ports; + uint32_t last_lpbk_mode[NUM_ADAPTER_PORTS_MAX]; + + nim_i2c_ctx_t *nim_ctx; + link_state_t *link_state; + nthw_mac_pcs_t *mac_pcs; + nthw_gpio_phy_t *gpio_phy; + + if (!fpga) { + NT_LOG(ERR, ETHDEV, "%s: fpga is NULL\n", drv->mp_adapter_id_str); + goto NT4GA_LINK_100G_MON_EXIT; + } + + assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX); + nim_ctx = link_info->u.var100g.nim_ctx; + link_state = link_info->link_state; + mac_pcs = link_info->u.var100g.mac_pcs100g; + gpio_phy = link_info->u.var100g.gpio_phy; + + monitor_task_is_running[adapter_no] = 1; + memset(last_lpbk_mode, 0, sizeof(last_lpbk_mode)); + + if (monitor_task_is_running[adapter_no]) { + NT_LOG(DBG, ETHDEV, "%s: link state machine running...\n", + drv->mp_adapter_id_str); + } + + while (monitor_task_is_running[adapter_no]) { + int i; + static bool reported_link[NUM_ADAPTER_PORTS_MAX] = { false }; + + /* Read sensors */ + if (drv->adapter_sensors != NULL) { + nthw_spis_t *t_spi = + new_sensors_t_spi(drv->fpga_info.mp_fpga); + if (t_spi) { + for (struct nt_sensor_group *ptr = + drv->adapter_sensors; + ptr != NULL; ptr = ptr->next) + ptr->read(ptr, t_spi); + nthw_spis_delete(t_spi); + } + } + + for (i = 0; i < nb_ports; i++) { + link_state_t new_link_state; + const bool is_port_disabled = + link_info->port_action[i].port_disable; + const bool was_port_disabled = + link_state[i].link_disabled; + const bool disable_port = is_port_disabled && + !was_port_disabled; + const bool enable_port = !is_port_disabled && + was_port_disabled; + + if (!monitor_task_is_running[adapter_no]) /* stop quickly */ + break; + + /* Reading NIM sensors */ + if (drv->nim_sensors[i] != NULL) { + nthw_spis_t *t_spi = new_sensors_t_spi(drv->fpga_info.mp_fpga); + if (t_spi) { + for (struct nim_sensor_group *ptr = + drv->nim_sensors[i]; + ptr != NULL; ptr = ptr->next) + ptr->read(ptr, t_spi); + nthw_spis_delete(t_spi); + } + } + + /* Has the administrative port state changed? */ + assert(!(disable_port && enable_port)); + if (disable_port) { + memset(&link_state[i], 0, + sizeof(link_state[i])); + link_state[i].link_disabled = true; + reported_link[i] = false; + /* Turn off laser and LED, etc. */ + (void)create_nim(drv, fpga, i, false); + NT_LOG(DBG, ETHDEV, "%s: Port %i is disabled\n", + drv->mp_port_id_str[i], i); + continue; + } + + if (enable_port) { + link_state[i].link_disabled = false; + NT_LOG(DBG, ETHDEV, "%s: Port %i is enabled\n", + drv->mp_port_id_str[i], i); + } + + if (is_port_disabled) + continue; + + if (link_info->port_action[i].port_lpbk_mode != + last_lpbk_mode[i]) { + /* Loopback mode has changed. Do something */ + if (!nim_is_present(&gpio_phy[i], + (uint8_t)i)) { + /* + * If there is no Nim present, we need to initialize the + * port anyway + */ + port_init(drv, fpga, i); + } + NT_LOG(INF, ETHDEV, + "%s: Loopback mode changed=%u\n", + drv->mp_port_id_str[i], + link_info->port_action[i].port_lpbk_mode); + set_loopback(drv, &mac_pcs[i], i, + link_info->port_action[i].port_lpbk_mode, + last_lpbk_mode[i]); + if (link_info->port_action[i].port_lpbk_mode == + 1) + link_state[i].link_up = true; + last_lpbk_mode[i] = + link_info->port_action[i].port_lpbk_mode; + continue; + } + + (void)link_state_build(drv, &mac_pcs[i], &gpio_phy[i], + i, &new_link_state, + is_port_disabled); + if (!new_link_state.nim_present) { + if (link_state[i].nim_present) { + NT_LOG(INF, ETHDEV, + "%s: NIM module removed\n", + drv->mp_port_id_str[i]); + } + link_state[i] = new_link_state; + continue; + } + + /* NIM module is present */ + if (new_link_state.lh_nim_absent || + !link_state[i].nim_present) { + sfp_nim_state_t new_state; + + NT_LOG(DBG, ETHDEV, "%s: NIM module inserted\n", + drv->mp_port_id_str[i]); + + if (port_init(drv, fpga, i)) { + NT_LOG(ERR, ETHDEV, + "%s: Failed to initialize NIM module\n", + drv->mp_port_id_str[i]); + continue; + } + if (nim_state_build(&nim_ctx[i], &new_state)) { + NT_LOG(ERR, ETHDEV, + "%s: Cannot read basic NIM data\n", + drv->mp_port_id_str[i]); + continue; + } + assert(new_state.br); /* Cannot be zero if NIM is present */ + NT_LOG(DBG, ETHDEV, + "%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'\n", + drv->mp_port_id_str[i], nim_ctx->nim_id, + nim_id_to_text(nim_ctx->nim_id), + (unsigned int)new_state.br, + nim_ctx->vendor_name, nim_ctx->prod_no, + nim_ctx->serial_no); + + (void)link_state_build(drv, &mac_pcs[i], + &gpio_phy[i], i, + &link_state[i], + is_port_disabled); + + NT_LOG(DBG, ETHDEV, + "%s: NIM module initialized\n", + drv->mp_port_id_str[i]); + continue; + } + if (reported_link[i] != new_link_state.link_up) { + NT_LOG(INF, ETHDEV, "%s: link is %s\n", + drv->mp_port_id_str[i], + (new_link_state.link_up ? "up" : + "down")); + link_state[i].link_up = new_link_state.link_up; + reported_link[i] = new_link_state.link_up; + } + check_link_state(drv, &mac_pcs[i]); + } /* end-for */ + if (monitor_task_is_running[adapter_no]) + NT_OS_WAIT_USEC(5 * 100000U); /* 5 x 0.1s = 0.5s */ + } + +NT4GA_LINK_100G_MON_EXIT: + + NT_LOG(DBG, ETHDEV, + "%s: Stopped NT4GA 100 Gbps link monitoring thread.\n", + drv->mp_adapter_id_str); + + return 0; +} + +/* + * Userland NIM state machine + */ +static void *nt4ga_link_100g_mon(void *data) +{ + (void)common_ptp_nim_state_machine(data); + + return NULL; +} + +/* + * Initialize all ports + * The driver calls this function during initialization (of the driver). + */ +int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, + nt_fpga_t *fpga) +{ + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + const int adapter_no = p_adapter_info->adapter_no; + const int nb_ports = fpga_info->n_phy_ports; + int res = 0; + + NT_LOG(DBG, ETHDEV, "%s: Initializing ports\n", + p_adapter_info->mp_adapter_id_str); + + /* + * Initialize global variables + */ + assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX); + + if (res == 0 && !p_adapter_info->nt4ga_link.variables_initialized) { + nthw_mac_pcs_t *mac_pcs = + p_adapter_info->nt4ga_link.u.var100g.mac_pcs100g; + nim_i2c_ctx_t *nim_ctx = + p_adapter_info->nt4ga_link.u.var100g.nim_ctx; + nthw_gpio_phy_t *gpio_phy = + p_adapter_info->nt4ga_link.u.var100g.gpio_phy; + int i; + + for (i = 0; i < nb_ports; i++) { + const uint8_t instance = + (uint8_t)(2U + i); /* 2 + adapter port number */ + res = nthw_mac_pcs_init(&mac_pcs[i], fpga, + i /* int nInstance */); + if (res != 0) + break; + res = nthw_iic_init(&nim_ctx[i].hwiic, fpga, instance, + 8 /* timing */); + if (res != 0) + break; + nim_ctx[i].instance = instance; + nim_ctx[i].devaddr = 0x50; /* 0xA0 / 2 */ + nim_ctx[i].regaddr = 0U; + res = nthw_gpio_phy_init(&gpio_phy[i], fpga, + 0 /* Only one instance */); + if (res != 0) + break; + } + if (res == 0) + p_adapter_info->nt4ga_link.variables_initialized = true; + } + + /* Create state-machine thread */ + if (res == 0) { + if (!monitor_task_is_running[adapter_no]) { + res = pthread_create(&monitor_tasks[adapter_no], NULL, + nt4ga_link_100g_mon, p_adapter_info); + } + } + return res; +} diff --git a/drivers/net/ntnic/adapter/nt4ga_link_100g.h b/drivers/net/ntnic/adapter/nt4ga_link_100g.h new file mode 100644 index 0000000000..803b3454b7 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_link_100g.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NT4GA_LINK_100G_H_ +#define NT4GA_LINK_100G_H_ + +#include "nthw_drv.h" + +int nt4ga_link_100g_ports_init(adapter_info_t *p_adapter_info, nt_fpga_t *p_fpga); + +#endif /* NT4GA_LINK_100G_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c new file mode 100644 index 0000000000..aaacd11f1e --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c @@ -0,0 +1,598 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" +#include "nt_util.h" +#include "nthw_drv.h" +#include "nt4ga_adapter.h" +#include "nt4ga_pci_ta_tg.h" +#include "nthw_pci_ta.h" +#include "nthw_pci_rd_tg.h" +#include "nthw_pci_wr_tg.h" + +int nt4ga_pci_ta_tg_init(struct adapter_info_s *p_adapter_info) +{ + const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str; + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + nt_fpga_t *p_fpga = fpga_info->mp_fpga; + nt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg; + int res; + int n_err_cnt = 0; + + if (p) { + memset(p, 0, sizeof(nt4ga_pci_ta_tg_t)); + } else { + NT_LOG(ERR, NTHW, "%s: %s: null ptr\n", p_adapter_id_str, __func__); + return -1; + } + + assert(p_fpga); + + p->mp_nthw_pci_rd_tg = nthw_pci_rd_tg_new(); + assert(p->mp_nthw_pci_rd_tg); + res = nthw_pci_rd_tg_init(p->mp_nthw_pci_rd_tg, p_fpga, 0); + if (res) { + n_err_cnt++; + NT_LOG(WRN, NTHW, "%s: module PCI_RD_TG not found\n", + p_adapter_id_str); + } + + p->mp_nthw_pci_wr_tg = nthw_pci_wr_tg_new(); + assert(p->mp_nthw_pci_wr_tg); + res = nthw_pci_wr_tg_init(p->mp_nthw_pci_wr_tg, p_fpga, 0); + if (res) { + n_err_cnt++; + NT_LOG(WRN, NTHW, "%s: module PCI_WR_TG not found\n", + p_adapter_id_str); + } + + p->mp_nthw_pci_ta = nthw_pci_ta_new(); + assert(p->mp_nthw_pci_ta); + res = nthw_pci_ta_init(p->mp_nthw_pci_ta, p_fpga, 0); + if (res) { + n_err_cnt++; + NT_LOG(WRN, NTHW, "%s: module PCI_TA not found\n", + p_adapter_id_str); + } + + return n_err_cnt; +} + +static int nt4ga_pci_ta_tg_ta_write_control_enable(nt4ga_pci_ta_tg_t *p, + uint32_t enable) +{ + nthw_pci_ta_set_control_enable(p->mp_nthw_pci_ta, enable); + return 0; +} + +static int nt4ga_pci_ta_tg_ta_read_length_error(nt4ga_pci_ta_tg_t *p, uint32_t *p_data) +{ + nthw_pci_ta_get_length_error(p->mp_nthw_pci_ta, p_data); + return 0; +} + +static int nt4ga_pci_ta_tg_ta_read_packet_bad(nt4ga_pci_ta_tg_t *p, uint32_t *p_data) +{ + nthw_pci_ta_get_packet_bad(p->mp_nthw_pci_ta, p_data); + return 0; +} + +static int nt4ga_pci_ta_tg_ta_read_packet_good(nt4ga_pci_ta_tg_t *p, uint32_t *p_data) +{ + nthw_pci_ta_get_packet_good(p->mp_nthw_pci_ta, p_data); + return 0; +} + +static int nt4ga_pci_ta_tg_ta_read_payload_error(nt4ga_pci_ta_tg_t *p, + uint32_t *p_data) +{ + nthw_pci_ta_get_payload_error(p->mp_nthw_pci_ta, p_data); + return 0; +} + +static int nt4ga_pci_ta_tg_rd_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova, + int slot_addr, uint32_t req_size, bool wait, + bool wrap) +{ + const uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * req_size)); + + nthw_pci_rd_tg_set_ram_addr(p->mp_nthw_pci_rd_tg, slot_addr); + nthw_pci_rd_tg_set_phys_addr(p->mp_nthw_pci_rd_tg, n_phys_addr); + nthw_pci_rd_tg_set_ram_data(p->mp_nthw_pci_rd_tg, req_size, wait, wrap); + return 0; +} + +static int nt4ga_pci_ta_tg_rd_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t num_iterations) +{ + nthw_pci_rd_tg_set_run(p->mp_nthw_pci_rd_tg, num_iterations); + return 0; +} + +static int nt4ga_pci_ta_tg_rd_tg_wait_ready(nt4ga_pci_ta_tg_t *p) +{ + int poll = 0; + uint32_t data = 0; + + while (data == 0) { + /* NOTE: Deliberately start with a sleep - ensures that the FPGA pipe is empty */ + NT_OS_WAIT_USEC(1000); + data = nthw_pci_rd_tg_get_ctrl_rdy(p->mp_nthw_pci_rd_tg); + poll++; + if (poll >= 1000) { + NT_LOG(ERR, NTHW, + "%s: FAILED waiting PCI RD TG ready: poll=%d\n", + __func__, poll); + return -1; + } + } + + return 0; +} + +static int nt4ga_pci_ta_tg_wr_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova, + int slot_addr, uint32_t req_size, bool wait, + bool wrap, bool inc) +{ + const uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * req_size)); + + nthw_pci_wr_tg_set_ram_addr(p->mp_nthw_pci_wr_tg, slot_addr); + nthw_pci_wr_tg_set_phys_addr(p->mp_nthw_pci_wr_tg, n_phys_addr); + nthw_pci_wr_tg_set_ram_data(p->mp_nthw_pci_wr_tg, req_size, wait, wrap, inc); + + return 0; +} + +static int nt4ga_pci_ta_tg_wr_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t num_iterations) +{ + nthw_pci_wr_tg_set_run(p->mp_nthw_pci_wr_tg, num_iterations); + return 0; +} + +static int nt4ga_pci_ta_tg_wr_tg_wait_ready(nt4ga_pci_ta_tg_t *p) +{ + int poll = 0; + uint32_t data = 0; + + while (data == 0) { + /* NOTE: Deliberately start with a sleep - ensures that the FPGA pipe is empty */ + NT_OS_WAIT_USEC(1000); + data = nthw_pci_wr_tg_get_ctrl_rdy(p->mp_nthw_pci_wr_tg); + poll++; + if (poll >= 1000) { + NT_LOG(ERR, NTHW, + "%s: FAILED waiting PCI WR TG ready: poll=%d\n", + __func__, poll); + return -1; + } + } + + return 0; +} + +int nt4ga_pci_ta_tg_measure_throughput_run(struct adapter_info_s *p_adapter_info, + struct nthw_hif_end_point_counters *pri, + struct nthw_hif_end_point_counters *sla) +{ + nt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg; + + const int delay = pri->n_tg_delay; + const int pkt_size = pri->n_tg_pkt_size; + const int num_pkts = pri->n_tg_num_pkts; + const int n_direction = pri->n_tg_direction; + const uint8_t n_numa_node = (uint8_t)pri->n_numa_node; + const int dma_buf_size = (4 * 1024 * 1024); + + const size_t align_size = ALIGN_SIZE(dma_buf_size); + uint32_t *mem_addr; + uint64_t iova; + + int bo_error = 0; + + nthw_hif *p_master_instance = p_adapter_info->fpga_info.mp_nthw_hif; + nthw_hif *p_slave_instance = NULL; + + nthw_pcie3 *p_pci_master = p_adapter_info->fpga_info.mp_nthw_pcie3; + nthw_pcie3 *p_pci_slave = NULL; + + assert(p_master_instance || p_pci_master); + + struct nt_dma_s *p_dma; + /* FPGA needs a Page alignment (4K on Intel) */ + p_dma = nt_dma_alloc(align_size, 0x1000, n_numa_node); + if (p_dma == NULL) { + NT_LOG(DBG, ETHDEV, "%s: vfio_dma_alloc failed\n", __func__); + return 0; + } + mem_addr = (uint32_t *)p_dma->addr; + iova = p_dma->iova; + + NT_LOG(DBG, NTHW, + "%s: Running HIF bandwidth measurements on NUMA node %d\n", + __func__, n_numa_node); + + bo_error = 0; + { + int wrap; + + /* Stop any existing running test */ + bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0); + + /* Prepare the HIF Traffic generator */ + bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 1); + bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p); + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + /* + * Ensure that the hostbuffer memory contain data that can be read - + * For this we will ask the FPGA to write data to it. The last wrap packet + * does not generate any data it only wraps (unlike the PCIe2 TG) + */ + { + int pkt; + + for (pkt = 0; pkt < num_pkts; pkt++) { + if (pkt >= (num_pkts - 1)) + wrap = 1; + + else + wrap = 0; + bo_error |= nt4ga_pci_ta_tg_wr_tg_setup(p, iova, + pkt, pkt_size, + 0, wrap, 1); + bo_error |= nt4ga_pci_ta_tg_rd_tg_setup(p, iova, + pkt, pkt_size, + 0, wrap); + } + } + + bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1); + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + /* Start WR TG Write once */ + bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1); + /* Wait until WR TG ready */ + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + /* Verify that we have a packet */ + { + int pkt; + + for (pkt = 0; pkt < num_pkts; pkt++) { + uint32_t value = 0; + int poll; + + for (poll = 8; poll < pkt_size; + poll += 4, value++) { + if (*(uint32_t *)((uint8_t *)mem_addr + + (pkt * pkt_size) + + poll) != value) { + NT_LOG(ERR, NTHW, + "HIF TG: Prepare failed. Data write failed: #%d.%d: %016X:%08X\n", + pkt, poll, + *(uint32_t *)((uint8_t *) + mem_addr + + (pkt * + pkt_size) + + poll), + value); + + /* + * Break out of the verification loop on first + * сompare error + */ + bo_error |= 1; + break; + } + } + } + } + + switch (n_direction) { + case 1: /* Read only test */ + nt4ga_pci_ta_tg_wr_tg_run(p, 0xffff); + break; + case 2: /* Write only test */ + nt4ga_pci_ta_tg_rd_tg_run(p, 0xffff); + break; + case 3: /* Combined read/write test */ + nt4ga_pci_ta_tg_wr_tg_run(p, 0xffff); + nt4ga_pci_ta_tg_rd_tg_run(p, 0xffff); + break; + default: /* stop tests */ + nt4ga_pci_ta_tg_wr_tg_run(p, 0); + nt4ga_pci_ta_tg_rd_tg_run(p, 0); + break; + } + + do { + /* prep */ + if (p_pci_master) { + nthw_pcie3_end_point_counters_sample_pre(p_pci_master, + pri); + } + if (p_pci_slave) { + nthw_pcie3_end_point_counters_sample_pre(p_pci_slave, + sla); + } + + /* start measure */ + if (p_master_instance) + nthw_hif_stat_req_enable(p_master_instance); + if (p_pci_master) + nthw_pcie3_stat_req_enable(p_pci_master); + + if (p_slave_instance) + nthw_hif_stat_req_enable(p_slave_instance); + if (p_pci_slave) + nthw_pcie3_stat_req_enable(p_pci_slave); + + /* Wait */ + NT_OS_WAIT_USEC(delay); + + /* Stop measure */ + if (p_master_instance) + nthw_hif_stat_req_disable(p_master_instance); + if (p_pci_master) + nthw_pcie3_stat_req_disable(p_pci_master); + + if (p_slave_instance) + nthw_hif_stat_req_disable(p_slave_instance); + if (p_pci_slave) + nthw_pcie3_stat_req_disable(p_pci_slave); + + /* Post process master */ + if (p_master_instance) { + nthw_hif_end_point_counters_sample(p_master_instance, + pri); + } + + if (p_pci_master) { + nthw_pcie3_end_point_counters_sample_post(p_pci_master, + pri); + } + + /* Post process slave */ + if (p_slave_instance) { + nthw_hif_end_point_counters_sample(p_slave_instance, + sla); + } + + if (p_pci_slave) { + nthw_pcie3_end_point_counters_sample_post(p_pci_slave, + sla); + } + + { + /* Check for TA transmit errors */ + uint32_t dw_good_pkts, dw_bad_pkts, dw_bad_length, + dw_bad_payload; + nt4ga_pci_ta_tg_ta_read_packet_good(p, + &dw_good_pkts); + nt4ga_pci_ta_tg_ta_read_packet_bad(p, &dw_bad_pkts); + nt4ga_pci_ta_tg_ta_read_length_error(p, + &dw_bad_length); + nt4ga_pci_ta_tg_ta_read_payload_error(p, &dw_bad_payload); + + NT_LOG(DBG, NTHW, + "%s: NUMA node %u: HIF: TA: Good pkts, Bad pkts, Bad length, Bad payload\n", + __func__, n_numa_node); + NT_LOG(DBG, NTHW, + "%s: NUMA node %u: HIF: TA: 0x%08x 0x%08x 0x%08x 0x%08x\n", + __func__, n_numa_node, dw_good_pkts, + dw_bad_pkts, dw_bad_length, dw_bad_payload); + + if (dw_bad_pkts | dw_bad_length | dw_bad_payload) { + bo_error |= 1; + NT_LOG(ERR, NTHW, + "%s: NUMA node %u: HIF: TA: error detected\n", + __func__, n_numa_node); + NT_LOG(ERR, NTHW, + "%s: NUMA node %u: HIF: TA: Good packets received: %u\n", + __func__, n_numa_node, dw_good_pkts); + NT_LOG(ERR, NTHW, + "%s: NUMA node %u: HIF: TA: Bad packets received : %u\n", + __func__, n_numa_node, dw_bad_pkts); + NT_LOG(ERR, NTHW, + "%s: NUMA node %u: HIF: TA: Bad length received : %u\n", + __func__, n_numa_node, + dw_bad_length); + NT_LOG(ERR, NTHW, + "%s: NUMA node %u: HIF: TA: Bad payload received : %u\n", + __func__, n_numa_node, + dw_bad_payload); + } + } + + if (bo_error != 0) + break; + + break; /* for now only loop once */ + + /* + * Only do "signalstop" looping if a specific numa node and direction is to + * be tested. + */ + } while ((bo_error == 0) && (n_numa_node != UINT8_MAX) && + (n_direction != -1)); + + /* Stop the test */ + bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0); + + /* PCIe3 sanity checks */ + { +#if defined(DEBUG) + int do_loop = 1; +#else + int do_loop = 0; +#endif + + while (do_loop) { + do_loop = 0; + + if (p_master_instance) { + nthw_hif_stat_req_enable(p_master_instance); + NT_OS_WAIT_USEC(100); + nthw_hif_stat_req_disable(p_master_instance); + } + + if (do_loop == 0) + break; + + NT_LOG(DBG, NTHW, + "%s: WARNING this is wrong - wait again\n", + __func__); + NT_OS_WAIT_USEC(200 * 1000); + } + } + } + + /* Stop the test */ + + bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0); + bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p); + + bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0); + + nt_dma_free(p_dma); + + return bo_error; +} + +int nt4ga_pci_ta_tg_measure_throughput_main(struct adapter_info_s *p_adapter_info, + const uint8_t numa_node, + const int direction, const int n_pkt_size, + const int n_batch_count, const int n_delay) +{ + /* All numa nodes is indicated by UINT8_MAX */ + const uint8_t numa_begin = (numa_node == UINT8_MAX ? 0 : numa_node); + const uint8_t numa_end = numa_begin; + + /* sanity check direction param */ + const int dir_begin = (direction <= 0 ? 1 : direction); + const int dir_end = (direction <= 0 ? 3 : direction); + + int bo_error = 0; + struct nthw_hif_end_points eps; + + if (n_delay == 0) + return -1; + + NT_LOG(DBG, NTHW, "HIF adapter throughput:\n"); + + /* Only do "signalstop"-looping if a specific numa node is to be tested. */ + { + uint8_t numa; + + for (numa = numa_begin; numa <= numa_end; numa++) { + { + int by_loop; + + for (by_loop = dir_begin; by_loop <= dir_end; + by_loop++) { + struct nthw_hif_end_point_counters *pri = + &eps.pri; + struct nthw_hif_end_point_counters *sla = + &eps.sla; + + pri->n_numa_node = numa; + pri->n_tg_direction = by_loop; + pri->n_tg_pkt_size = (n_pkt_size > 0 ? + n_pkt_size : + TG_PKT_SIZE); + pri->n_tg_num_pkts = + (n_batch_count > 0 ? + n_batch_count : + TG_NUM_PACKETS); + pri->n_tg_delay = (n_delay > 0 ? n_delay : + TG_DELAY); + pri->cur_rx = 0; + pri->cur_tx = 0; + pri->n_ref_clk_cnt = -1; + pri->bo_error = 0; + + sla->n_numa_node = numa; + sla->n_tg_direction = by_loop; + sla->n_tg_pkt_size = (n_pkt_size > 0 ? + n_pkt_size : + TG_PKT_SIZE); + sla->n_tg_num_pkts = + (n_batch_count > 0 ? + n_batch_count : + TG_NUM_PACKETS); + sla->n_tg_delay = (n_delay > 0 ? n_delay : + TG_DELAY); + sla->cur_rx = 0; + sla->cur_tx = 0; + pri->n_ref_clk_cnt = -1; + sla->bo_error = 0; + + bo_error += + nt4ga_pci_ta_tg_measure_throughput_run(p_adapter_info, + pri, sla); +#if defined(DEBUG) && (1) + { + NT_LOG(DBG, NTHW, + "%s: @ %d: %d %d %d %d: %016lX %016lX : %6ld Mbps %6ld Mbps\n", + __func__, pri->n_numa_node, + pri->n_tg_direction, + pri->n_tg_num_pkts, + pri->n_tg_pkt_size, + pri->n_tg_delay, + pri->cur_rx, pri->cur_tx, + (pri->cur_rx * 8UL / + 1000000UL), + (pri->cur_tx * 8UL / + 1000000UL)); + } + { + NT_LOG(DBG, NTHW, + "%s: @ %d: %d %d %d %d: %016lX %016lX : %6ld Mbps %6ld Mbps\n", + __func__, sla->n_numa_node, + sla->n_tg_direction, + sla->n_tg_num_pkts, + sla->n_tg_pkt_size, + sla->n_tg_delay, + sla->cur_rx, sla->cur_tx, + (sla->cur_rx * 8UL / + 1000000UL), + (sla->cur_tx * 8UL / + 1000000UL)); + } +#endif + + if (pri->bo_error != 0 || sla->bo_error != 0) + bo_error++; + if (bo_error) + break; + } + } + } + } + + if (bo_error != 0) { + NT_LOG(ERR, NTHW, "%s: error during bandwidth measurement\n", + __func__); + } + + NT_LOG(DBG, NTHW, "HIF adapter throughput: done\n"); + + NT_LOG(DBG, NTHW, "%s: [%s:%u] done\n", __func__, __FILE__, __LINE__); + + return 0; +} diff --git a/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h new file mode 100644 index 0000000000..8b46491f77 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NT4GA_PCI_TA_TG_H_ +#define _NT4GA_PCI_TA_TG_H_ + +#include + +#define TA_TG_DBG_SHOW_SUMMARY (1) + +#define TG_NUM_PACKETS (8) +#define TG_PKT_SIZE (2048 * 1) +#define TG_AREA_SIZE (TG_NUM_PACKETS * TG_PKT_SIZE) + +#define TG_DELAY (200000) /* usec */ + +/* Struct predefinitions */ +struct adapter_info_s; +struct nthw_hif_end_point_counters; + +struct nt4ga_pci_ta_tg_s { + struct nthw_pci_rd_tg *mp_nthw_pci_rd_tg; + struct nthw_pci_wr_tg *mp_nthw_pci_wr_tg; + struct nthw_pci_ta *mp_nthw_pci_ta; +}; + +typedef struct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg_t; +typedef struct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg; + +int nt4ga_pci_ta_tg_init(struct adapter_info_s *p_adapter_info); + +int nt4ga_pci_ta_tg_measure_throughput_run(struct adapter_info_s *p_adapter_info, + struct nthw_hif_end_point_counters *pri, + struct nthw_hif_end_point_counters *sla); +int nt4ga_pci_ta_tg_measure_throughput_main(struct adapter_info_s *p_adapter_info, + const uint8_t numa_node, + const int direction, const int n_pkt_size, + const int n_batch_count, const int n_delay); + +#endif /* _NT4GA_PCI_TA_TG_H_ */ diff --git a/drivers/net/ntnic/adapter/nt4ga_stat.c b/drivers/net/ntnic/adapter/nt4ga_stat.c new file mode 100644 index 0000000000..b61c73ea12 --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_stat.c @@ -0,0 +1,705 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" +#include "nt_util.h" +#include "nthw_drv.h" +#include "nthw_fpga.h" +#include "nt4ga_adapter.h" + +#define NO_FLAGS 0 + +/* Inline timestamp format s pcap 32:32 bits. Convert to nsecs */ +static inline uint64_t timestamp2ns(uint64_t ts) +{ + return ((ts >> 32) * 1000000000) + (ts & 0xffffffff); +} + +static int nt4ga_stat_collect_cap_v1_stats(nt4ga_stat_t *p_nt4ga_stat, + uint32_t *p_stat_dma_virtual); +static int nt4ga_stat_collect_virt_v1_stats(nt4ga_stat_t *p_nt4ga_stat, + uint32_t *p_stat_dma_virtual); + +int nt4ga_stat_collect(struct adapter_info_s *p_adapter_info _unused, + nt4ga_stat_t *p_nt4ga_stat) +{ + nthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat; + + if (p_nthw_stat->mb_is_vswitch) { + /* + * Set all bits in the DMA block timestamp since 9530-42-05 and other Vswitch FPGA + * images may only clear all bits in this memory location. TBV + * Consequently, last_timestamp must be constructed via a system call. + */ + *p_nthw_stat->mp_timestamp = 0xFFFFFFFF; + p_nt4ga_stat->last_timestamp = NT_OS_GET_TIME_NS(); + nt4ga_stat_collect_virt_v1_stats(p_nt4ga_stat, + p_nt4ga_stat->p_stat_dma_virtual); + } else { + p_nt4ga_stat->last_timestamp = + timestamp2ns(*p_nthw_stat->mp_timestamp); + nt4ga_stat_collect_cap_v1_stats(p_nt4ga_stat, + p_nt4ga_stat->p_stat_dma_virtual); + } + return 0; +} + +int nt4ga_stat_init(struct adapter_info_s *p_adapter_info) +{ + const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str; + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + nt_fpga_t *p_fpga = fpga_info->mp_fpga; + nt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat; + + if (p_nt4ga_stat) { + memset(p_nt4ga_stat, 0, sizeof(nt4ga_stat_t)); + } else { + NT_LOG(ERR, ETHDEV, "%s: ERROR (%s:%d)", p_adapter_id_str, + __func__, __LINE__); + return -1; + } + + { + nthw_stat_t *p_nthw_stat = nthw_stat_new(); + nthw_rmc_t *p_nthw_rmc = nthw_rmc_new(); + + if (!p_nthw_stat) { + NT_LOG(ERR, ETHDEV, "%s: ERROR (%s:%d)", p_adapter_id_str, + __func__, __LINE__); + return -1; + } + + if (!p_nthw_rmc) { + nthw_stat_delete(p_nthw_stat); + + NT_LOG(ERR, ETHDEV, "%s: ERROR (%s:%d)", p_adapter_id_str, + __func__, __LINE__); + return -1; + } + + p_nt4ga_stat->mp_nthw_stat = p_nthw_stat; + nthw_stat_init(p_nthw_stat, p_fpga, 0); + + p_nt4ga_stat->mp_nthw_rmc = p_nthw_rmc; + nthw_rmc_init(p_nthw_rmc, p_fpga, 0); + + p_nt4ga_stat->mn_rx_host_buffers = p_nthw_stat->m_nb_rx_host_buffers; + p_nt4ga_stat->mn_tx_host_buffers = p_nthw_stat->m_nb_tx_host_buffers; + + p_nt4ga_stat->mn_rx_ports = p_nthw_stat->m_nb_rx_ports; + p_nt4ga_stat->mn_tx_ports = p_nthw_stat->m_nb_tx_ports; + } + + return 0; +} + +int nt4ga_stat_setup(struct adapter_info_s *p_adapter_info) +{ + const int n_physical_adapter_no _unused = p_adapter_info->adapter_no; + nt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat; + nthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat; + nthw_rmc_t *p_nthw_rmc = p_nt4ga_stat->mp_nthw_rmc; + + if (p_nthw_rmc) + nthw_rmc_block(p_nthw_rmc); + + /* Allocate and map memory for fpga statistics */ + { + uint32_t n_stat_size = + (uint32_t)(p_nthw_stat->m_nb_counters * sizeof(uint32_t) + + sizeof(p_nthw_stat->mp_timestamp)); + struct nt_dma_s *p_dma; + int numa_node = p_adapter_info->fpga_info.numa_node; + + /* FPGA needs a 16K alignment on Statistics */ + p_dma = nt_dma_alloc(n_stat_size, 0x4000, numa_node); + + if (!p_dma) { + NT_LOG(ERR, ETHDEV, "%s: pDma alloc failed\n", + __func__); + return -1; + } + + NT_LOG(DBG, ETHDEV, "%s: %x @%d %p %" PRIX64 " %" PRIX64 "\n", __func__, + n_stat_size, numa_node, p_dma->addr, p_dma->iova); + + NT_LOG(DBG, ETHDEV, + "DMA: Physical adapter %02ld, PA = 0x%016" PRIX64 + " DMA = 0x%016" PRIX64 " size = 0x%" PRIX64 "\n", + n_physical_adapter_no, p_dma->iova, p_dma->addr, n_stat_size); + + p_nt4ga_stat->p_stat_dma_virtual = (uint32_t *)p_dma->addr; + p_nt4ga_stat->n_stat_size = n_stat_size; + p_nt4ga_stat->p_stat_dma = p_dma; + + memset(p_nt4ga_stat->p_stat_dma_virtual, 0xaa, n_stat_size); + nthw_stat_set_dma_address(p_nthw_stat, p_dma->iova, + p_nt4ga_stat->p_stat_dma_virtual); + } + + if (p_nthw_rmc) + nthw_rmc_unblock(p_nthw_rmc, false); + + p_nt4ga_stat->mp_stat_structs_color = calloc(p_nthw_stat->m_nb_color_counters, + sizeof(struct color_counters)); + if (!p_nt4ga_stat->mp_stat_structs_color) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", __func__, + __LINE__); + return -1; + } + + p_nt4ga_stat->mp_stat_structs_hb = + calloc(p_nt4ga_stat->mn_rx_host_buffers + p_nt4ga_stat->mn_tx_host_buffers, + sizeof(struct host_buffer_counters)); + if (!p_nt4ga_stat->mp_stat_structs_hb) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", __func__, + __LINE__); + return -1; + } + + /* + * Separate memory allocation for VSWITCH and Inline to appropriate port counter structures. + */ + if (p_nthw_stat->mb_is_vswitch) { + p_nt4ga_stat->virt.mp_stat_structs_port_rx = + calloc(p_nthw_stat->m_nb_rx_host_buffers, + sizeof(struct port_counters_vswitch_v1)); + if (!p_nt4ga_stat->virt.mp_stat_structs_port_rx) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", + __func__, __LINE__); + return -1; + } + p_nt4ga_stat->virt.mp_stat_structs_port_tx = + calloc(p_nthw_stat->m_nb_tx_host_buffers, + sizeof(struct port_counters_vswitch_v1)); + if (!p_nt4ga_stat->virt.mp_stat_structs_port_tx) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", + __func__, __LINE__); + return -1; + } + p_nt4ga_stat->flm_stat_ver = 0; + p_nt4ga_stat->mp_stat_structs_flm = NULL; + } else { /* Inline */ + p_nt4ga_stat->cap.mp_stat_structs_port_rx = + calloc(NUM_ADAPTER_PORTS_MAX, + sizeof(struct port_counters_v2)); + if (!p_nt4ga_stat->cap.mp_stat_structs_port_rx) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", + __func__, __LINE__); + return -1; + } + p_nt4ga_stat->cap.mp_stat_structs_port_tx = + calloc(NUM_ADAPTER_PORTS_MAX, + sizeof(struct port_counters_v2)); + if (!p_nt4ga_stat->cap.mp_stat_structs_port_tx) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", + __func__, __LINE__); + return -1; + } + + p_nt4ga_stat->flm_stat_ver = 0; + + p_nt4ga_stat->mp_stat_structs_flm = + calloc(1, sizeof(struct flm_counters_v1)); + if (!p_nt4ga_stat->mp_stat_structs_flm) { + NT_LOG(ERR, GENERAL, "Cannot allocate mem (%s:%d).\n", + __func__, __LINE__); + return -1; + } + } + + memset(p_nt4ga_stat->a_stat_structs_color_base, 0, + sizeof(struct color_counters) * NT_MAX_COLOR_FLOW_STATS); + p_nt4ga_stat->last_timestamp = 0; + + nthw_stat_trigger(p_nthw_stat); + + return 0; +} + +int nt4ga_stat_stop(struct adapter_info_s *p_adapter_info) +{ + nt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat; + + if (p_nt4ga_stat->virt.mp_stat_structs_port_rx) { + free(p_nt4ga_stat->virt.mp_stat_structs_port_rx); + p_nt4ga_stat->virt.mp_stat_structs_port_rx = NULL; + } + if (p_nt4ga_stat->cap.mp_stat_structs_port_rx) { + free(p_nt4ga_stat->cap.mp_stat_structs_port_rx); + p_nt4ga_stat->cap.mp_stat_structs_port_rx = NULL; + } + + if (p_nt4ga_stat->virt.mp_stat_structs_port_tx) { + free(p_nt4ga_stat->virt.mp_stat_structs_port_tx); + p_nt4ga_stat->virt.mp_stat_structs_port_tx = NULL; + } + if (p_nt4ga_stat->cap.mp_stat_structs_port_tx) { + free(p_nt4ga_stat->cap.mp_stat_structs_port_tx); + p_nt4ga_stat->cap.mp_stat_structs_port_tx = NULL; + } + + if (p_nt4ga_stat->mp_stat_structs_color) { + free(p_nt4ga_stat->mp_stat_structs_color); + p_nt4ga_stat->mp_stat_structs_color = NULL; + } + + if (p_nt4ga_stat->mp_stat_structs_hb) { + free(p_nt4ga_stat->mp_stat_structs_hb); + p_nt4ga_stat->mp_stat_structs_hb = NULL; + } + + if (p_nt4ga_stat->mp_stat_structs_flm) { + free(p_nt4ga_stat->mp_stat_structs_flm); + p_nt4ga_stat->mp_stat_structs_flm = NULL; + } + + if (p_nt4ga_stat->p_stat_dma) { + nt_dma_free(p_nt4ga_stat->p_stat_dma); + p_nt4ga_stat->p_stat_dma = NULL; + } + + return 0; +} + +int nt4ga_stat_dump(struct adapter_info_s *p_adapter_info, FILE *pfh) +{ + const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str; + fpga_info_t *fpga_info = &p_adapter_info->fpga_info; + nt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat; + int i; + + for (i = 0; i < fpga_info->n_phy_ports; i++) { + fprintf(pfh, + "%s: Intf %02d: Rx: %016" PRIX64 " %016" PRIX64 + " %016" PRIX64 " Tx: %016" PRIX64 " %016" PRIX64 + " %016" PRIX64 "\n", + p_adapter_id_str, i, p_nt4ga_stat->a_port_rx_packets_total[i], + p_nt4ga_stat->a_port_rx_octets_total[i], + p_nt4ga_stat->a_port_rx_drops_total[i], + p_nt4ga_stat->a_port_tx_packets_total[i], + p_nt4ga_stat->a_port_tx_octets_total[i], + p_nt4ga_stat->a_port_tx_drops_total[i]); + } + + return 0; +} + +/* Called with stat mutex locked */ +static int nt4ga_stat_collect_virt_v1_stats(nt4ga_stat_t *p_nt4ga_stat, + uint32_t *p_stat_dma_virtual) +{ + nthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat; + const int n_rx_ports = p_nt4ga_stat->mn_rx_ports; + const int n_tx_ports = p_nt4ga_stat->mn_tx_ports; + int c, h, p; + + if (!p_nthw_stat || !p_nt4ga_stat) + return -1; + + if (p_nthw_stat->mn_stat_layout_version != 6) { + NT_LOG(ERR, ETHDEV, "HW STA module version not supported"); + return -1; + } + + /* RX ports */ + for (c = 0; c < p_nthw_stat->m_nb_color_counters / 2; c++) { + const unsigned int tcp_flags_bits = 6U; + const uint32_t val_mask_dma = 0xffffffffULL >> tcp_flags_bits; + + p_nt4ga_stat->mp_stat_structs_color[c].color_packets += + p_stat_dma_virtual[c * 2] & val_mask_dma; + p_nt4ga_stat->mp_stat_structs_color[c].tcp_flags |= + (uint8_t)(p_stat_dma_virtual[c * 2] >> + (32 - tcp_flags_bits)); + p_nt4ga_stat->mp_stat_structs_color[c].color_bytes += + p_stat_dma_virtual[c * 2 + 1]; + } + + /* Move to Host buffer counters */ + p_stat_dma_virtual += p_nthw_stat->m_nb_color_counters; + + /* Host buffer counters */ + for (h = 0; h < p_nthw_stat->m_nb_rx_host_buffers; h++) { + p_nt4ga_stat->mp_stat_structs_hb[h].flush_packets += + p_stat_dma_virtual[h * 8]; + p_nt4ga_stat->mp_stat_structs_hb[h].drop_packets += + p_stat_dma_virtual[h * 8 + 1]; + p_nt4ga_stat->mp_stat_structs_hb[h].fwd_packets += + p_stat_dma_virtual[h * 8 + 2]; + p_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_packets += + p_stat_dma_virtual[h * 8 + 3]; + p_nt4ga_stat->mp_stat_structs_hb[h].flush_bytes += + p_stat_dma_virtual[h * 8 + 4]; + p_nt4ga_stat->mp_stat_structs_hb[h].drop_bytes += + p_stat_dma_virtual[h * 8 + 5]; + p_nt4ga_stat->mp_stat_structs_hb[h].fwd_bytes += + p_stat_dma_virtual[h * 8 + 6]; + p_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_bytes += + p_stat_dma_virtual[h * 8 + 7]; + } + + /* Move to Rx Port counters */ + p_stat_dma_virtual += p_nthw_stat->m_nb_rx_hb_counters; + + /* RX ports */ + for (p = 0; p < n_rx_ports; p++) { + p_nt4ga_stat->virt.mp_stat_structs_port_rx[p].octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters]; + p_nt4ga_stat->virt.mp_stat_structs_port_rx[p].pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1]; + p_nt4ga_stat->virt.mp_stat_structs_port_rx[p].drop_events += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2]; + p_nt4ga_stat->virt.mp_stat_structs_port_rx[p].qos_drop_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 3]; + p_nt4ga_stat->virt.mp_stat_structs_port_rx[p].qos_drop_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 4]; + + /* Rx totals */ + p_nt4ga_stat->a_port_rx_octets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters]; + p_nt4ga_stat->a_port_rx_packets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1]; + p_nt4ga_stat->a_port_rx_drops_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2]; + } + + /* Move to Tx Port counters */ + p_stat_dma_virtual += n_rx_ports * p_nthw_stat->m_nb_rx_port_counters; + + /* TX ports */ + for (p = 0; p < n_tx_ports; p++) { + p_nt4ga_stat->virt.mp_stat_structs_port_tx[p].octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters]; + p_nt4ga_stat->virt.mp_stat_structs_port_tx[p].pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1]; + p_nt4ga_stat->virt.mp_stat_structs_port_tx[p].drop_events += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2]; + p_nt4ga_stat->virt.mp_stat_structs_port_tx[p].qos_drop_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 3]; + p_nt4ga_stat->virt.mp_stat_structs_port_tx[p].qos_drop_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 4]; + + /* Tx totals */ + p_nt4ga_stat->a_port_tx_octets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters]; + p_nt4ga_stat->a_port_tx_packets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1]; + p_nt4ga_stat->a_port_tx_drops_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2]; + } + + return 0; +} + +/* Called with stat mutex locked */ +static int nt4ga_stat_collect_cap_v1_stats(nt4ga_stat_t *p_nt4ga_stat, + uint32_t *p_stat_dma_virtual) +{ + nthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat; + + const int n_rx_ports = p_nt4ga_stat->mn_rx_ports; + const int n_tx_ports = p_nt4ga_stat->mn_tx_ports; + int c, h, p; + + if (!p_nthw_stat || !p_nt4ga_stat) + return -1; + + if (p_nthw_stat->mn_stat_layout_version != 6) { + NT_LOG(ERR, ETHDEV, "HW STA module version not supported"); + return -1; + } + + /* RX ports */ + for (c = 0; c < p_nthw_stat->m_nb_color_counters / 2; c++) { + p_nt4ga_stat->mp_stat_structs_color[c].color_packets += + p_stat_dma_virtual[c * 2]; + p_nt4ga_stat->mp_stat_structs_color[c].color_bytes += + p_stat_dma_virtual[c * 2 + 1]; + } + + /* Move to Host buffer counters */ + p_stat_dma_virtual += p_nthw_stat->m_nb_color_counters; + + for (h = 0; h < p_nthw_stat->m_nb_rx_host_buffers; h++) { + p_nt4ga_stat->mp_stat_structs_hb[h].flush_packets += + p_stat_dma_virtual[h * 8]; + p_nt4ga_stat->mp_stat_structs_hb[h].drop_packets += + p_stat_dma_virtual[h * 8 + 1]; + p_nt4ga_stat->mp_stat_structs_hb[h].fwd_packets += + p_stat_dma_virtual[h * 8 + 2]; + p_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_packets += + p_stat_dma_virtual[h * 8 + 3]; + p_nt4ga_stat->mp_stat_structs_hb[h].flush_bytes += + p_stat_dma_virtual[h * 8 + 4]; + p_nt4ga_stat->mp_stat_structs_hb[h].drop_bytes += + p_stat_dma_virtual[h * 8 + 5]; + p_nt4ga_stat->mp_stat_structs_hb[h].fwd_bytes += + p_stat_dma_virtual[h * 8 + 6]; + p_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_bytes += + p_stat_dma_virtual[h * 8 + 7]; + } + + /* Move to Rx Port counters */ + p_stat_dma_virtual += p_nthw_stat->m_nb_rx_hb_counters; + + /* RX ports */ + for (p = 0; p < n_rx_ports; p++) { + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 0]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].broadcast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].multicast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].unicast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 3]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_alignment += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 4]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_code_violation += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 5]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_crc += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 6]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].undersize_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 7]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].oversize_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 8]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].fragments += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 9]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].jabbers_not_truncated += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 10]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].jabbers_truncated += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 11]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_64_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 12]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_65_to_127_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 13]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_128_to_255_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 14]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_256_to_511_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 15]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_512_to_1023_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 16]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p] + .pkts_1024_to_1518_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 17]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p] + .pkts_1519_to_2047_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 18]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p] + .pkts_2048_to_4095_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 19]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p] + .pkts_4096_to_8191_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 20]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_8192_to_max_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 21]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].mac_drop_events += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_lr += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 23]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].duplicate += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 24]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_ip_chksum_error += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 25]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_udp_chksum_error += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 26]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_tcp_chksum_error += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 27]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_giant_undersize += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 28]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_baby_giant += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 29]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_not_isl_vlan_mpls += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 30]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 31]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_vlan += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 32]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 33]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_mpls += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 34]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_mpls += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 35]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_vlan_mpls += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 36]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan_mpls += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 37]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_no_filter += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 38]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_dedup_drop += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 39]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_filter_drop += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 40]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_overflow += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 41]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_dbs_drop += + p_nthw_stat->m_dbs_present ? + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + + 42] : + 0; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_no_filter += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 43]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_dedup_drop += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 44]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_filter_drop += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 45]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_overflow += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 46]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_dbs_drop += + p_nthw_stat->m_dbs_present ? + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + + 47] : + 0; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_first_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 48]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_first_not_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 49]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_mid_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 50]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_mid_not_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 51]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_last_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 52]; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_last_not_hit += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 53]; + + /* Rx totals */ + uint64_t new_drop_events_sum = + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 38] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 39] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 40] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 41] + + (p_nthw_stat->m_dbs_present ? + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + + 42] : + 0); + + uint64_t new_packets_sum = + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 7] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 8] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 9] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 10] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 11] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 12] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 13] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 14] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 15] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 16] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 17] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 18] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 19] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 20] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 21]; + + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].drop_events += + new_drop_events_sum; + p_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts += new_packets_sum; + + p_nt4ga_stat->a_port_rx_octets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 0]; + p_nt4ga_stat->a_port_rx_packets_total[p] += new_packets_sum; + p_nt4ga_stat->a_port_rx_drops_total[p] += new_drop_events_sum; + } + + /* Move to Tx Port counters */ + p_stat_dma_virtual += n_rx_ports * p_nthw_stat->m_nb_rx_port_counters; + + for (p = 0; p < n_tx_ports; p++) { + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 0]; + + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].broadcast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].multicast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].unicast_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 3]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_alignment += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 4]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_code_violation += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 5]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_crc += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 6]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].undersize_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 7]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].oversize_pkts += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 8]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].fragments += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 9]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].jabbers_not_truncated += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 10]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].jabbers_truncated += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 11]; + + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_64_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 12]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_65_to_127_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 13]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_128_to_255_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 14]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_256_to_511_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 15]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_512_to_1023_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 16]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p] + .pkts_1024_to_1518_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 17]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p] + .pkts_1519_to_2047_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 18]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p] + .pkts_2048_to_4095_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 19]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p] + .pkts_4096_to_8191_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 20]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_8192_to_max_octets += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 21]; + + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].mac_drop_events += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 22]; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_lr += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 23]; + + /* Tx totals */ + uint64_t new_drop_events_sum = + p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22]; + + uint64_t new_packets_sum = + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 7] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 8] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 9] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 10] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 11] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 12] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 13] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 14] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 15] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 16] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 17] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 18] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 19] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 20] + + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 21]; + + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].drop_events += + new_drop_events_sum; + p_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts += new_packets_sum; + + p_nt4ga_stat->a_port_tx_octets_total[p] += + p_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 0]; + p_nt4ga_stat->a_port_tx_packets_total[p] += new_packets_sum; + p_nt4ga_stat->a_port_tx_drops_total[p] += new_drop_events_sum; + } + + return 0; +} diff --git a/drivers/net/ntnic/adapter/nt4ga_stat.h b/drivers/net/ntnic/adapter/nt4ga_stat.h new file mode 100644 index 0000000000..4a1067200c --- /dev/null +++ b/drivers/net/ntnic/adapter/nt4ga_stat.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NT4GA_STAT_H_ +#define NT4GA_STAT_H_ + +#include "nt_util.h" +#include "common_adapter_defs.h" + +#define NT_MAX_COLOR_FLOW_STATS 0x400 + +struct color_counters { + uint64_t color_packets; + uint64_t color_bytes; + uint8_t tcp_flags; +}; + +struct host_buffer_counters { + uint64_t flush_packets; + uint64_t drop_packets; + uint64_t fwd_packets; + uint64_t dbs_drop_packets; + uint64_t flush_bytes; + uint64_t drop_bytes; + uint64_t fwd_bytes; + uint64_t dbs_drop_bytes; +}; + +struct port_counters_v2 { + /* Rx/Tx common port counters */ + uint64_t drop_events; + uint64_t pkts; + /* FPGA counters */ + uint64_t octets; + uint64_t broadcast_pkts; + uint64_t multicast_pkts; + uint64_t unicast_pkts; + uint64_t pkts_alignment; + uint64_t pkts_code_violation; + uint64_t pkts_crc; + uint64_t undersize_pkts; + uint64_t oversize_pkts; + uint64_t fragments; + uint64_t jabbers_not_truncated; + uint64_t jabbers_truncated; + uint64_t pkts_64_octets; + uint64_t pkts_65_to_127_octets; + uint64_t pkts_128_to_255_octets; + uint64_t pkts_256_to_511_octets; + uint64_t pkts_512_to_1023_octets; + uint64_t pkts_1024_to_1518_octets; + uint64_t pkts_1519_to_2047_octets; + uint64_t pkts_2048_to_4095_octets; + uint64_t pkts_4096_to_8191_octets; + uint64_t pkts_8192_to_max_octets; + uint64_t mac_drop_events; + uint64_t pkts_lr; + /* Rx only port counters */ + uint64_t duplicate; + uint64_t pkts_ip_chksum_error; + uint64_t pkts_udp_chksum_error; + uint64_t pkts_tcp_chksum_error; + uint64_t pkts_giant_undersize; + uint64_t pkts_baby_giant; + uint64_t pkts_not_isl_vlan_mpls; + uint64_t pkts_isl; + uint64_t pkts_vlan; + uint64_t pkts_isl_vlan; + uint64_t pkts_mpls; + uint64_t pkts_isl_mpls; + uint64_t pkts_vlan_mpls; + uint64_t pkts_isl_vlan_mpls; + uint64_t pkts_no_filter; + uint64_t pkts_dedup_drop; + uint64_t pkts_filter_drop; + uint64_t pkts_overflow; + uint64_t pkts_dbs_drop; + uint64_t octets_no_filter; + uint64_t octets_dedup_drop; + uint64_t octets_filter_drop; + uint64_t octets_overflow; + uint64_t octets_dbs_drop; + uint64_t ipft_first_hit; + uint64_t ipft_first_not_hit; + uint64_t ipft_mid_hit; + uint64_t ipft_mid_not_hit; + uint64_t ipft_last_hit; + uint64_t ipft_last_not_hit; +}; + +struct port_counters_vswitch_v1 { + /* Rx/Tx common port counters */ + uint64_t octets; + uint64_t pkts; + uint64_t drop_events; + uint64_t qos_drop_octets; + uint64_t qos_drop_pkts; +}; + +struct flm_counters_v1 { + /* FLM 0.17 */ + uint64_t current; + uint64_t learn_done; + uint64_t learn_ignore; + uint64_t learn_fail; + uint64_t unlearn_done; + uint64_t unlearn_ignore; + uint64_t auto_unlearn_done; + uint64_t auto_unlearn_ignore; + uint64_t auto_unlearn_fail; + uint64_t timeout_unlearn_done; + uint64_t rel_done; + uint64_t rel_ignore; + /* FLM 0.20 */ + uint64_t prb_done; + uint64_t prb_ignore; + uint64_t sta_done; + uint64_t inf_done; + uint64_t inf_skip; + uint64_t pck_hit; + uint64_t pck_miss; + uint64_t pck_unh; + uint64_t pck_dis; + uint64_t csh_hit; + uint64_t csh_miss; + uint64_t csh_unh; + uint64_t cuc_start; + uint64_t cuc_move; +}; + +struct nt4ga_stat_s { + nthw_stat_t *mp_nthw_stat; + nthw_rmc_t *mp_nthw_rmc; + struct nt_dma_s *p_stat_dma; + uint32_t *p_stat_dma_virtual; + uint32_t n_stat_size; + + uint64_t last_timestamp; + + int mn_rx_host_buffers; + int mn_tx_host_buffers; + + int mn_rx_ports; + int mn_tx_ports; + + struct color_counters *mp_stat_structs_color; + /* For calculating increments between stats polls */ + struct color_counters a_stat_structs_color_base[NT_MAX_COLOR_FLOW_STATS]; + + union { + /*Port counters for VSWITCH/inline */ + struct { + struct port_counters_vswitch_v1 *mp_stat_structs_port_rx; + struct port_counters_vswitch_v1 *mp_stat_structs_port_tx; + } virt; + struct { + struct port_counters_v2 *mp_stat_structs_port_rx; + struct port_counters_v2 *mp_stat_structs_port_tx; + } cap; + }; + + struct host_buffer_counters *mp_stat_structs_hb; + + int flm_stat_ver; + struct flm_counters_v1 *mp_stat_structs_flm; + + /* Rx/Tx totals: */ + uint64_t n_totals_reset_timestamp; /* timestamp for last totals reset */ + + uint64_t a_port_rx_octets_total[NUM_ADAPTER_PORTS_MAX]; + /* Base is for calculating increments between statistics reads */ + uint64_t a_port_rx_octets_base[NUM_ADAPTER_PORTS_MAX]; + + uint64_t a_port_rx_packets_total[NUM_ADAPTER_PORTS_MAX]; + uint64_t a_port_rx_packets_base[NUM_ADAPTER_PORTS_MAX]; + + uint64_t a_port_rx_drops_total[NUM_ADAPTER_PORTS_MAX]; + uint64_t a_port_rx_drops_base[NUM_ADAPTER_PORTS_MAX]; + + uint64_t a_port_tx_octets_total[NUM_ADAPTER_PORTS_MAX]; + uint64_t a_port_tx_octets_base[NUM_ADAPTER_PORTS_MAX]; + + uint64_t a_port_tx_packets_base[NUM_ADAPTER_PORTS_MAX]; + uint64_t a_port_tx_packets_total[NUM_ADAPTER_PORTS_MAX]; + + uint64_t a_port_tx_drops_base[NUM_ADAPTER_PORTS_MAX]; + uint64_t a_port_tx_drops_total[NUM_ADAPTER_PORTS_MAX]; +}; + +typedef struct nt4ga_stat_s nt4ga_stat_t; + +int nt4ga_stat_init(struct adapter_info_s *p_adapter_info); +int nt4ga_stat_setup(struct adapter_info_s *p_adapter_info); +int nt4ga_stat_stop(struct adapter_info_s *p_adapter_info); + +int nt4ga_stat_dump(struct adapter_info_s *p_adapter_info, FILE *pfh); + +int nt4ga_stat_collect(struct adapter_info_s *p_adapter_info, + nt4ga_stat_t *p_nt4ga_stat); + +#endif /* NT4GA_STAT_H_ */ diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build index 65064f44ab..383ff15390 100644 --- a/drivers/net/ntnic/meson.build +++ b/drivers/net/ntnic/meson.build @@ -9,22 +9,39 @@ cflags += [ # includes includes = [ include_directories('.'), + include_directories('adapter'), include_directories('include'), + include_directories('nim'), include_directories('ntlog/include'), include_directories('ntutil/include'), include_directories('nthw'), include_directories('nthw/core'), include_directories('nthw/supported'), + include_directories('sensors'), + include_directories('sensors/avr_sensors'), + include_directories('sensors/board_sensors'), + include_directories('sensors/nim_sensors'), + include_directories('sensors/ntavr'), ] # all sources sources = files( + 'adapter/nt4ga_adapter.c', + 'adapter/nt4ga_link.c', + 'adapter/nt4ga_link_100g.c', + 'adapter/nt4ga_pci_ta_tg.c', + 'adapter/nt4ga_stat.c', + 'nim/i2c_nim.c', + 'nim/nt_link_speed.c', + 'nim/qsfp_sensors.c', + 'nim/sfp_sensors.c', 'nthw/core/nthw_clock_profiles.c', 'nthw/core/nthw_fpga.c', 'nthw/core/nthw_fpga_nt200a0x.c', 'nthw/core/nthw_fpga_rst.c', 'nthw/core/nthw_fpga_rst9563.c', 'nthw/core/nthw_fpga_rst_nt200a0x.c', + 'nthw/core/nthw_gmf.c', 'nthw/core/nthw_gpio_phy.c', 'nthw/core/nthw_hif.c', 'nthw/core/nthw_iic.c', @@ -34,6 +51,7 @@ sources = files( 'nthw/core/nthw_pci_ta.c', 'nthw/core/nthw_pci_wr_tg.c', 'nthw/core/nthw_pcie3.c', + 'nthw/core/nthw_rmc.c', 'nthw/core/nthw_sdc.c', 'nthw/core/nthw_si5340.c', 'nthw/core/nthw_spi_v3.c', @@ -49,6 +67,12 @@ sources = files( 'nthw/supported/nthw_fpga_9563_055_024_0000.c', 'ntlog/ntlog.c', 'ntutil/nt_util.c', + 'sensors/avr_sensors/avr_sensors.c', + 'sensors/board_sensors/board_sensors.c', + 'sensors/board_sensors/tempmon.c', + 'sensors/nim_sensors/nim_sensors.c', + 'sensors/ntavr/ntavr.c', + 'sensors/sensors.c', ) if is_variable('default_cflags') diff --git a/drivers/net/ntnic/nim/i2c_nim.c b/drivers/net/ntnic/nim/i2c_nim.c new file mode 100644 index 0000000000..55740e6de6 --- /dev/null +++ b/drivers/net/ntnic/nim/i2c_nim.c @@ -0,0 +1,1974 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "nthw_drv.h" +#include "i2c_nim.h" +#include "ntlog.h" +#include "nt_util.h" + +#include "nim_sensors.h" +#include "sfp_p_registers.h" +#include "qsfp_registers.h" +#include "sfp_sensors.h" +#include "qsfp_sensors.h" + +#include +#include /* memcmp, memset */ + +/* + * Nim functions + */ +#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */ +#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */ +#define QSFP_CONTROL_STATUS_LIN_ADDR 86 +#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F +#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */ +#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */ +#define NIM_READ false +#define NIM_WRITE true +#define NIM_PAGE_SEL_REGISTER 127 +#define nim_i2c_0xa0 0xA0 /* Basic I2C address */ +#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */ +#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */ +#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */ +#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */ +#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */ +#define QSFP_DMI_OPTION_LIN_ADDR 220 + +#define QSFP_EXTENDED_IDENTIFIER 129 +#define QSFP_POWER_CLASS_BITS_1_4 0xC0 +#define QSFP_POWER_CLASS_BITS_5_7 0x03 + +static bool sfp_is_supported_tri_speed_pn(char *prod_no) +{ + static const char *const pn_trispeed_list[] = { + "FCMJ-8521-3", "FCLF-8521-3", "FCLF8521P2BTL", "EOLT-C12-02A", + "AMXP-24RJS", "ABCU-5710RZ", "ABCU-5740RZ", "FCLF8522P2BTL", + }; + + /* Determine if copper SFP is supported 3-speed type */ + for (size_t i = 0; i < ARRAY_SIZE(pn_trispeed_list); i++) + if (strcmp(pn_trispeed_list[i], prod_no) == 0) + return true; + + return false; +} + +static bool page_addressing(nt_nim_identifier_t id) +{ + switch (id) { + case NT_NIM_SFP_SFP_PLUS: + return false; + case NT_NIM_XFP: + return true; + case NT_NIM_QSFP: + case NT_NIM_QSFP_PLUS: + case NT_NIM_QSFP28: + return true; + default: + NT_LOG(DBG, ETHDEV, "%s: Unknown NIM identifier %d\n", __func__, + id); + return false; + } +} + +nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx) +{ + return (nt_nim_identifier_t)ctx->nim_id; +} + +static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write, + uint16_t lin_addr, uint8_t i2c_addr, + uint8_t reg_addr, uint8_t seq_cnt, uint8_t *p_data) +{ + /* Divide I2C_Addr by 2 because nthw_iic_read/writeData multiplies by 2 */ + const uint8_t i2c_devaddr = i2c_addr / 2U; + (void)lin_addr; /* Unused */ + + if (do_write) + return nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, reg_addr, + seq_cnt, p_data); + else + return nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, reg_addr, + seq_cnt, p_data); +} + +/* + * ------------------------------------------------------------------------------ + * Selects a new page for page addressing. This is only relevant if the NIM + * supports this. Since page switching can take substantial time the current page + * select is read and subsequently only changed if necessary. + * Important: + * XFP Standard 8077, Ver 4.5, Page 61 states that: + * If the host attempts to write a table select value which is not supported in + * a particular module, the table select byte will revert to 01h. + * This can lead to some surprising result that some pages seems to be duplicated. + * ------------------------------------------------------------------------------ + */ + +static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel) +{ + uint8_t curr_page_sel; + + /* Read the current page select value */ + if (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER, + nim_i2c_0xa0, NIM_PAGE_SEL_REGISTER, + sizeof(curr_page_sel), &curr_page_sel) != 0) + return -1; + + /* Only write new page select value if necessary */ + if (page_sel != curr_page_sel) { + if (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER, + nim_i2c_0xa0, NIM_PAGE_SEL_REGISTER, + sizeof(page_sel), &page_sel) != 0) + return -1; + } + return 0; +} + +static int nim_nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing, + uint16_t lin_addr, uint16_t length, + uint8_t *p_data, bool do_write) +{ + uint16_t i; + uint8_t reg_addr; /* The actual register address in I2C device */ + uint8_t i2c_addr; + int block_size = 128; /* Equal to size of MSA pages */ + int seq_cnt; + int max_seq_cnt = 1; + int multi_byte = 1; /* One byte per I2C register is default */ + const int m_port_no = ctx->instance - 2; + + if (lin_addr >= SFP_PHY_LIN_ADDR) { + /* + * This represents an address space at I2C address 0xAC for SFP modules + * containing a PHY. (eg 1G Copper SFP). Each register is 16bit and is + * accessed MSByte first and this reading latches the LSByte that is + * subsequently read from the same address. + */ + multi_byte = 2; + max_seq_cnt = 2; + + /* Test for correct multibyte access */ + if ((length % multi_byte) != 0) { + NT_LOG(ERR, ETHDEV, + "Port %d: %s: Uneven length (%d) for address range [0x%X..0x%X].", + m_port_no, __func__, length, SFP_PHY_LIN_ADDR, + SFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG - 1); + return -1; + } + + if (lin_addr + (length / 2) > + SFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG) { + NT_LOG(ERR, ETHDEV, + "Port %d: %s: Access above address range [0x%X..0x%X].", + m_port_no, __func__, SFP_PHY_LIN_ADDR, + SFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG - 1); + return -1; + } + } else if (lin_addr + length > 128) { + /* + * Page addressing could be relevant since the last byte is outside the + * basic range so check if it is enabled + */ + if (m_page_addressing) { + /* Crossing into the PHY address range is not allowed */ + if (lin_addr + length > SFP_PHY_LIN_ADDR) { + NT_LOG(ERR, ETHDEV, + "Port %d: %s: Access above paged address range [0..0x%X].", + m_port_no, __func__, SFP_PHY_LIN_ADDR); + return -1; + } + } else { + /* Access outside 0xA2 address range not allowed */ + if (lin_addr + length > 512) { + NT_LOG(ERR, ETHDEV, + "Port %d: %s: Access above address range [0..511].", + m_port_no, __func__); + return -1; + } + } + } + /* No missing else here - all devices supports access to address [0..127] */ + + for (i = 0; i < length;) { + bool use_page_select = false; + + /* + * Find out how much can be read from the current block in case of + * single byte access + */ + if (multi_byte == 1) + max_seq_cnt = block_size - (lin_addr % block_size); + + if (m_page_addressing) { + if (lin_addr >= 128) { /* Only page setup above this address */ + use_page_select = true; + + /* Map to [128..255] of 0xA0 device */ + reg_addr = (uint8_t)(block_size + + (lin_addr % block_size)); + } else { + reg_addr = (uint8_t)lin_addr; + } + i2c_addr = nim_i2c_0xa0; /* Base I2C address */ + } else { + if (lin_addr >= SFP_PHY_LIN_ADDR) { + /* Map to address [0..31] of 0xAC device */ + reg_addr = (uint8_t)(lin_addr - SFP_PHY_LIN_ADDR); + i2c_addr = nim_i2c_0xac; + } else if (lin_addr >= 256) { + /* Map to address [0..255] of 0xA2 device */ + reg_addr = (uint8_t)(lin_addr - 256); + i2c_addr = nim_i2c_0xa2; + } else { + reg_addr = (uint8_t)lin_addr; + i2c_addr = nim_i2c_0xa0; /* Base I2C address */ + } + } + + /* Now actually do the reading/writing */ + seq_cnt = length - i; /* Number of remaining bytes */ + + if (seq_cnt > max_seq_cnt) + seq_cnt = max_seq_cnt; + + /* + * Read a number of bytes without explicitly specifying a new address. + * This can speed up I2C access since automatic incrementation of the + * I2C device internal address counter can be used. It also allows + * a HW implementation, that can deal with block access. + * Furthermore it also allows for access to data that must be accessed + * as 16bit words reading two bytes at each address eg PHYs. + */ + if (use_page_select) { + if (nim_setup_page(ctx, + (uint8_t)((lin_addr / 128) - 1)) != 0) { + NT_LOG(ERR, ETHDEV, + "%s: Cannot set up page for linear address %u\n", + __func__, lin_addr); + return -1; + } + } + if (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr, + reg_addr, (uint8_t)seq_cnt, + p_data) != 0) { + NT_LOG(ERR, ETHDEV, + "%s: Call to NIM_ReadWriteI2cData failed\n", + __func__); + return -1; + } + + p_data += seq_cnt; + i = (uint16_t)(i + seq_cnt); + lin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte)); + } + return 0; +} + +int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, + void *data) +{ + return nim_nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), + lin_addr, length, data, NIM_READ); +} + +static int write_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, + void *data) +{ + return nim_nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id), + lin_addr, length, data, NIM_WRITE); +} + +/* Read and return a single byte */ +static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr) +{ + uint8_t data; + + read_data_lin(ctx, addr, sizeof(data), &data); + return data; +} + +static int nim_read_id(nim_i2c_ctx_t *ctx) +{ + /* We are only reading the first byte so we don't care about pages here. */ + const bool use_page_addressing = false; + + if (nim_nim_read_write_data_lin(ctx, use_page_addressing, + NIM_IDENTIFIER_ADDR, sizeof(ctx->nim_id), + &ctx->nim_id, NIM_READ) != 0) + return -1; + return 0; +} + +static int i2c_nim_common_construct(nim_i2c_ctx_p ctx) +{ + ctx->nim_id = 0; + int res = nim_read_id(ctx); + + if (res) { + NT_LOG(ERR, PMD, "Can't read NIM id."); + return res; + } + memset(ctx->vendor_name, 0, sizeof(ctx->vendor_name)); + memset(ctx->prod_no, 0, sizeof(ctx->prod_no)); + memset(ctx->serial_no, 0, sizeof(ctx->serial_no)); + memset(ctx->date, 0, sizeof(ctx->date)); + memset(ctx->rev, 0, sizeof(ctx->rev)); + + ctx->content_valid = false; + memset(ctx->len_info, 0, sizeof(ctx->len_info)); + ctx->pwr_level_req = 0; + ctx->pwr_level_cur = 0; + ctx->avg_pwr = false; + ctx->tx_disable = false; + ctx->lane_idx = -1; + ctx->lane_count = 1; + ctx->options = 0; + return 0; +} + +static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, + uint8_t max_len, char *p_data); + +#define XSFP_READ_VENDOR_INFO(x) \ + static void x##sfp_read_vendor_info(nim_i2c_ctx_t *ctx) \ + { \ + nim_read_vendor_info(ctx, Q##SFP_VENDOR_NAME_LIN_ADDR, \ + sizeof(ctx->vendor_name), \ + ctx->vendor_name); \ + nim_read_vendor_info(ctx, Q##SFP_VENDOR_PN_LIN_ADDR, \ + sizeof(ctx->prod_no), ctx->prod_no); \ + nim_read_vendor_info(ctx, Q##SFP_VENDOR_SN_LIN_ADDR, \ + sizeof(ctx->serial_no), ctx->serial_no); \ + nim_read_vendor_info(ctx, Q##SFP_VENDOR_DATE_LIN_ADDR, \ + sizeof(ctx->date), ctx->date); \ + nim_read_vendor_info(ctx, Q##SFP_VENDOR_REV_LIN_ADDR, \ + (uint8_t)(sizeof(ctx->rev) - 2), \ + ctx->rev); /*OBS Only two bytes*/ \ + } + +XSFP_READ_VENDOR_INFO() +XSFP_READ_VENDOR_INFO(q) + +static int sfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state) +{ + int res; + + assert(ctx && state); + assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized"); + + (void)memset(state, 0, sizeof(*state)); + + res = nthw_iic_read_data(&ctx->hwiic, ctx->devaddr, SFP_BIT_RATE_ADDR, + sizeof(state->br), &state->br); + return res; +} + +static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state) +{ + int res = 0; /* unused due to no readings from HW */ + + assert(ctx && state); + assert(ctx->nim_id != NT_NIM_UNKNOWN && "Nim is not initialized"); + + (void)memset(state, 0, sizeof(*state)); + + switch (ctx->nim_id) { + case 12U: + state->br = 10U; /* QSFP: 4 x 1G = 4G */ + break; + case 13U: + state->br = 103U; /* QSFP+: 4 x 10G = 40G */ + break; + case 17U: + state->br = 255U; /* QSFP28: 4 x 25G = 100G */ + break; + default: + NT_LOG(INF, PMD, + "%s:%d nim_id = %u is not an QSFP/QSFP+/QSFP28 module\n", + __func__, __LINE__, ctx->nim_id); + res = -1; + } + + return res; +} + +int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state) +{ + if (translate_nimid(ctx) == NT_NIM_SFP_SFP_PLUS) + return sfp_nim_state_build(ctx, state); + else + return qsfp_nim_state_build(ctx, state); +} + +const char *nim_id_to_text(uint8_t nim_id) +{ + switch (nim_id) { + case 0x0: + return "UNKNOWN"; + case 0x1: + return "GBIC"; + case 0x2: + return "FIXED"; + case 0x3: + return "SFP/SFP+"; + case 0x04: + return "300 pin XBI"; + case 0x05: + return "XEN-PAK"; + case 0x06: + return "XFP"; + case 0x07: + return "XFF"; + case 0x08: + return "XFP-E"; + case 0x09: + return "XPAK"; + case 0x0A: + return "X2"; + case 0x0B: + return "DWDM"; + case 0x0C: + return "QSFP"; + case 0x0D: + return "QSFP+"; + case 0x11: + return "QSFP28"; + case 0x12: + return "CFP4"; + default: + return "ILLEGAL!"; + } +} + +/* + * Read and check the validity of the NIM basic data. + * This will also preload the cache + */ +static void check_content_valid(nim_i2c_ctx_p ctx, uint16_t start_addr) +{ + uint32_t sum = 0; + uint8_t buf[96]; + + read_data_lin(ctx, start_addr, sizeof(buf), &buf[0]); + + for (int i = 0; i < 63; i++) + sum += buf[i]; + + if ((sum & 0xFF) != buf[63]) { + ctx->content_valid = false; + } else { + sum = 0; + + for (int i = 64; i < 95; i++) + sum += buf[i]; + + ctx->content_valid = ((sum & 0xFF) == buf[95]); + } + if (ctx->content_valid) + NT_LOG(DBG, NTHW, "NIM content validation passed"); + else + NT_LOG(WRN, NTHW, "NIM content validation failed"); +} + +/* + * Set/reset Soft Rate__select bits (RS0 & RS1) + */ +static void nim_sfp_set_rate_sel_high(nim_i2c_ctx_p ctx, bool rx_rate_high, + bool tx_rate_high) +{ + const bool m_page_addressing = page_addressing(ctx->nim_id); + uint8_t data; + + nim_nim_read_write_data_lin(ctx, m_page_addressing, + SFP_CONTROL_STATUS_LIN_ADDR, sizeof(data), + &data, NIM_READ); + + if (rx_rate_high) + data |= SFP_SOFT_RATE0_BIT; + else + data &= (uint8_t)~(SFP_SOFT_RATE0_BIT); + + nim_nim_read_write_data_lin(ctx, m_page_addressing, + SFP_CONTROL_STATUS_LIN_ADDR, sizeof(data), + &data, NIM_WRITE); + + /* Read the Extended Status/Control and set/reset Soft RS1 bit */ + nim_nim_read_write_data_lin(ctx, m_page_addressing, + SFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(data), + &data, NIM_READ); + + if (tx_rate_high) + data |= SFP_SOFT_RATE1_BIT; + else + data &= (uint8_t)~(SFP_SOFT_RATE1_BIT); + + nim_nim_read_write_data_lin(ctx, m_page_addressing, + SFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(data), + &data, NIM_WRITE); +} + +/* + * Some NIM modules requires some changes to a rate setting. + */ +static int nim_sfp_set_rate_select(nim_i2c_ctx_p ctx, nt_link_speed_t speed) +{ + if ((speed & (int)ctx->speed_mask) == 0) { + char buf[128]; + + NT_LOG(ERR, ETHDEV, "%s - Speed (%s) not within SpeedMask (%s)", + nt_translate_link_speed(speed), + nt_translate_link_speed_mask(ctx->speed_mask, buf, + sizeof(buf))); + return -1; + } + + if (ctx->specific_u.sfp.dual_rate) { + uint64_t req_speed = nt_get_link_speed(speed); + uint64_t other_speed = + nt_get_link_speed((nt_link_speed_t)(ctx->speed_mask ^ (uint32_t)speed)); + bool rate_high = req_speed > other_speed; + /* + * Do this both for 1/10 and 10/25. For Sfp28 it is not known if + * this is necessary but it is believed not to do any harm. + */ + nim_sfp_set_rate_sel_high(ctx, rate_high, rate_high); + } + return 0; +} + +/* + * Disable TX laser. + */ +int nim_sfp_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable) +{ + int res; + uint8_t value; + const bool pg_addr = page_addressing(ctx->nim_id); + + res = nim_nim_read_write_data_lin(ctx, pg_addr, SFP_CONTROL_STATUS_LIN_ADDR, + sizeof(value), &value, NIM_READ); + if (res != 0) + return res; + + if (disable) + value |= SFP_SOFT_TX_DISABLE_BIT; + else + value &= (uint8_t)~SFP_SOFT_TX_DISABLE_BIT; + + res = nim_nim_read_write_data_lin(ctx, pg_addr, SFP_CONTROL_STATUS_LIN_ADDR, + sizeof(value), &value, NIM_WRITE); + + return res; +} + +/* + * Disable laser for specific lane or all lanes + */ +int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable, + int lane_idx) +{ + uint8_t value; + uint8_t mask; + const bool pg_addr = page_addressing(ctx->nim_id); + + if (lane_idx < 0) /* If no lane is specified then all lanes */ + mask = QSFP_SOFT_TX_ALL_DISABLE_BITS; + else + mask = (uint8_t)(1U << lane_idx); + + if (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, + sizeof(value), &value, NIM_READ) != 0) + return -1; + + if (disable) + value |= mask; + else + value &= (uint8_t)~mask; + + if (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR, + sizeof(value), &value, NIM_WRITE) != 0) + return -1; + return 0; +} + +/* + * Read vendor information at a certain address. Any trailing whitespace is + * removed and a missing string termination in the NIM data is handled. + */ +static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr, + uint8_t max_len, char *p_data) +{ + const bool pg_addr = page_addressing(ctx->nim_id); + int i; + /* Subtract "1" from maxLen that includes a terminating "0" */ + + if (nim_nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1), + (uint8_t *)p_data, NIM_READ) != 0) + return -1; + + /* Terminate at first found white space */ + for (i = 0; i < max_len - 1; i++) { + if (*p_data == ' ' || *p_data == '\n' || *p_data == '\t' || + *p_data == '\v' || *p_data == '\f' || *p_data == '\r') { + *p_data = '\0'; + return 0; + } + + p_data++; + } + + /* + * Add line termination as the very last character, if it was missing in the + * NIM data + */ + *p_data = '\0'; + return 0; +} + +/* + * Import length info in various units from NIM module data and convert to meters + */ +static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info, + uint16_t *p_nim_units) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(ctx->len_info); i++) + if (*(p_nim_len_info + i) == 255) { + ctx->len_info[i] = 65535; + } else { + uint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i); + + if (len > 65535) + ctx->len_info[i] = 65535; + else + ctx->len_info[i] = (uint16_t)len; + } +} + +static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx) +{ + const bool pg_addr = page_addressing(ctx->nim_id); + uint8_t options; + uint8_t value; + uint8_t nim_len_info[5]; + uint16_t nim_units[5] = { 1000, 2, 1, 1, + 1 + }; /* QSFP MSA units in meters */ + const char *yes_no[2] _unused = { "No", "Yes" }; + + NT_LOG(DBG, ETHDEV, "Instance %d: NIM id: %s (%d)\n", ctx->instance, + nim_id_to_text(ctx->nim_id), ctx->nim_id); + + /* Read DMI options */ + if (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR, + sizeof(options), &options, NIM_READ) != 0) + return -1; + ctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT; + NT_LOG(DBG, ETHDEV, + "Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)\n", + ctx->instance, yes_no[ctx->avg_pwr]); + + qsfp_read_vendor_info(ctx); + NT_LOG(DBG, PMD, + "Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)\n", + ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no, + ctx->date, ctx->rev); + + if (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR, + sizeof(nim_len_info), nim_len_info, + NIM_READ) != 0) + return -1; + + /* + * Returns supported length information in meters for various fibers as 5 indivi- + * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper] + * If no length information is available for a certain entry, the returned value + * will be zero. This will be the case for SFP modules - EBW entry. + * If the MSBit is set the returned value in the lower 31 bits indicates that the + * supported length is greater than this. + */ + + nim_import_len_info(ctx, nim_len_info, nim_units); + + /* Read required power level */ + if (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER, + sizeof(value), &value, NIM_READ) != 0) + return -1; + + /* + * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43: + * If power class >= 5 setHighPower must be called for the module to be fully + * functional + */ + if ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) { + /* NIM in power class 1 - 4 */ + ctx->pwr_level_req = + (uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) + + 1); + } else { + /* NIM in power class 5 - 7 */ + ctx->pwr_level_req = + (uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4); + } + + return 0; +} + +/* + * If true the user must actively select the desired rate. If false the module + * however can still support several rates without the user is required to select + * one of them. Supported rates must then be deduced from the product number. + * SFF-8636, Rev 2.10a: + * p40: 6.2.7 Rate Select + * p85: A.2 Rate Select + */ +static bool qsfp28_is_speed_selection_enabled(nim_i2c_ctx_p ctx) +{ + const uint8_t options_reg_addr = 195; + const uint8_t enh_options_reg_addr = 221; + + uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & + 0x01; /* bit: 5 */ + + if (rate_select_ena == 0) + return false; + + uint8_t rate_select_type = (read_byte(ctx, enh_options_reg_addr) >> 2) & + 0x03; /* bit 3..2 */ + + if (rate_select_type != 2) { + NT_LOG(DBG, NTHW, "NIM has unhandled rate select type (%d)", + rate_select_type); + return false; + } + + return true; /* When true selectRate() can be used */ +} + +/* + * Select a speed that is supported for a multi rate module. The possible speed + * values must be obtained by setSpeedMask(). + * Currently rate selection is assumed to be between 40Gb (10GBd) and 100G (25Gbd) + * The value in () are the baud rates for PAM-4 and are valid for extended rate + * select, version 2. + */ +static int qsfp28_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed) +{ + const uint8_t rx_rate_sel_addr = 87; + const uint8_t tx_rate_sel_addr = 88; + + if (ctx->lane_idx < 0) { + /* + * All lanes together + * The condition below indicates that the module supports rate selection + */ + if (ctx->speed_mask == (uint32_t)(NT_LINK_SPEED_40G | NT_LINK_SPEED_100G)) { + uint16_t data; + + if (speed == NT_LINK_SPEED_100G) { + data = 0xAAAA; + } else if (speed == NT_LINK_SPEED_40G) { + data = 0x0000; + } else { + NT_LOG(ERR, NTHW, "Unhandled NIM speed (%s).", + nt_translate_link_speed(speed)); + return -1; + } + + /* Set speed for Rx and Tx on all lanes */ + write_data_lin(ctx, rx_rate_sel_addr, sizeof(data), &data); + write_data_lin(ctx, tx_rate_sel_addr, sizeof(data), &data); + } else { + /* For ordinary modules only this speed is supported */ + if (speed != NT_LINK_SPEED_100G) { + NT_LOG(ERR, NTHW, + "NIM cannot select this speed (%s).", + nt_translate_link_speed(speed)); + return -1; + } + } + } else { + /* + * Individual lanes + * Currently we do not support QSFP28 modules that support rate selection when + * running on individual lanes but that might change in the future + */ + if (speed != NT_LINK_SPEED_25G) { + NT_LOG(ERR, NTHW, + "NIM cannot select this lane speed (%s).", + nt_translate_link_speed(speed)); + return -1; + } + } + return 0; +} + +int nim_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed) +{ + if (translate_nimid(ctx) == NT_NIM_SFP_SFP_PLUS) { + return nim_sfp_set_rate_select(ctx, speed); + } else if (translate_nimid(ctx) == NT_NIM_QSFP28) { + if (qsfp28_is_speed_selection_enabled(ctx)) + return qsfp28_set_link_speed(ctx, speed); + + return 0; /* NIM picks up the speed automatically */ + } + NT_LOG(ERR, ETHDEV, + "%s nim is not supported for adjustable link speed.", + nim_id_to_text(ctx->nim_id)); + return -1; +} + +/* + * Reads basic vendor and DMI information. + */ +static int sfp_read_basic_data(nim_i2c_ctx_p ctx) +{ + const char *yes_no[2] _unused = { "No", "Yes" }; + + check_content_valid(ctx, 0); + NT_LOG(DBG, PMD, "NIM id: %s (%d)", nim_id_to_text(ctx->nim_id), + ctx->nim_id); + + /* Read DMI options */ + uint8_t options; + + read_data_lin(ctx, SFP_DMI_OPTION_LIN_ADDR, sizeof(options), &options); + ctx->avg_pwr = options & SFP_DMI_AVG_PWR_BIT; + ctx->dmi_supp = options & SFP_DMI_IMPL_BIT; + ctx->specific_u.sfp.ext_cal = options & SFP_DMI_EXT_CAL_BIT; + ctx->specific_u.sfp.addr_chg = options & SFP_DMI_ADDR_CHG_BIT; + + if (ctx->dmi_supp) { + ctx->options |= + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } + + if (ctx->dmi_supp) { + NT_LOG(DBG, PMD, + "NIM options: (DMI: %s, AvgPwr: %s, ExtCal: %s, AddrChg: %s)", + yes_no[ctx->dmi_supp], yes_no[ctx->avg_pwr], + yes_no[ctx->specific_u.sfp.ext_cal], + yes_no[ctx->specific_u.sfp.addr_chg]); + } else { + NT_LOG(DBG, PMD, "NIM options: DMI not supported"); + } + /* Read enhanced options */ + read_data_lin(ctx, SFP_ENHANCED_OPTIONS_LIN_ADDR, sizeof(options), + &options); + ctx->tx_disable = options & SFP_SOFT_TX_DISABLE_IMPL_BIT; + + if (ctx->tx_disable) + ctx->options |= (1 << NIM_OPTION_TX_DISABLE); + + sfp_read_vendor_info(ctx); + + uint8_t nim_len_info[5]; + + read_data_lin(ctx, SFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info), + nim_len_info); + + /* + * Returns supported length information in meters for various fibers as 5 indivi- + * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper] + * If no length information is available for a certain entry, the returned value + * will be zero. This will be the case for SFP modules - EBW entry. + * If the MSBit is set the returned value in the lower 31 bits indicates that the + * supported length is greater than this. + */ + + uint16_t nim_units[5] = { 1000, 100, 10, 10, + 1 + }; /* SFP MSA units in meters */ + nim_import_len_info(ctx, &nim_len_info[0], &nim_units[0]); + + if (ctx->len_info[0] != 0 || ctx->len_info[1] != 0) { + /* + * Make sure that for SFP modules the supported length for SM fibers + * which is given in both km and 100m units is are equal to the greatest + * value. + * The following test will also be valid if NIM_LEN_MAX has been set! + */ + if (ctx->len_info[1] > ctx->len_info[0]) + ctx->len_info[0] = ctx->len_info[1]; + + ctx->len_info[1] = 0; /* EBW is not supported for SFP */ + } + + read_data_lin(ctx, SFP_OPTION0_LIN_ADDR, sizeof(options), &options); + + if (options & SFP_POWER_LEVEL2_REQ_BIT) + ctx->pwr_level_req = 2; + else + ctx->pwr_level_req = 1; + + ctx->pwr_level_cur = 1; + + if (ctx->pwr_level_req == 2) { + /* Read the current power level status */ + read_data_lin(ctx, SFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(options), + &options); + + if (options & SFP_POWER_LEVEL2_GET_BIT) + ctx->pwr_level_cur = 2; + else + ctx->pwr_level_cur = 1; + } + return 0; +} + +/* + * Read the vendor product number and from this determine which QSFP DMI options + * that are present. This list also covers QSFP28 modules. + * This function should be used if automatic detection does not work. + */ +static bool qsfpplus_get_qsfp_options_from_pn(nim_i2c_ctx_p ctx) +{ + if (strcmp(ctx->prod_no, "FTL410QE1C") == 0) { + /* FINISAR FTL410QE1C, QSFP+ */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_TX_BIAS) | (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "FTL410QE2C") == 0) { + /* FINISAR FTL410QE2C, QSFP+ */ + ctx->options = (1 << NIM_OPTION_TEMP) | + (1 << NIM_OPTION_SUPPLY); + } else if (strcmp(ctx->prod_no, "FTL4C1QE1C") == 0) { + /* FINISAR FTL4C1QE1C, QSFP+ */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "AFBR-79E4Z") == 0) { + /* + * AFBR-79E4Z: The digital diagnostic accuracy is not guaranteed so only + * the mandatory temperature sensor is made available (although it will + * also be inaccurate) + */ + /* AVAGO 79E4Z, QSFP+ */ + ctx->options = (1 << NIM_OPTION_TEMP); + } else if (strcmp(ctx->prod_no, "AFBR-79E4Z-D") == 0) { + /* AVAGO 79E4Z-D, QSFP+ */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "AFBR-79EQDZ") == 0) { + /* AVAGO 79EQDZ, QSFP+ */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "AFBR-79EBRZ") == 0) { + /* + * Avago RxOnly BiDi NIM + * No sensors available not even the normally mandatory temp sensor and this + * is ok since the temp sensor is not mandatory on active optical modules + */ + /* SFF-8436_rev4.1, p67 */ + ctx->options = (1 << NIM_OPTION_RX_ONLY); + } else if (strcmp(ctx->prod_no, "AFBR-79EBPZ-NU1") == 0) { + /* + * Avago RxTx BiDi NIM + * No sensors available not even the normally mandatory temp sensor and this + * is ok since the temp sensor is not mandatory on active optical modules + */ + ctx->options = 0; + } else if (strcmp(ctx->prod_no, "AFBR-79EBPZ") == 0) { + /* + * Avago RxTx BiDi NIM + * No sensors available not even the normally mandatory temp sensor and this + * is ok since the temp sensor is not mandatory on active optical modules + */ + ctx->options = 0; + } else if (strcmp(ctx->prod_no, "AFBR-89CDDZ") == 0) { + /* AVAGO 89CDDZ, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "AFBR-89BDDZ") == 0) { + /* AVAGO 89BDDZ, QSFP28, BiDi */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "AFBR-89BRDZ") == 0) { + /* + * AVAGO 89BRDZ, QSFP28, BiDi, RxOnly + * but sensors have been set as above except for Tx sensors + */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_RX_ONLY); + /* + * According to mail correspondence AFBR-89BRDZ is a RxOnly version of + * AFBR-89BDDZ with lasers default off. + * The lasers can be turned on however but should probably not because the + * receivers might be degraded, and this is the cause for selling them as RxOnly. + */ + } else if (strcmp(ctx->prod_no, "SQF1000L4LNGG01P") == 0) { + /* Sumitomo SQF1000L4LNGG01P, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "SQF1000L4LNGG01B") == 0) { + /* Sumitomo SQF1000L4LNGG01B, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "SQF1001L4LNGG01P") == 0) { + /* Sumitomo SQF1001L4LNGG01P, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "SQF1001L4LNGG01B") == 0) { + /* Sumitomo SQF1001L4LNGG01B, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "SQF1002L4LNGG01B") == 0) { + /* Sumitomo SQF1002L4LNGG01B, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "FIM37700/171") == 0) { + /* Fujitsu FIM37700/171, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "FIM37700/172") == 0) { + /* Fujitsu FIM37700/172, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "TR-FC85S-NVS") == 0) { + /* InnoLight TR-FC85S-NVS, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "TR-FC13L-NVS") == 0) { + /* InnoLight TR-FC13L-NVS, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "FTLC9551REPM") == 0) { + /* Finisar FTLC9551REPM, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else if (strcmp(ctx->prod_no, "FTLC9558REPM") == 0) { + /* Finisar FTLC9558REPM, QSFP28 */ + ctx->options = + (1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) | + (1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) | + (1 << NIM_OPTION_TX_POWER); + } else { + /* + * DO NOTE: The temperature sensor is not mandatory on active/passive copper + * and active optical modules + */ + ctx->options = (1 << NIM_OPTION_TEMP); + return false; + } + + return true; +} + +/* + * Try to figure out if a sensor is present by reading its value(s) and its limits. + * This is a highly impirical way that cannot be guaranteed to give the correct + * result but it was a wish not to be dependent on a PN table based solution. + */ +static void qsfpplus_find_qsfp_sensor_option(nim_i2c_ctx_p ctx, + uint16_t value_addr, + uint8_t lane_count, + uint16_t limit_addr, bool two_compl, + uint32_t sensor_option) +{ + uint8_t data[8]; + int i, j; + int value; + int value_list[4]; + int limit; + int limit_list[4]; + bool present; + + /* Read current value(s) */ + read_data_lin(ctx, value_addr, (uint16_t)(lane_count * 2), data); + + for (j = 0; j < lane_count; j++) { + value = 0; + + for (i = 0; i < 2; i++) { + value = value << 8; + value += data[2 * j + i]; + } + + if (two_compl && value >= 0x8000) + value = value - 0x10000; + + value_list[j] = value; + } + + /* Read limits Warning high/low Alarm high/low 4 values each two bytes */ + read_data_lin(ctx, limit_addr, 8, data); + + for (j = 0; j < 4; j++) { + limit = 0; + + for (i = 0; i < 2; i++) { + limit = limit << 8; + limit += data[2 * j + i]; + } + + if (two_compl && limit >= 0x8000) + limit = limit - 0x10000; + + limit_list[j] = limit; + } + + /* Find out if limits contradicts each other */ + int alarm_high = limit_list[0]; + int alarm_low = limit_list[1]; + int warn_high = limit_list[2]; + int warn_low = limit_list[3]; + + bool alarm_limits = false; /* Are they present - that is both not zero */ + bool warn_limits = false; + bool limit_conflict = false; + + if (alarm_high != 0 || alarm_low != 0) { + alarm_limits = true; + + if (alarm_high <= alarm_low) + limit_conflict = true; + } + + if (warn_high != 0 || warn_low != 0) { + warn_limits = true; + + /* Warning limits must be least restrictive */ + if (warn_high <= warn_low) + limit_conflict = true; + else if ((warn_high > alarm_high) || (warn_low < alarm_low)) + limit_conflict = true; + } + + /* Try to deduce if the sensor is present or not */ + present = false; + + if (limit_conflict) { + present = false; + } else if (warn_limits || + alarm_limits) { /* Is one or both present and not contradictory */ + present = true; + } else { + /* + * All limits are zero - look at the sensor value + * If one sensor is non-zero the sensor is set to be present + */ + for (j = 0; j < lane_count; j++) { + if (value_list[j] != 0) { + present = true; + break; + } + } + + /* + * If all limits and values are zero then present will be false here. In this + * case it is assumed that the sensor is not present: + * Experience indicates that for QSFP+ modules RxPwr will be non-zero even with + * no optical input. QSFP28 modules however can easily have RxPwr equal to zero + * with no optical input. + * For all investigated modules it was found that if RxPwr is implemented then + * the limits are also set. This is not always the case with TxBias and TxPwr + * but here the measured values will be non-zero when the laser is on what it + * will be just after initialization since it has no external hardware disable. + */ + } + + if (present) + ctx->options |= (1U << sensor_option); +} + +/* + * Find active QSFP sensors. + */ +static void qsfpplus_get_qsfp_options_from_data(nim_i2c_ctx_p ctx) +{ + ctx->options = 0; + + qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TEMP_LIN_ADDR, 1, + QSFP_TEMP_THRESH_LIN_ADDR, true, + NIM_OPTION_TEMP); + + qsfpplus_find_qsfp_sensor_option(ctx, QSFP_VOLT_LIN_ADDR, 1, + QSFP_VOLT_THRESH_LIN_ADDR, false, + NIM_OPTION_SUPPLY); + + qsfpplus_find_qsfp_sensor_option(ctx, QSFP_RX_PWR_LIN_ADDR, 4, + QSFP_RX_PWR_THRESH_LIN_ADDR, false, + NIM_OPTION_RX_POWER); + + qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_PWR_LIN_ADDR, 4, + QSFP_TX_PWR_THRESH_LIN_ADDR, false, + NIM_OPTION_TX_POWER); + + qsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_BIAS_LIN_ADDR, 4, + QSFP_BIAS_THRESH_LIN_ADDR, false, + NIM_OPTION_TX_BIAS); +} + +static void sfp_find_port_params(nim_i2c_ctx_p ctx) +{ + uint8_t data; + uint16_t bit_rate_nom; + uint8_t connector; + uint8_t gig_eth_comp; + uint8_t dmi_opt; + uint8_t fiber_chan_tx_tech; + unsigned int len_sm; + unsigned int len_mm_50um; + unsigned int len_mm_62_5um; + + ctx->specific_u.sfp.sfp28 = false; + + /* gigEthComp: */ + static const uint8_t eth_1000_b_t = 1 << 3; + static const uint8_t eth_1000_b_sx = 1 << 0; + static const uint8_t eth_1000_b_lx = 1 << 1; + + /* fiberChanTxTech: */ + static const uint8_t cu_passive = 1 << 2; + static const uint8_t cu_active = 1 << 3; + + /* dmiOpt: */ + static const uint8_t dd_present = 1 << 6; + + /* connector: */ + static const uint8_t cu_pig_tail = 0x21; + + ctx->port_type = NT_PORT_TYPE_SFP_NOT_RECOGNISED; + + read_data_lin(ctx, 12, sizeof(data), &data); + bit_rate_nom = (uint16_t)(data * 100); + + read_data_lin(ctx, 2, sizeof(connector), &connector); + read_data_lin(ctx, 6, sizeof(gig_eth_comp), &gig_eth_comp); + read_data_lin(ctx, 92, sizeof(dmi_opt), &dmi_opt); + read_data_lin(ctx, 8, sizeof(fiber_chan_tx_tech), &fiber_chan_tx_tech); + + read_data_lin(ctx, 15, sizeof(data), &data); + len_sm = (unsigned int)data * 100; /* Unit is 100m */ + + read_data_lin(ctx, 16, sizeof(data), &data); + len_mm_50um = (unsigned int)data * 10; /* Unit is 10m */ + + read_data_lin(ctx, 17, sizeof(data), &data); + len_mm_62_5um = (unsigned int)data * 10; /* Unit is 10m */ + + /* First find out if it is a SFP or a SFP+ NIM */ + if (bit_rate_nom == 0) { + /* + * A Nominal bit rate of zero indicates that it has not been defined and must + * be deduced from transceiver technology + */ + ctx->specific_u.sfp.sfpplus = !(gig_eth_comp & eth_1000_b_t); + } else if (bit_rate_nom == 25500) { + /* SFF-8024 - 4.4 Extended Specification Compliance References */ + read_data_lin(ctx, 36, sizeof(data), &data); + + if (data == 0x02) + ctx->port_type = NT_PORT_TYPE_SFP_28_SR; + else if (data == 0x03) + ctx->port_type = NT_PORT_TYPE_SFP_28_LR; + else if (data == 0x0B) + ctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_L; + else if (data == 0x0C) + ctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_S; + else if (data == 0x0D) + ctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_N; + else + ctx->port_type = NT_PORT_TYPE_SFP_28; + + ctx->specific_u.sfp.sfp28 = true; + ctx->specific_u.sfp.sfpplus = true; + + /* + * Whitelist of 25G transceivers known to also support 10G. + * There is no way to inquire about this capability. + */ + if ((strcmp(ctx->prod_no, "TR-PZ85S-N00") == 0) || + (strcmp(ctx->prod_no, "TR-PZ13L-N00") == 0) || + (strcmp(ctx->prod_no, "FTLF8536P4BCV") == 0) || + (strcmp(ctx->prod_no, "FTLF1436P4BCV") == 0)) { + ctx->specific_u.sfp.dual_rate = true; + + /* Change the port type for dual rate modules */ + if (ctx->port_type == NT_PORT_TYPE_SFP_28_SR) + ctx->port_type = NT_PORT_TYPE_SFP_28_SR_DR; + else if (ctx->port_type == NT_PORT_TYPE_SFP_28_LR) + ctx->port_type = NT_PORT_TYPE_SFP_28_LR_DR; + } + + return; + } + ctx->specific_u.sfp.sfpplus = (bit_rate_nom >= 10000); + /* Then find sub-types of each */ + if (ctx->specific_u.sfp.sfpplus) { + if (fiber_chan_tx_tech & cu_active) { + ctx->port_type = NT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC; + } else if (fiber_chan_tx_tech & cu_passive) { + if (connector == cu_pig_tail) + ctx->port_type = + NT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC; + else + ctx->port_type = NT_PORT_TYPE_SFP_PLUS_CU; + } else { + ctx->port_type = NT_PORT_TYPE_SFP_PLUS; + } + if (gig_eth_comp & (eth_1000_b_sx | eth_1000_b_lx)) { + ctx->port_type = NT_PORT_TYPE_SFP_PLUS_DUAL_RATE; + ctx->specific_u.sfp.dual_rate = true; + } + + read_data_lin(ctx, 65, sizeof(data), &data); + /* Test hard RATE_SELECT bit */ + ctx->specific_u.sfp.hw_rate_sel = ((data & (1 << 5)) != 0); + + read_data_lin(ctx, 93, sizeof(data), &data); + /* Test soft RATE_SELECT bit */ + ctx->specific_u.sfp.sw_rate_sel = ((data & (1 << 3)) != 0); + } else { /* SFP */ + /* 100M */ + if (bit_rate_nom != 0 && bit_rate_nom < 1000) { + ctx->port_type = NT_PORT_TYPE_SFP_FX; + /* 1G */ + } else { + ctx->specific_u.sfp.cu_type = false; + if (gig_eth_comp & eth_1000_b_sx) { + ctx->port_type = NT_PORT_TYPE_SFP_SX; + } else if (gig_eth_comp & eth_1000_b_lx) { + ctx->port_type = NT_PORT_TYPE_SFP_LX; + } else if (gig_eth_comp & eth_1000_b_t) { + ctx->specific_u.sfp.tri_speed = + sfp_is_supported_tri_speed_pn(ctx->prod_no); + + if (ctx->specific_u.sfp.tri_speed) { + ctx->port_type = + NT_PORT_TYPE_SFP_CU_TRI_SPEED; + } else { + ctx->port_type = NT_PORT_TYPE_SFP_CU; + } + ctx->specific_u.sfp.cu_type = true; + } else { + /* + * Not all modules report their ethernet compliance correctly so use + * length indicators + */ + if (len_sm > 0) + ctx->port_type = NT_PORT_TYPE_SFP_LX; + else if ((len_mm_50um > 0) || (len_mm_62_5um > 0)) + ctx->port_type = NT_PORT_TYPE_SFP_SX; + } + + /* Add Diagnostic Data suffix if necessary */ + if (dmi_opt & dd_present) { + if (ctx->port_type == NT_PORT_TYPE_SFP_SX) + ctx->port_type = NT_PORT_TYPE_SFP_SX_DD; + else if (ctx->port_type == NT_PORT_TYPE_SFP_LX) + ctx->port_type = NT_PORT_TYPE_SFP_LX_DD; + else if (ctx->port_type == NT_PORT_TYPE_SFP_CU) + ctx->port_type = NT_PORT_TYPE_SFP_CU_DD; + else if (ctx->port_type == + NT_PORT_TYPE_SFP_CU_TRI_SPEED) + ctx->port_type = + NT_PORT_TYPE_SFP_CU_TRI_SPEED_DD; + } + } + } +} + + +static void sfp_set_speed_mask(nim_i2c_ctx_p ctx) +{ + if (ctx->specific_u.sfp.sfp28) { + ctx->speed_mask = NT_LINK_SPEED_25G; /* Default for SFP28 */ + if (ctx->specific_u.sfp.dual_rate) + ctx->speed_mask |= NT_LINK_SPEED_10G; + } else if (ctx->specific_u.sfp.sfpplus) { + ctx->speed_mask = NT_LINK_SPEED_10G; /* Default for SFP+ */ + if (ctx->specific_u.sfp.dual_rate) + ctx->speed_mask |= NT_LINK_SPEED_1G; + if (ctx->port_type == NT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC) + ctx->speed_mask |= NT_LINK_SPEED_1G; + if (ctx->port_type == NT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC) + ctx->speed_mask |= NT_LINK_SPEED_1G; + } else { /* SFP */ + if (ctx->port_type == NT_PORT_TYPE_SFP_FX) { + ctx->speed_mask = NT_LINK_SPEED_100M; + } else { + ctx->speed_mask = NT_LINK_SPEED_1G; /* Default for SFP */ + if (ctx->specific_u.sfp.dual_rate || + ctx->specific_u.sfp.tri_speed) + ctx->speed_mask |= NT_LINK_SPEED_100M; + if (ctx->specific_u.sfp.tri_speed) + ctx->speed_mask |= NT_LINK_SPEED_10M; + } + } + if (ctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_L || + ctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_S || + ctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_N) { + /* Enable multiple speed setting for SFP28 DAC cables */ + ctx->speed_mask = (NT_LINK_SPEED_25G | NT_LINK_SPEED_10G | + NT_LINK_SPEED_1G); + } +} + +static void qsfp28_find_port_params(nim_i2c_ctx_p ctx) +{ + uint8_t fiber_chan_speed; + + /* Table 6-17 SFF-8636 */ + read_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed); + + if (fiber_chan_speed & (1 << 7)) { + /* SFF-8024, Rev 4.7, Table 4-4 */ + uint8_t extended_specification_compliance_code = 0; + + read_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1, + &extended_specification_compliance_code); + + switch (extended_specification_compliance_code) { + case 0x02: + ctx->port_type = NT_PORT_TYPE_QSFP28_SR4; + break; + case 0x03: + ctx->port_type = NT_PORT_TYPE_QSFP28_LR4; + break; + case 0x0B: + ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L; + break; + case 0x0C: + ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S; + break; + case 0x0D: + ctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N; + break; + case 0x25: + ctx->port_type = NT_PORT_TYPE_QSFP28_DR; + break; + case 0x26: + ctx->port_type = NT_PORT_TYPE_QSFP28_FR; + break; + case 0x27: + ctx->port_type = NT_PORT_TYPE_QSFP28_LR; + break; + default: + ctx->port_type = NT_PORT_TYPE_QSFP28; + } + } else { + ctx->port_type = NT_PORT_TYPE_QSFP28; + } +} + +/* + * If true the user must actively select the desired rate. If false the module + * however can still support several rates without the user is required to select + * one of them. Supported rates must then be deduced from the product number. + * SFF-8636, Rev 2.10a: + * p40: 6.2.7 Rate Select + * p85: A.2 Rate Select + */ +static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx) +{ + const uint8_t ext_rate_select_compl_reg_addr = 141; + const uint8_t options_reg_addr = 195; + const uint8_t enh_options_reg_addr = 221; + + uint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) & + 0x01; /* bit: 5 */ + + if (rate_select_ena == 0) + return false; + + uint8_t rate_select_type = (read_byte(ctx, enh_options_reg_addr) >> 2) & + 0x03; /* bit 3..2 */ + + if (rate_select_type != 2) { + NT_LOG(DBG, PMD, "NIM has unhandled rate select type (%d)", + rate_select_type); + return false; + } + + uint8_t ext_rate_select_ver = read_byte(ctx, ext_rate_select_compl_reg_addr) & + 0x03; /* bit 1..0 */ + + if (ext_rate_select_ver != 0x02) { + NT_LOG(DBG, PMD, + "NIM has unhandled extended rate select version (%d)", + ext_rate_select_ver); + return false; + } + + return true; /* When true selectRate() can be used */ +} + +static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx) +{ + if (ctx->port_type == NT_PORT_TYPE_QSFP28_FR || + ctx->port_type == NT_PORT_TYPE_QSFP28_DR || + ctx->port_type == NT_PORT_TYPE_QSFP28_LR) { + if (ctx->lane_idx < 0) + ctx->speed_mask = NT_LINK_SPEED_100G; + else + ctx->speed_mask = + 0; /* PAM-4 modules can only run on all lanes together */ + } else { + if (ctx->lane_idx < 0) + ctx->speed_mask = NT_LINK_SPEED_100G; + else + ctx->speed_mask = NT_LINK_SPEED_25G; + + if (qsfp28_is_rate_selection_enabled(ctx)) { + /* + * It is assumed that if the module supports dual rates then the other rate + * is 10G per lane or 40G for all lanes. + */ + if (ctx->lane_idx < 0) + ctx->speed_mask |= NT_LINK_SPEED_40G; + else + ctx->speed_mask = NT_LINK_SPEED_10G; + } + } +} + +static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx) +{ + uint8_t device_tech; + + read_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech), + &device_tech); + + switch (device_tech & 0xF0) { + case 0xA0: /* Copper cable unequalized */ + case 0xB0: /* Copper cable passive equalized */ + ctx->port_type = NT_PORT_TYPE_QSFP_PASSIVE_DAC; + break; + case 0xC0: /* Copper cable, near and far end limiting active equalizers */ + case 0xD0: /* Copper cable, far end limiting active equalizers */ + case 0xE0: /* Copper cable, near end limiting active equalizers */ + case 0xF0: /* Copper cable, linear active equalizers */ + ctx->port_type = NT_PORT_TYPE_QSFP_ACTIVE_DAC; + break; + default: /* Optical */ + ctx->port_type = NT_PORT_TYPE_QSFP_PLUS; + break; + } +} + +static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx) +{ + ctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G : + (NT_LINK_SPEED_10G); +} + +static int sfp_preinit(nim_i2c_ctx_p ctx) +{ + int res = sfp_read_basic_data(ctx); + + if (!res) { + sfp_find_port_params(ctx); + sfp_set_speed_mask(ctx); + } + return res; +} + +static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx) +{ + assert(lane_idx < 4); + ctx->specific_u.qsfp.qsfp28 = false; + ctx->lane_idx = lane_idx; + ctx->lane_count = 4; +} + +static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx) +{ + qsfpplus_construct(ctx, lane_idx); + int res = qsfpplus_read_basic_data(ctx); + + if (!res) { + qsfpplus_find_port_params(ctx); + /* + * If not on the known modules list try to figure out which sensors that are present + */ + if (!qsfpplus_get_qsfp_options_from_pn(ctx)) { + NT_LOG(DBG, NTHW, + "NIM options not known in advance - trying to detect"); + qsfpplus_get_qsfp_options_from_data(ctx); + } + + /* + * Read if TX_DISABLE has been implemented + * For passive optical modules this is required while it for copper and active + * optical modules is optional. Under all circumstances register 195.4 will + * indicate, if TX_DISABLE has been implemented in register 86.0-3 + */ + uint8_t value; + + read_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value); + + ctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0; + + if (ctx->tx_disable) + ctx->options |= (1 << NIM_OPTION_TX_DISABLE); + + /* + * Previously - considering AFBR-89BRDZ - code tried to establish if a module was + * RxOnly by testing the state of the lasers after reset. Lasers were for this + * module default disabled. + * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was + * decided that this option should not be detected automatically but from PN + */ + ctx->specific_u.qsfp.rx_only = + (ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0; + qsfpplus_set_speed_mask(ctx); + } + return res; +} + +static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx) +{ + uint8_t data; + bool init_complete_flag_present = false; + + /* + * Revision compliance + * 7: SFF-8636 Rev 2.5, 2.6 and 2.7 + * 8: SFF-8636 Rev 2.8, 2.9 and 2.10 + */ + read_data_lin(ctx, 1, + sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance), + &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); + NT_LOG(DBG, NTHW, "NIM RevCompliance = %d", + ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance); + + /* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */ + if (ctx->lane_idx > 0) + return; + + if (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) { + /* Check if init complete flag is implemented */ + read_data_lin(ctx, 221, sizeof(data), &data); + init_complete_flag_present = (data & (1 << 4)) != 0; + } + + NT_LOG(DBG, NTHW, "NIM InitCompleteFlagPresent = %d", + init_complete_flag_present); + + /* + * If the init complete flag is not present then wait 500ms that together with 500ms + * after reset (in the adapter code) should be enough to read data from upper pages + * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been + * prone to this when trying to read sensor options using getQsfpOptionsFromData() + * Probably because access to the paged address space is required. + */ + if (!init_complete_flag_present) { + NT_OS_WAIT_USEC(500000); + return; + } + + /* Otherwise wait for the init complete flag to be set */ + int count = 0; + + while (true) { + if (count > 10) { /* 1 s timeout */ + NT_LOG(WRN, NTHW, "Timeout waiting for module ready"); + break; + } + + read_data_lin(ctx, 6, sizeof(data), &data); + + if (data & 0x01) { + NT_LOG(DBG, NTHW, "Module ready after %dms", + count * 100); + break; + } + + NT_OS_WAIT_USEC(100000); /* 100 ms */ + count++; + } +} + +static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx) +{ + const char *const nim_list[] = { + "AFBR-89BDDZ", /* Avago BiDi */ + "AFBR-89BRDZ", /* Avago BiDi, RxOnly */ + "FTLC4352RKPL", /* Finisar QSFP28-LR */ + "FTLC4352RHPL", /* Finisar QSFP28-DR */ + "FTLC4352RJPL", /* Finisar QSFP28-FR */ + "SFBR-89BDDZ-CS4", /* Foxconn, QSFP28 100G/40G BiDi */ + }; + + for (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) { + if (ctx->prod_no == nim_list[i]) { + ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); + ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena = + true; + NT_LOG(DBG, NTHW, "Found FEC info via PN list"); + return; + } + } + + /* + * For modules not in the list find FEC info via registers + * Read if the module has controllable FEC + * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing) + * (Page 03h, Bytes 224-229) + */ + uint8_t data; + uint16_t addr = 227 + 3 * 128; + + read_data_lin(ctx, addr, sizeof(data), &data); + + /* Check if the module has FEC support that can be controlled */ + ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl = + (data & (1 << 6)) != 0; + ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl = + (data & (1 << 7)) != 0; + + if (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl) + ctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC); + + if (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl) + ctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC); +} + +static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx) +{ + int res = qsfpplus_preinit(ctx, lane_idx); + + if (!res) { + qsfp28_wait_for_ready_after_reset(ctx); + memset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0, + sizeof(ctx->specific_u.qsfp.specific_u.qsfp28)); + ctx->specific_u.qsfp.qsfp28 = true; + qsfp28_find_port_params(ctx); + qsfp28_get_fec_options(ctx); + qsfp28_set_speed_mask(ctx); + } + return res; +} + +static void sfp_nim_add_all_sensors(uint8_t m_port_no, nim_i2c_ctx_t *ctx, + struct nim_sensor_group **nim_sensors_ptr, + uint16_t *nim_sensors_cnt) +{ + struct nim_sensor_group *sensor = NULL; + *nim_sensors_cnt = 0; + + if (ctx == NULL || nim_sensors_ptr == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + /* + * If the user has not provided a name for the temperature sensor then apply + * one automatically + */ + if (strlen(sfp_sensors_level0[0].name) == 0) { + if (ctx->specific_u.sfp.sfp28) { + rte_strscpy(sfp_sensors_level0[0].name, "SFP28", + sizeof(sfp_sensors_level0[0].name)); + } else if (ctx->specific_u.sfp.sfpplus) { + rte_strscpy(sfp_sensors_level0[0].name, "SFP+", + sizeof(sfp_sensors_level0[0].name)); + } else { + rte_strscpy(sfp_sensors_level0[0].name, "SFP", + sizeof(sfp_sensors_level0[0].name)); + } + } + + /* allocate temperature sensor */ + nim_sensors_ptr[m_port_no] = allocate_nim_sensor_group(m_port_no, + ctx, + NT_SENSOR_SOURCE_PORT, + &sfp_sensors_level0[0]); + sensor = nim_sensors_ptr[m_port_no]; + sensor->read = &nim_read_sfp_temp; + (*nim_sensors_cnt)++; + + /* voltage */ + sensor->next = allocate_nim_sensor_group(m_port_no, + ctx, + NT_SENSOR_SOURCE_PORT, + &sfp_sensors_level1[0]); + sensor = sensor->next; + sensor->read = &nim_read_sfp_voltage; + (*nim_sensors_cnt)++; + + /* bias current */ + sensor->next = allocate_nim_sensor_group(m_port_no, + ctx, + NT_SENSOR_SOURCE_PORT, + &sfp_sensors_level1[1]); + sensor = sensor->next; + sensor->read = &nim_read_sfp_bias_current; + (*nim_sensors_cnt)++; + + /* tx power */ + sensor->next = allocate_nim_sensor_group(m_port_no, + ctx, + NT_SENSOR_SOURCE_PORT, + &sfp_sensors_level1[2]); + sensor = sensor->next; + sensor->read = &nim_read_sfp_tx_power; + (*nim_sensors_cnt)++; + + /* rx power */ + sensor->next = allocate_nim_sensor_group(m_port_no, + ctx, + NT_SENSOR_SOURCE_PORT, + &sfp_sensors_level1[3]); + sensor = sensor->next; + sensor->read = &nim_read_sfp_rx_power; + (*nim_sensors_cnt)++; +} + +static void +qsfp_plus_nim_add_all_sensors(uint8_t m_port_no, nim_i2c_ctx_t *ctx, + struct nim_sensor_group **nim_sensors_ptr, + uint16_t *nim_sensors_cnt) +{ + struct nim_sensor_group *sensor = NULL; + + if (ctx == NULL || nim_sensors_ptr == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + /* + * If the user has not provided a name for the temperature sensor then apply + * one automatically + */ + if (strlen(qsfp_sensor_level0[0].name) == 0) { + if (ctx->specific_u.qsfp.qsfp28) + rte_strscpy(qsfp_sensor_level0[0].name, "QSFP28", + sizeof(qsfp_sensor_level0[0].name)); + else + rte_strscpy(qsfp_sensor_level0[0].name, "QSFP+", + sizeof(qsfp_sensor_level0[0].name)); + } + + /* temperature sensor */ + nim_sensors_ptr[m_port_no] = allocate_nim_sensor_group(m_port_no, ctx, + NT_SENSOR_SOURCE_PORT, + &qsfp_sensor_level0[0]); + sensor = nim_sensors_ptr[m_port_no]; + sensor->read = &nim_read_qsfp_temp; + (*nim_sensors_cnt)++; + + /* voltage */ + sensor->next = allocate_nim_sensor_group(m_port_no, ctx, + NT_SENSOR_SOURCE_LEVEL1_PORT, + &qsfp_sensor_level1[0]); + sensor = sensor->next; + sensor->read = &nim_read_qsfp_voltage; + (*nim_sensors_cnt)++; + + /* bias current sensors */ + for (uint8_t i = 1; i < 5; i++) { + sensor->next = allocate_nim_sensor_group(m_port_no, ctx, + NT_SENSOR_SOURCE_LEVEL1_PORT, + &qsfp_sensor_level1[i]); + sensor = sensor->next; + sensor->read = &nim_read_qsfp_bias_current; + (*nim_sensors_cnt)++; + } + + /* tx power */ + for (uint8_t i = 5; i < 9; i++) { + sensor->next = allocate_nim_sensor_group(m_port_no, ctx, + NT_SENSOR_SOURCE_LEVEL1_PORT, + &qsfp_sensor_level1[i]); + sensor = sensor->next; + sensor->read = &nim_read_qsfp_tx_power; + (*nim_sensors_cnt)++; + } + + /* rx power */ + for (uint8_t i = 9; i < 13; i++) { + sensor->next = allocate_nim_sensor_group(m_port_no, ctx, + NT_SENSOR_SOURCE_LEVEL1_PORT, + &qsfp_sensor_level1[i]); + sensor = sensor->next; + sensor->read = &nim_read_qsfp_rx_power; + (*nim_sensors_cnt)++; + } +} + +struct nim_sensor_group * +allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx, + enum nt_sensor_source_e ssrc, + struct nt_adapter_sensor_description *sd) +{ + struct nim_sensor_group *sg = malloc(sizeof(struct nim_sensor_group)); + + if (sg == NULL) { + NT_LOG(ERR, ETHDEV, "%s: sensor group is NULL", __func__); + return NULL; + } + sg->sensor = allocate_sensor_by_description(port, ssrc, sd); + sg->ctx = ctx; + sg->next = NULL; + return sg; +} + +int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port, + struct nim_sensor_group **nim_sensors_ptr, + uint16_t *nim_sensors_cnt) +{ + int res = i2c_nim_common_construct(ctx); + + switch (translate_nimid(ctx)) { + case NT_NIM_SFP_SFP_PLUS: + sfp_preinit(ctx); + sfp_nim_add_all_sensors(port, ctx, nim_sensors_ptr, + nim_sensors_cnt); + break; + case NT_NIM_QSFP_PLUS: + qsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1); + qsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr, + nim_sensors_cnt); + break; + case NT_NIM_QSFP28: + qsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1); + qsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr, + nim_sensors_cnt); + break; + default: + res = 1; + NT_LOG(ERR, NTHW, "NIM type %s is not supported.\n", + nim_id_to_text(ctx->nim_id)); + } + + return res; +} diff --git a/drivers/net/ntnic/nim/i2c_nim.h b/drivers/net/ntnic/nim/i2c_nim.h new file mode 100644 index 0000000000..f664e6b7ee --- /dev/null +++ b/drivers/net/ntnic/nim/i2c_nim.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef I2C_NIM_H_ +#define I2C_NIM_H_ + +#include "nthw_drv.h" +#include "nim_defines.h" +#include "nt_link_speed.h" + +#include "sensors.h" + +typedef struct sfp_nim_state { + uint8_t br; /* bit rate, units of 100 MBits/sec */ +} sfp_nim_state_t, *sfp_nim_state_p; + +typedef struct nim_i2c_ctx { + nthw_iic_t hwiic; /* depends on *Fpga_t, instance number, and cycle time */ + uint8_t instance; + uint8_t devaddr; + uint8_t regaddr; + uint8_t nim_id; + nt_port_type_t port_type; + + char vendor_name[17]; + char prod_no[17]; + char serial_no[17]; + char date[9]; + char rev[5]; + bool avg_pwr; + bool content_valid; + uint8_t pwr_level_req; + uint8_t pwr_level_cur; + uint16_t len_info[5]; + uint32_t speed_mask; /* Speeds supported by the NIM */ + int8_t lane_idx; /* Is this associated with a single lane or all lanes (-1) */ + uint8_t lane_count; + uint32_t options; + bool tx_disable; + bool dmi_supp; + + union { + struct { + bool sfp28; + bool sfpplus; + bool dual_rate; + bool hw_rate_sel; + bool sw_rate_sel; + bool cu_type; + bool tri_speed; + bool ext_cal; + bool addr_chg; + } sfp; + + struct { + bool rx_only; + bool qsfp28; + union { + struct { + uint8_t rev_compliance; + bool media_side_fec_ctrl; + bool host_side_fec_ctrl; + bool media_side_fec_ena; + bool host_side_fec_ena; + } qsfp28; + } specific_u; + } qsfp; + + } specific_u; +} nim_i2c_ctx_t, *nim_i2c_ctx_p; + +struct nim_sensor_group { + struct nt_adapter_sensor *sensor; + void (*read)(struct nim_sensor_group *sg, nthw_spis_t *t_spi); + struct nim_i2c_ctx *ctx; + struct nim_sensor_group *next; +}; + +struct nim_sensor_group * +allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx, + enum nt_sensor_source_e ssrc, + struct nt_adapter_sensor_description *sd); + +/* + * Utility functions + */ + +nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx); + +/* + * Builds an nim state for the port implied by `ctx`, returns zero + * if successful, and non-zero otherwise. SFP and QSFP nims are supported + */ +int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state); + +/* + * Returns a type name such as "SFP/SFP+" for a given NIM type identifier, + * or the string "ILLEGAL!". + */ +const char *nim_id_to_text(uint8_t nim_id); + +int nim_sfp_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable); + +int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_t *ctx, bool disable, + int lane_idx); + +int nim_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed); + +/* + * This function tries to classify NIM based on it's ID and some register reads + * and collects information into ctx structure. The @extra parameter could contain + * the initialization argument for specific type of NIMS. + */ +int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port, + struct nim_sensor_group **nim_sensors_ptr, + uint16_t *nim_sensors_cnt); + +int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length, + void *data); + +#endif /* I2C_NIM_H_ */ diff --git a/drivers/net/ntnic/nim/nim_defines.h b/drivers/net/ntnic/nim/nim_defines.h new file mode 100644 index 0000000000..da3567d073 --- /dev/null +++ b/drivers/net/ntnic/nim/nim_defines.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NIM_DEFINES_H_ +#define NIM_DEFINES_H_ + +#define NIM_IDENTIFIER_ADDR 0 /* 1 byte */ + +#define SFP_BIT_RATE_ADDR 12 /* 1 byte */ +#define SFP_VENDOR_NAME_ADDR 20 /* 16bytes */ +#define SFP_VENDOR_PN_ADDR 40 /* 16bytes */ +#define SFP_VENDOR_REV_ADDR 56 /* 4bytes */ +#define SFP_VENDOR_SN_ADDR 68 /* 16bytes */ +#define SFP_VENDOR_DATE_ADDR 84 /* 8bytes */ + +#define SFP_CONTROL_STATUS_LIN_ADDR (110U + 256U) /* 0xA2 */ +#define SFP_SOFT_TX_DISABLE_BIT (1U << 6) + +#define QSFP_EXTENDED_IDENTIFIER 129 +#define QSFP_SUP_LEN_INFO_ADDR 142 /* 5bytes */ +#define QSFP_TRANSMITTER_TYPE_ADDR 147 /* 1byte */ +#define QSFP_VENDOR_NAME_ADDR 148 /* 16bytes */ +#define QSFP_VENDOR_PN_ADDR 168 /* 16bytes */ +#define QSFP_VENDOR_REV_ADDR 184 /* 2bytes */ +#define QSFP_VENDOR_SN_ADDR 196 /* 16bytes */ +#define QSFP_VENDOR_DATE_ADDR 212 /* 8bytes */ + +/* I2C addresses */ +#define nim_i2c_0xa0 0xA0 /* Basic I2C address */ +#define nim_i2c_0xa2 0xA2 /* Diagnostic monitoring */ +#define nim_i2c_0xac 0xAC /* Address of integrated PHY */ + +typedef enum { + NIM_OPTION_TEMP = 0, + NIM_OPTION_SUPPLY, + NIM_OPTION_RX_POWER, + NIM_OPTION_TX_BIAS, + NIM_OPTION_TX_POWER, + NIM_OPTION_TX_DISABLE, + /* Indicates that the module should be checked for the two next FEC types */ + NIM_OPTION_FEC, + NIM_OPTION_MEDIA_SIDE_FEC, + NIM_OPTION_HOST_SIDE_FEC, + NIM_OPTION_RX_ONLY +} nim_option_t; + +enum nt_nim_identifier_e { + NT_NIM_UNKNOWN = 0x00, /* Nim type is unknown */ + NT_NIM_GBIC = 0x01, /* Nim type = GBIC */ + NT_NIM_FIXED = 0x02, /* Nim type = FIXED */ + NT_NIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */ + NT_NIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */ + NT_NIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */ + NT_NIM_XFP = 0x06, /* Nim type = XFP */ + NT_NIM_XFF = 0x07, /* Nim type = XFF */ + NT_NIM_XFP_E = 0x08, /* Nim type = XFP-E */ + NT_NIM_XPAK = 0x09, /* Nim type = XPAK */ + NT_NIM_X2 = 0x0A, /* Nim type = X2 */ + NT_NIM_DWDM = 0x0B, /* Nim type = DWDM */ + NT_NIM_QSFP = 0x0C, /* Nim type = QSFP */ + NT_NIM_QSFP_PLUS = 0x0D, /* Nim type = QSFP+ */ + NT_NIM_QSFP28 = 0x11, /* Nim type = QSFP28 */ + NT_NIM_CFP4 = 0x12, /* Nim type = CFP4 */ +}; + +typedef enum nt_nim_identifier_e nt_nim_identifier_t; + +/* + * Port types + * The use of all non-generic XX_NOT_PRESENT is deprecated - use + * NT_PORT_TYPE_NIM_NOT_PRESENT instead + */ +enum nt_port_type_e { + NT_PORT_TYPE_NOT_AVAILABLE = + 0, /* The NIM/port type is not available (unknown) */ + NT_PORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */ + NT_PORT_TYPE_RJ45, /* RJ45 type */ + NT_PORT_TYPE_SFP_NOT_PRESENT, /* SFP type but slot is empty */ + NT_PORT_TYPE_SFP_SX, /* SFP SX */ + NT_PORT_TYPE_SFP_SX_DD, /* SFP SX digital diagnostic */ + NT_PORT_TYPE_SFP_LX, /* SFP LX */ + NT_PORT_TYPE_SFP_LX_DD, /* SFP LX digital diagnostic */ + NT_PORT_TYPE_SFP_ZX, /* SFP ZX */ + NT_PORT_TYPE_SFP_ZX_DD, /* SFP ZX digital diagnostic */ + NT_PORT_TYPE_SFP_CU, /* SFP copper */ + NT_PORT_TYPE_SFP_CU_DD, /* SFP copper digital diagnostic */ + NT_PORT_TYPE_SFP_NOT_RECOGNISED, /* SFP unknown */ + NT_PORT_TYPE_XFP, /* XFP */ + NT_PORT_TYPE_XPAK, /* XPAK */ + NT_PORT_TYPE_SFP_CU_TRI_SPEED, /* SFP copper tri-speed */ + NT_PORT_TYPE_SFP_CU_TRI_SPEED_DD, /* SFP copper tri-speed digital diagnostic */ + NT_PORT_TYPE_SFP_PLUS, /* SFP+ type */ + NT_PORT_TYPE_SFP_PLUS_NOT_PRESENT, /* SFP+ type but slot is empty */ + NT_PORT_TYPE_XFP_NOT_PRESENT, /* XFP type but slot is empty */ + NT_PORT_TYPE_QSFP_PLUS_NOT_PRESENT, /* QSFP type but slot is empty */ + NT_PORT_TYPE_QSFP_PLUS, /* QSFP type */ + NT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC, /* SFP+ Passive DAC */ + NT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC, /* SFP+ Active DAC */ + NT_PORT_TYPE_CFP4, /* CFP4 type */ + NT_PORT_TYPE_CFP4_LR4 = NT_PORT_TYPE_CFP4, /* CFP4 100G, LR4 type */ + NT_PORT_TYPE_CFP4_NOT_PRESENT, /* CFP4 type but slot is empty */ + NT_PORT_TYPE_INITIALIZE, /* The port type is not fully established yet */ + NT_PORT_TYPE_NIM_NOT_PRESENT, /* Generic "Not present" */ + NT_PORT_TYPE_HCB, /* Test mode: Host Compliance Board */ + NT_PORT_TYPE_NOT_SUPPORTED, /* The NIM type is not supported in this context */ + NT_PORT_TYPE_SFP_PLUS_DUAL_RATE, /* SFP+ supports 1G/10G */ + NT_PORT_TYPE_CFP4_SR4, /* CFP4 100G, SR4 type */ + NT_PORT_TYPE_QSFP28_NOT_PRESENT, /* QSFP28 type but slot is empty */ + NT_PORT_TYPE_QSFP28, /* QSFP28 type */ + NT_PORT_TYPE_QSFP28_SR4, /* QSFP28-SR4 type */ + NT_PORT_TYPE_QSFP28_LR4, /* QSFP28-LR4 type */ + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + NT_PORT_TYPE_QSFP_PLUS_4X10, + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + NT_PORT_TYPE_QSFP_PASSIVE_DAC_4X10, + NT_PORT_TYPE_QSFP_PASSIVE_DAC = + NT_PORT_TYPE_QSFP_PASSIVE_DAC_4X10, /* QSFP passive DAC type */ + /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */ + NT_PORT_TYPE_QSFP_ACTIVE_DAC_4X10, + NT_PORT_TYPE_QSFP_ACTIVE_DAC = + NT_PORT_TYPE_QSFP_ACTIVE_DAC_4X10, /* QSFP active DAC type */ + NT_PORT_TYPE_SFP_28, /* SFP28 type */ + NT_PORT_TYPE_SFP_28_SR, /* SFP28-SR type */ + NT_PORT_TYPE_SFP_28_LR, /* SFP28-LR type */ + NT_PORT_TYPE_SFP_28_CR_CA_L, /* SFP28-CR-CA-L type */ + NT_PORT_TYPE_SFP_28_CR_CA_S, /* SFP28-CR-CA-S type */ + NT_PORT_TYPE_SFP_28_CR_CA_N, /* SFP28-CR-CA-N type */ + NT_PORT_TYPE_QSFP28_CR_CA_L, /* QSFP28-CR-CA-L type */ + NT_PORT_TYPE_QSFP28_CR_CA_S, /* QSFP28-CR-CA-S type */ + NT_PORT_TYPE_QSFP28_CR_CA_N, /* QSFP28-CR-CA-N type */ + NT_PORT_TYPE_SFP_28_SR_DR, /* SFP28-SR-DR type */ + NT_PORT_TYPE_SFP_28_LR_DR, /* SFP28-LR-DR type */ + NT_PORT_TYPE_SFP_FX, /* SFP FX */ + NT_PORT_TYPE_SFP_PLUS_CU, /* SFP+ CU type */ + /* QSFP28-FR type. Uses PAM4 modulation on one lane only */ + NT_PORT_TYPE_QSFP28_FR, + /* QSFP28-DR type. Uses PAM4 modulation on one lane only */ + NT_PORT_TYPE_QSFP28_DR, + /* QSFP28-LR type. Uses PAM4 modulation on one lane only */ + NT_PORT_TYPE_QSFP28_LR, +}; + +typedef enum nt_port_type_e nt_port_type_t, *nt_port_type_p; + +#endif /* NIM_DEFINES_H_ */ diff --git a/drivers/net/ntnic/nim/nt_link_speed.c b/drivers/net/ntnic/nim/nt_link_speed.c new file mode 100644 index 0000000000..35c75f5e56 --- /dev/null +++ b/drivers/net/ntnic/nim/nt_link_speed.c @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include +#include + +#include "nt_link_speed.h" + +const char *nt_translate_link_speed(nt_link_speed_t link_speed) +{ + switch (link_speed) { + case NT_LINK_SPEED_UNKNOWN: + return "NotAvail"; + case NT_LINK_SPEED_10M: + return "10M"; + case NT_LINK_SPEED_100M: + return "100M"; + case NT_LINK_SPEED_1G: + return "1G"; + case NT_LINK_SPEED_10G: + return "10G"; + case NT_LINK_SPEED_25G: + return "25G"; + case NT_LINK_SPEED_40G: + return "40G"; + case NT_LINK_SPEED_50G: + return "50G"; + case NT_LINK_SPEED_100G: + return "100G"; + default: + /* DEBUG assert: remind developer that a switch/case entry is needed here.... */ + assert(false); + return "Unhandled"; + } +} + +uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed) +{ + uint64_t n_link_speed = 0ULL; + + switch (e_link_speed) { + case NT_LINK_SPEED_UNKNOWN: + n_link_speed = 0UL; + break; + case NT_LINK_SPEED_10M: + n_link_speed = (10ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_100M: + n_link_speed = (100ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_1G: + n_link_speed = (1ULL * 1000ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_10G: + n_link_speed = (10ULL * 1000ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_25G: + n_link_speed = (25ULL * 1000ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_40G: + n_link_speed = (40ULL * 1000ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_50G: + n_link_speed = (50ULL * 1000ULL * 1000ULL * 1000ULL); + break; + case NT_LINK_SPEED_100G: + n_link_speed = (100ULL * 1000ULL * 1000ULL * 1000ULL); + break; + default: + /* DEBUG assert: remind developer that a switch/case entry is needed here.... */ + assert(false); + n_link_speed = 0UL; + break; + } + return n_link_speed; +} + +const char *nt_translate_link_speed_mask(uint32_t link_speed_mask, char *buffer, + uint32_t length) +{ + size_t len = 0; + + buffer[0] = 0; + + for (int i = 0; i < 32; i++) { + if ((1U << i) & link_speed_mask) { + len = strlen(buffer); + + if (len > 0) { + if ((length - len - 1) > 2) { + strncat(buffer, ", ", length); + len = strlen(buffer); + } + } + + if (len < (length - 1)) + strncat(buffer, nt_translate_link_speed(1 << i), + length); + } + } + + return buffer; +} diff --git a/drivers/net/ntnic/nim/nt_link_speed.h b/drivers/net/ntnic/nim/nt_link_speed.h new file mode 100644 index 0000000000..969e3fb867 --- /dev/null +++ b/drivers/net/ntnic/nim/nt_link_speed.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NT_LINK_SPEED_H_ +#define NT_LINK_SPEED_H_ + +#include + +/* + * Link speed. + * Note this is a bitmask. + */ +enum nt_link_speed_e { + NT_LINK_SPEED_UNKNOWN = 0, + NT_LINK_SPEED_10M = 0x01, /* 10 Mbps */ + NT_LINK_SPEED_100M = 0x02, /* 100 Mbps */ + NT_LINK_SPEED_1G = 0x04, /* 1 Gbps (Autoneg only) */ + NT_LINK_SPEED_10G = 0x08, /* 10 Gbps (Autoneg only) */ + NT_LINK_SPEED_40G = 0x10, /* 40 Gbps (Autoneg only) */ + NT_LINK_SPEED_100G = 0x20, /* 100 Gbps (Autoneg only) */ + NT_LINK_SPEED_50G = 0x40, /* 50 Gbps (Autoneg only) */ + NT_LINK_SPEED_25G = 0x80, /* 25 Gbps (Autoneg only) */ + NT_LINK_SPEED_END /* always keep this entry as the last in enum */ +}; + +typedef enum nt_link_speed_e nt_link_speed_t; + +const char *nt_translate_link_speed(nt_link_speed_t link_speed); +const char *nt_translate_link_speed_mask(uint32_t link_speed_mask, char *buffer, + uint32_t length); +uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed); + +#endif /* NT_LINK_SPEED_H_ */ diff --git a/drivers/net/ntnic/nim/qsfp_registers.h b/drivers/net/ntnic/nim/qsfp_registers.h new file mode 100644 index 0000000000..366dcbf06e --- /dev/null +++ b/drivers/net/ntnic/nim/qsfp_registers.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _QSFP_REGISTERS_H +#define _QSFP_REGISTERS_H + +/* + * QSFP Registers + */ +#define QSFP_INT_STATUS_RX_LOS_ADDR 3 +#define QSFP_TEMP_LIN_ADDR 22 +#define QSFP_VOLT_LIN_ADDR 26 +#define QSFP_RX_PWR_LIN_ADDR 34 /* uint16_t [0..3] */ +#define QSFP_TX_BIAS_LIN_ADDR 42 /* uint16_t [0..3] */ +#define QSFP_TX_PWR_LIN_ADDR 50 /* uint16_t [0..3] */ + +#define QSFP_CONTROL_STATUS_LIN_ADDR 86 +#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F + +#define QSFP_EXTENDED_IDENTIFIER 129 +#define QSFP_POWER_CLASS_BITS_1_4 0xC0 +#define QSFP_POWER_CLASS_BITS_5_7 0x03 + +#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */ +#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */ +#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */ +#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */ +#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */ +#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */ +#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */ + +#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */ +#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */ + +#define QSFP_OPTION3_LIN_ADDR 195 +#define QSFP_OPTION3_TX_DISABLE_BIT (1 << 4) + +#define QSFP_DMI_OPTION_LIN_ADDR 220 +#define QSFP_DMI_AVG_PWR_BIT (1 << 3) + +#define QSFP_TEMP_THRESH_LIN_ADDR (128 + (3 * 128)) /* Page 3 */ +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define QSFP_VOLT_THRESH_LIN_ADDR (144 + (3 * 128)) /* Page 3 */ +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define QSFP_RX_PWR_THRESH_LIN_ADDR (176 + (3 * 128)) /* Page 3 */ +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define QSFP_BIAS_THRESH_LIN_ADDR (184 + (3 * 128)) /* Page 3 */ +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define QSFP_TX_PWR_THRESH_LIN_ADDR (192 + (3 * 128)) /* Page 3 */ +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#endif /* _QSFP_REGISTERS_H */ diff --git a/drivers/net/ntnic/nim/qsfp_sensors.c b/drivers/net/ntnic/nim/qsfp_sensors.c new file mode 100644 index 0000000000..8264f8fb62 --- /dev/null +++ b/drivers/net/ntnic/nim/qsfp_sensors.c @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include + +#include "qsfp_sensors.h" + +#include "ntlog.h" +#include "qsfp_registers.h" + +static bool qsfp_plus_nim_get_sensor(nim_i2c_ctx_p ctx, uint16_t addr, + nim_option_t nim_option, uint8_t count, + uint16_t *p_lane_values) +{ + (void)nim_option; + + read_data_lin(ctx, addr, (uint16_t)(sizeof(uint16_t) * count), + p_lane_values); + + for (int i = 0; i < count; i++) { + *p_lane_values = (*p_lane_values); /* Swap to little endian */ + +#ifdef NIM_DMI_TEST_VALUE + if (nim_option == NIM_OPTION_RX_POWER) + *p_lane_values = (uint16_t)NIM_DMI_RX_PWR_TEST_VALUE; + else + *p_lane_values = (uint16_t)NIM_DMI_TEST_VALUE; +#endif + + p_lane_values++; + } + + return true; +} + +/* + * Read NIM temperature + */ +static bool qsfp_plus_nim_get_temperature(nim_i2c_ctx_p ctx, int16_t *p_value) +{ + return qsfp_plus_nim_get_sensor(ctx, QSFP_TEMP_LIN_ADDR, NIM_OPTION_TEMP, + 1, (uint16_t *)p_value); +} + +/* + * Read NIM supply voltage + */ +static bool qsfp_plus_nim_get_supply_voltage(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return qsfp_plus_nim_get_sensor(ctx, QSFP_VOLT_LIN_ADDR, + NIM_OPTION_SUPPLY, 1, p_value); +} + +/* + * Read NIM bias current for four lanes + */ +static bool qsfp_plus_nim_get_tx_bias_current(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return qsfp_plus_nim_get_sensor(ctx, QSFP_TX_BIAS_LIN_ADDR, + NIM_OPTION_TX_BIAS, 4, p_value); +} + +/* + * Read NIM TX optical power for four lanes + */ +static bool qsfp_plus_nim_get_tx_power(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return qsfp_plus_nim_get_sensor(ctx, QSFP_TX_PWR_LIN_ADDR, + NIM_OPTION_TX_POWER, 4, p_value); +} + +/* + * Read NIM RX optical power for four lanes + */ +static bool qsfp_plus_nim_get_rx_power(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return qsfp_plus_nim_get_sensor(ctx, QSFP_TX_PWR_LIN_ADDR, + NIM_OPTION_RX_POWER, 4, p_value); +} + +void nim_read_qsfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + int16_t res; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (qsfp_plus_nim_get_temperature(sg->ctx, &res)) + update_sensor_value(sg->sensor, (int)(res * 10 / 256)); + + else + update_sensor_value(sg->sensor, -1); +} + +void nim_read_qsfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t res; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (qsfp_plus_nim_get_supply_voltage(sg->ctx, &res)) + update_sensor_value(sg->sensor, (int)((res) / 10)); + + else + update_sensor_value(sg->sensor, -1); +} + +void nim_read_qsfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp[4] = { 0 }; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + bool res = qsfp_plus_nim_get_tx_bias_current(sg->ctx, temp); + + if (res) { + for (uint8_t i = 0; i < sg->ctx->lane_count; i++) + update_sensor_value(sg->sensor, (int)temp[i] * 2); + } else { + update_sensor_value(sg->sensor, -1); + } +} + +void nim_read_qsfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp[4] = { 0 }; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + bool res = qsfp_plus_nim_get_tx_power(sg->ctx, temp); + + if (res) { + for (uint8_t i = 0; i < sg->ctx->lane_count; i++) + update_sensor_value(sg->sensor, (int)temp[i]); + } else { + update_sensor_value(sg->sensor, -1); + } +} + +void nim_read_qsfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp[4] = { 0 }; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + bool res = qsfp_plus_nim_get_rx_power(sg->ctx, temp); + + if (res) { + for (uint8_t i = 0; i < sg->ctx->lane_count; i++) + update_sensor_value(sg->sensor, (int)temp[i]); + } else { + update_sensor_value(sg->sensor, -1); + } +} diff --git a/drivers/net/ntnic/nim/qsfp_sensors.h b/drivers/net/ntnic/nim/qsfp_sensors.h new file mode 100644 index 0000000000..de64b978cb --- /dev/null +++ b/drivers/net/ntnic/nim/qsfp_sensors.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _QSFP_H +#define _QSFP_H + +#include "sensors.h" +#include "i2c_nim.h" + +/* Read functions */ +void nim_read_qsfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_qsfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_qsfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_qsfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_qsfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi); + +#endif /* _QSFP_H */ diff --git a/drivers/net/ntnic/nim/sfp_p_registers.h b/drivers/net/ntnic/nim/sfp_p_registers.h new file mode 100644 index 0000000000..a0fbe2afd7 --- /dev/null +++ b/drivers/net/ntnic/nim/sfp_p_registers.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _SFP_P_REG_H +#define _SFP_P_REG_H + +/* + * SFP/SFP+ Registers + */ +#define SFP_GB_ETH_COMP_CODES_LIN_ADDR 6 +#define SFP_GB_ETH_COMP_1000BASET_BIT (1 << 3) +#define SFP_GB_ETH_COMP_1000BASECX_BIT (1 << 2) +#define SFP_GB_ETH_COMP_1000BASELX_BIT (1 << 1) +#define SFP_GB_ETH_COMP_1000BASESX_BIT (1 << 0) + +#define SFP_FIBER_CHAN_TRANS_TECH_LIN_ADDR 8 +#define SFP_FIBER_CHAN_TRANS_TECH_ACTIVE_CU_BIT (1 << 3) +#define SFP_FIBER_CHAN_TRANS_TECH_PASSIVE_CU_BIT (1 << 2) + +#define SFP_FIBER_CHAN_TRANS_MEDIA_LIN_ADDR 9 +#define SFP_FIBER_CHAN_TRANS_MEDIA_MM62_BIT (1 << 3) +#define SFP_FIBER_CHAN_TRANS_MEDIA_MM50_BIT (1 << 2) +#define SFP_FIBER_CHAN_TRANS_MEDIA_SM_BIT (1 << 0) + +#define SFP_CU_LINK_LEN_LIN_ADDR 18 /* 1byte */ +#define SFP_SUP_LEN_INFO_LIN_ADDR 14 /* 5bytes */ +#define SFP_CU_LINK_LEN_LIN_ADDR 18 /* 1byte */ +#define SFP_VENDOR_NAME_LIN_ADDR 20 /* 16bytes */ +#define SFP_VENDOR_PN_LIN_ADDR 40 /* 16bytes */ +#define SFP_VENDOR_REV_LIN_ADDR 56 /* 4bytes */ +#define SFP_VENDOR_SN_LIN_ADDR 68 /* 16bytes */ +#define SFP_VENDOR_DATE_LIN_ADDR 84 /* 8bytes */ + +/* The following field is only relevant to SFP+ and is marked as reserved for SFP */ +#define SFP_OPTION0_LIN_ADDR 64 +#define SFP_POWER_LEVEL2_REQ_BIT (1 << 1) + +#define SFP_DMI_OPTION_LIN_ADDR (92) +#define SFP_DMI_IMPL_BIT (1 << 6) +#define SFP_DMI_EXT_CAL_BIT (1 << 4) +#define SFP_DMI_AVG_PWR_BIT (1 << 3) +#define SFP_DMI_ADDR_CHG_BIT (1 << 2) + +#define SFP_ENHANCED_OPTIONS_LIN_ADDR (93) +#define SFP_SOFT_TX_FAULT_IMPL_BIT (1 << 5) +#define SFP_SOFT_TX_DISABLE_IMPL_BIT (1 << 6) + +#define SFP_SFF8472_COMPLIANCE_LIN_ADDR 94 + +#define SFP_TEMP_THRESH_LIN_ADDR (0 + 256) +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define SFP_VOLT_THRESH_LIN_ADDR (8 + 256) +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define SFP_TX_BIAS_THRESH_LIN_ADDR (16 + 256) +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define SFP_TX_PWR_THRESH_LIN_ADDR (24 + 256) +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +#define SFP_RX_PWR_THRESH_LIN_ADDR (32 + 256) +/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */ + +/* Calibration data addresses */ +#define SFP_RX_PWR_COEFF_LIN_ADDR (56 + 256) /* 5 x 32bit float values */ + +#define SFP_TX_BIAS_SLOPE_LIN_ADDR (76 + 256) +#define SFP_TX_BIAS_OFFSET_LIN_ADDR (78 + 256) + +#define SFP_TX_PWR_SLOPE_LIN_ADDR (80 + 256) +#define SFP_TX_PWR_OFFSET_LIN_ADDR (82 + 256) + +#define SFP_TEMP_SLOPE_LIN_ADDR (84 + 256) +#define SFP_TEMP_OFFSET_LIN_ADDR (86 + 256) + +#define SFP_VOLT_SLOPE_LIN_ADDR (88 + 256) +#define SFP_VOLT_OFFSET_LIN_ADDR (90 + 256) + +/* Live data */ +#define SFP_TEMP_LIN_ADDR (96 + 256) +#define SFP_VOLT_LIN_ADDR (98 + 256) +#define SFP_TX_BIAS_LIN_ADDR (100 + 256) +#define SFP_TX_PWR_LIN_ADDR (102 + 256) +#define SFP_RX_PWR_LIN_ADDR (104 + 256) + +#define SFP_SOFT_RATE0_BIT (1 << 3) +#define SFP_TX_FAULT_SET_BIT (1 << 2) + +#define SFP_EXT_CTRL_STAT0_LIN_ADDR (118 + 256) /* 0xA2 area */ +#define SFP_SOFT_RATE1_BIT (1 << 3) +#define SFP_POWER_LEVEL2_GET_BIT (1 << 1) /* For reading the actual level */ +#define SFP_POWER_LEVEL2_SET_BIT (1 << 0) /* For setting the wanted level */ + +/* PHY addresses */ +#define SFP_PHY_LIN_ADDR (12 * 128) +#define SFP_PHY_LIN_RNG 32 /* 16bit words */ + +#endif /* _SFP_P_REG_H */ diff --git a/drivers/net/ntnic/nim/sfp_sensors.c b/drivers/net/ntnic/nim/sfp_sensors.c new file mode 100644 index 0000000000..766d6feaf3 --- /dev/null +++ b/drivers/net/ntnic/nim/sfp_sensors.c @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include + +#include "ntlog.h" +#include "sfp_sensors.h" + +#include "sfp_p_registers.h" + +/* + * Return calibrated data from an SFP module. + * It is first investigated if external calibration is to be used and if it is + * calibration data is retrieved. The function can only be used when calibration + * consists of a slope and offset factor. After function return p_data will point + * to 16bit data that can be either signed or unsigned. + */ +static bool sfp_nim_get_dmi_data(uint16_t data_addr, uint16_t slope_addr, + uint16_t offset_addr, void *p_value, + bool signed_data, nim_i2c_ctx_p ctx) +{ + int32_t value; + uint16_t slope = 1; + int16_t offset = 0; + + if (!ctx->dmi_supp) + return false; + + /* Read data in big endian format */ + read_data_lin(ctx, data_addr, 2, p_value); + *(uint16_t *)p_value = + htons(*(uint16_t *)p_value); /* Swap to little endian */ + + /* + * Inject test value which can be both signed and unsigned but handle + * here as unsigned + */ +#ifdef NIM_DMI_TEST_VALUE + *(uint16_t *)p_value = (uint16_t)NIM_DMI_TEST_VALUE; +#endif + +#if defined(NIM_DMI_TEST_SLOPE) || defined(NIM_DMI_TEST_OFFSET) + ctx->specific_u.sfp.ext_cal = true; +#endif + + if (ctx->specific_u.sfp.ext_cal) { + /* External calibration is needed */ + read_data_lin(ctx, slope_addr, sizeof(slope), &slope); + read_data_lin(ctx, offset_addr, sizeof(offset), &offset); + + /* Swap calibration to little endian */ + slope = htons(slope); + offset = htons(offset); + +#ifdef NIM_DMI_TEST_SLOPE + slope = NIM_DMI_TEST_SLOPE; +#endif + +#ifdef NIM_DMI_TEST_OFFSET + offset = NIM_DMI_TEST_OFFSET; /* 0x0140 equals 1.25 */ +#endif + + if (signed_data) { + value = *(int16_t *)p_value * slope / 256 + offset; + + if (value > INT16_MAX) + value = INT16_MAX; + else if (value < INT16_MIN) + value = INT16_MIN; + + *(int16_t *)p_value = (int16_t)value; + } else { + value = *(uint16_t *)p_value * slope / 256 + offset; + + if (value > UINT16_MAX) + value = UINT16_MAX; + else if (value < 0) + value = 0; + + *(uint16_t *)p_value = (uint16_t)value; + } + } + + return true; +} + +/* + * Read NIM temperature + */ +static bool sfp_nim_get_temperature(nim_i2c_ctx_p ctx, int16_t *p_value) +{ + return sfp_nim_get_dmi_data(SFP_TEMP_LIN_ADDR, SFP_TEMP_SLOPE_LIN_ADDR, + SFP_TEMP_OFFSET_LIN_ADDR, p_value, true, ctx); +} + +/* + * Read NIM supply voltage + */ +static bool sfp_nim_get_supply_voltage(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return sfp_nim_get_dmi_data(SFP_VOLT_LIN_ADDR, SFP_VOLT_SLOPE_LIN_ADDR, + SFP_VOLT_OFFSET_LIN_ADDR, p_value, false, ctx); +} + +/* + * Read NIM bias current + */ +static bool sfp_nim_get_tx_bias_current(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return sfp_nim_get_dmi_data(SFP_TX_BIAS_LIN_ADDR, + SFP_TX_BIAS_SLOPE_LIN_ADDR, + SFP_TX_BIAS_OFFSET_LIN_ADDR, p_value, false, + ctx); +} + +/* + * Read NIM TX optical power + */ +static bool sfp_nim_get_tx_power(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return sfp_nim_get_dmi_data(SFP_TX_PWR_LIN_ADDR, + SFP_TX_PWR_SLOPE_LIN_ADDR, + SFP_TX_PWR_OFFSET_LIN_ADDR, p_value, false, + ctx); +} + +/* + * Return the SFP received power in units of 0.1uW from DMI data. + * If external calibration is necessary, the calibration data is retrieved and + * the calibration is carried out. + */ +static bool sfp_nim_get_calibrated_rx_power(nim_i2c_ctx_p ctx, uint16_t addr, + uint16_t *p_value) +{ + float rx_pwr_cal[5]; + float power_raised; + float rx_power; + + /* Read data in big endian format */ + read_data_lin(ctx, addr, sizeof(*p_value), p_value); + *(uint16_t *)p_value = + htons(*(uint16_t *)p_value); /* Swap to little endian */ + +#ifdef NIM_DMI_RX_PWR_TEST_VALUE + *p_value = NIM_DMI_RX_PWR_TEST_VALUE; +#endif + +#ifdef NIM_DMI_RX_PWR_CAL_DATA + ctx->specific_u.sfp.ext_cal = true; +#endif + + if (ctx->specific_u.sfp.ext_cal) { + /* Read calibration data in big endian format */ + read_data_lin(ctx, SFP_RX_PWR_COEFF_LIN_ADDR, sizeof(rx_pwr_cal), + rx_pwr_cal); + + for (int i = 0; i < 5; i++) { + uint32_t *p_val = (uint32_t *)&rx_pwr_cal[i]; + *p_val = ntohl(*p_val); /* 32 bit swap */ + } + +#ifdef NIM_DMI_RX_PWR_CAL_DATA + /* Testdata for verification */ + NIM_DMI_RX_PWR_CAL_DATA +#endif + + /* + * If SFP module specifies external calibration - use calibration data + * according to the polynomial correction formula + * RxPwrCal = Coeff0 + Coeff1 * RxPwr + Coeff2 * RxPwr^2 + + * Coeff3 * RxPwr^3 + Coeff4 * RxPwr^4 + */ + power_raised = 1.0; + rx_power = rx_pwr_cal[4]; /* Coeff0 */ + + for (int i = 3; i >= 0; i--) { + power_raised *= (float)*p_value; + rx_power += rx_pwr_cal[i] * power_raised; + } + + /* Check out for out of range */ + if (rx_power > 65535) + return false; + + if (rx_power < 0) + *p_value = 0; + else + *p_value = (uint16_t)rx_power; + } + + return true; +} + +/* + * Read RX optical power if it exists + */ +static bool sfp_nim_get_rx_power(nim_i2c_ctx_p ctx, uint16_t *p_value) +{ + return sfp_nim_get_calibrated_rx_power(ctx, SFP_RX_PWR_LIN_ADDR, p_value); +} + +void nim_read_sfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + int16_t temp; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (sfp_nim_get_temperature(sg->ctx, &temp)) + update_sensor_value(sg->sensor, (int)(temp * 10 / 256)); + + else + update_sensor_value(sg->sensor, -1); +} + +void nim_read_sfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (sfp_nim_get_supply_voltage(sg->ctx, &temp)) { + update_sensor_value(sg->sensor, + (int)(temp / 10)); /* Unit: 100uV -> 1mV */ + } else { + update_sensor_value(sg->sensor, -1); + } +} + +void nim_read_sfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (sfp_nim_get_tx_bias_current(sg->ctx, &temp)) + update_sensor_value(sg->sensor, (int)(temp * 2)); + + else + update_sensor_value(sg->sensor, -1); +} + +void nim_read_sfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (sfp_nim_get_tx_power(sg->ctx, &temp)) + update_sensor_value(sg->sensor, (int)temp); + + else + update_sensor_value(sg->sensor, -1); +} + +void nim_read_sfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint16_t temp; + (void)t_spi; + + if (sg == NULL || sg->ctx == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + + if (sfp_nim_get_rx_power(sg->ctx, &temp)) + update_sensor_value(sg->sensor, (int)temp); + + else + update_sensor_value(sg->sensor, -1); +} diff --git a/drivers/net/ntnic/nim/sfp_sensors.h b/drivers/net/ntnic/nim/sfp_sensors.h new file mode 100644 index 0000000000..ab56027dc8 --- /dev/null +++ b/drivers/net/ntnic/nim/sfp_sensors.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _SFP_H +#define _SFP_H + +#include "sensors.h" +#include "i2c_nim.h" + +/* Read functions */ +void nim_read_sfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_sfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_sfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_sfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi); +void nim_read_sfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi); + +#endif /* _SFP_H */ diff --git a/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c b/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c index efdcc222a8..bd7cd2a27c 100644 --- a/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c +++ b/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c @@ -5,5 +5,12 @@ #include "nthw_clock_profiles.h" /* Clock profile for NT200A02 2x40G, 2x100G */ -const int n_data_si5340_nt200a02_u23_v5; -const clk_profile_data_fmt2_t *p_data_si5340_nt200a02_u23_v5; +#define si5340_revd_register_t type_si5340_nt200a02_u23_v5 +#define si5340_revd_registers data_si5340_nt200a02_u23_v5 +#include "nthw_nt200a02_u23_si5340_v5.h" +const int n_data_si5340_nt200a02_u23_v5 = SI5340_REVD_REG_CONFIG_NUM_REGS; +const clk_profile_data_fmt2_t *p_data_si5340_nt200a02_u23_v5 = + (const clk_profile_data_fmt2_t *)&data_si5340_nt200a02_u23_v5[0]; +#undef si5340_revd_registers +#undef si5340_revd_register_t +#undef SI5340_REVD_REG_CONFIG_HEADER /*Disable the include once protection */ diff --git a/drivers/net/ntnic/nthw/core/nthw_core.h b/drivers/net/ntnic/nthw/core/nthw_core.h index 798a95d5cf..025b6b61cc 100644 --- a/drivers/net/ntnic/nthw/core/nthw_core.h +++ b/drivers/net/ntnic/nthw/core/nthw_core.h @@ -16,9 +16,11 @@ #include "nthw_pci_ta.h" #include "nthw_iic.h" +#include "nthw_gmf.h" #include "nthw_gpio_phy.h" #include "nthw_mac_pcs.h" #include "nthw_mac_pcs_xxv.h" +#include "nthw_rmc.h" #include "nthw_sdc.h" #include "nthw_spim.h" diff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.c b/drivers/net/ntnic/nthw/core/nthw_gmf.c new file mode 100644 index 0000000000..fe63c461e5 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_gmf.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_gmf.h" + +nthw_gmf_t *nthw_gmf_new(void) +{ + nthw_gmf_t *p = malloc(sizeof(nthw_gmf_t)); + + if (p) + memset(p, 0, sizeof(nthw_gmf_t)); + return p; +} + +void nthw_gmf_delete(nthw_gmf_t *p) +{ + if (p) { + memset(p, 0, sizeof(nthw_gmf_t)); + free(p); + } +} + +int nthw_gmf_init(nthw_gmf_t *p, nt_fpga_t *p_fpga, int n_instance) +{ + nt_module_t *mod = fpga_query_module(p_fpga, MOD_GMF, n_instance); + + if (p == NULL) + return mod == NULL ? -1 : 0; + + if (mod == NULL) { + NT_LOG(ERR, NTHW, "%s: GMF %d: no such instance\n", + p_fpga->p_fpga_info->mp_adapter_id_str, n_instance); + return -1; + } + + p->mp_fpga = p_fpga; + p->mn_instance = n_instance; + p->mp_mod_gmf = mod; + + p->mp_ctrl = module_get_register(p->mp_mod_gmf, GMF_CTRL); + p->mp_ctrl_enable = register_get_field(p->mp_ctrl, GMF_CTRL_ENABLE); + p->mp_ctrl_ifg_enable = register_get_field(p->mp_ctrl, GMF_CTRL_IFG_ENABLE); + p->mp_ctrl_ifg_auto_adjust_enable = + register_get_field(p->mp_ctrl, GMF_CTRL_IFG_AUTO_ADJUST_ENABLE); + + p->mp_speed = module_get_register(p->mp_mod_gmf, GMF_SPEED); + p->mp_speed_ifg_speed = register_get_field(p->mp_speed, GMF_SPEED_IFG_SPEED); + + p->mp_ifg_clock_delta = + module_get_register(p->mp_mod_gmf, GMF_IFG_SET_CLOCK_DELTA); + p->mp_ifg_clock_delta_delta = + register_get_field(p->mp_ifg_clock_delta, GMF_IFG_SET_CLOCK_DELTA_DELTA); + + p->mp_ifg_max_adjust_slack = + module_get_register(p->mp_mod_gmf, GMF_IFG_MAX_ADJUST_SLACK); + p->mp_ifg_max_adjust_slack_slack = + register_get_field(p->mp_ifg_max_adjust_slack, GMF_IFG_MAX_ADJUST_SLACK_SLACK); + + p->mp_debug_lane_marker = + module_get_register(p->mp_mod_gmf, GMF_DEBUG_LANE_MARKER); + p->mp_debug_lane_marker_compensation = + register_get_field(p->mp_debug_lane_marker, GMF_DEBUG_LANE_MARKER_COMPENSATION); + + p->mp_stat_sticky = module_get_register(p->mp_mod_gmf, GMF_STAT_STICKY); + p->mp_stat_sticky_data_underflowed = + register_get_field(p->mp_stat_sticky, GMF_STAT_STICKY_DATA_UNDERFLOWED); + p->mp_stat_sticky_ifg_adjusted = + register_get_field(p->mp_stat_sticky, GMF_STAT_STICKY_IFG_ADJUSTED); + + p->mn_param_gmf_ifg_speed_mul = + fpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_MUL, 1); + p->mn_param_gmf_ifg_speed_div = + fpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_DIV, 1); + + p->m_administrative_block = false; + + p->mp_stat_next_pkt = module_query_register(p->mp_mod_gmf, GMF_STAT_NEXT_PKT); + if (p->mp_stat_next_pkt) { + p->mp_stat_next_pkt_ns = + register_query_field(p->mp_stat_next_pkt, + GMF_STAT_NEXT_PKT_NS); + } else { + p->mp_stat_next_pkt_ns = NULL; + } + p->mp_stat_max_delayed_pkt = + module_query_register(p->mp_mod_gmf, GMF_STAT_MAX_DELAYED_PKT); + if (p->mp_stat_max_delayed_pkt) { + p->mp_stat_max_delayed_pkt_ns = + register_query_field(p->mp_stat_max_delayed_pkt, + GMF_STAT_MAX_DELAYED_PKT_NS); + } else { + p->mp_stat_max_delayed_pkt_ns = NULL; + } + p->mp_ctrl_ifg_tx_now_always = + register_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_NOW_ALWAYS); + p->mp_ctrl_ifg_tx_on_ts_always = + register_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_ON_TS_ALWAYS); + + p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock = + register_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_ON_TS_ADJUST_ON_SET_CLOCK); + + p->mp_ifg_clock_delta_adjust = + module_query_register(p->mp_mod_gmf, GMF_IFG_SET_CLOCK_DELTA_ADJUST); + if (p->mp_ifg_clock_delta_adjust) { + p->mp_ifg_clock_delta_adjust_delta = + register_query_field(p->mp_ifg_clock_delta_adjust, + GMF_IFG_SET_CLOCK_DELTA_ADJUST_DELTA); + } else { + p->mp_ifg_clock_delta_adjust_delta = NULL; + } + return 0; +} + +void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable) +{ + if (!p->m_administrative_block) + field_set_val_flush32(p->mp_ctrl_enable, enable ? 1 : 0); +} + +void nthw_gmf_set_ifg_enable(nthw_gmf_t *p, bool enable) +{ + field_set_val_flush32(p->mp_ctrl_ifg_enable, enable ? 1 : 0); +} + +void nthw_gmf_set_tx_now_always_enable(nthw_gmf_t *p, bool enable) +{ + if (p->mp_ctrl_ifg_tx_now_always) + field_set_val_flush32(p->mp_ctrl_ifg_tx_now_always, enable ? 1 : 0); +} + +void nthw_gmf_set_tx_on_ts_always_enable(nthw_gmf_t *p, bool enable) +{ + if (p->mp_ctrl_ifg_tx_on_ts_always) + field_set_val_flush32(p->mp_ctrl_ifg_tx_on_ts_always, enable ? 1 : 0); +} + +void nthw_gmf_set_tx_on_ts_adjust_on_set_clock(nthw_gmf_t *p, bool enable) +{ + if (p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock) { + field_set_val_flush32(p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock, + enable ? 1 : 0); + } +} + +void nthw_gmf_set_ifg_auto_adjust_enable(nthw_gmf_t *p, bool enable) +{ + field_set_val_flush32(p->mp_ctrl_ifg_auto_adjust_enable, enable); +} + +int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val) +{ + if (n_speed_val <= + (1ULL << (field_get_bit_width(p->mp_speed_ifg_speed) - 1))) { + field_set_val(p->mp_speed_ifg_speed, (uint32_t *)&n_speed_val, + (field_get_bit_width(p->mp_speed_ifg_speed) <= 32 ? 1 : + 2)); + field_flush_register(p->mp_speed_ifg_speed); + return 0; + } + return -1; +} + +int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p) +{ + const int n_bit_width = field_get_bit_width(p->mp_speed_ifg_speed); + + assert(n_bit_width >= + 22); /* Sanity check: GMF ver 1.2 is bw 22 - GMF ver 1.3 is bw 64 */ + return n_bit_width; +} + +int nthw_gmf_set_ifg_speed_bits(nthw_gmf_t *p, const uint64_t n_rate_limit_bits, + const uint64_t n_link_speed) +{ + const int n_bit_width = (nthw_gmf_get_ifg_speed_bit_width(p) / 2); + const double f_adj_rate = + ((double)((((double)n_rate_limit_bits) / (double)n_link_speed) * + p->mn_param_gmf_ifg_speed_mul) / + p->mn_param_gmf_ifg_speed_div); + const double f_speed = ((1UL / f_adj_rate) - 1) * exp2(n_bit_width); + uint64_t n_speed_val = (uint64_t)round(f_speed); + + return nthw_gmf_set_ifg_speed_raw(p, n_speed_val); +} + +int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double f_rate_limit_percent) +{ + uint64_t n_speed_val; + + if (f_rate_limit_percent == 0.0 || f_rate_limit_percent == 100.0) { + n_speed_val = 0; + } else if (f_rate_limit_percent <= 99) { + const int n_bit_width = (nthw_gmf_get_ifg_speed_bit_width(p) / 2); + const double f_adj_rate = + ((double)(f_rate_limit_percent * + (double)p->mn_param_gmf_ifg_speed_mul) / + p->mn_param_gmf_ifg_speed_div / 100); + const double f_speed = ((1UL / f_adj_rate) - 1) * exp2(n_bit_width); + + n_speed_val = (uint64_t)f_speed; + } else { + return -1; + } + + return nthw_gmf_set_ifg_speed_raw(p, n_speed_val); +} + +void nthw_gmf_set_delta(nthw_gmf_t *p, uint64_t delta) +{ + field_set_val(p->mp_ifg_clock_delta_delta, (uint32_t *)&delta, 2); + field_flush_register(p->mp_ifg_clock_delta_delta); +} + +void nthw_gmf_set_delta_adjust(nthw_gmf_t *p, uint64_t delta_adjust) +{ + if (p->mp_ifg_clock_delta_adjust) { + field_set_val(p->mp_ifg_clock_delta_adjust_delta, + (uint32_t *)&delta_adjust, 2); + field_flush_register(p->mp_ifg_clock_delta_adjust_delta); + } +} + +void nthw_gmf_set_slack(nthw_gmf_t *p, uint64_t slack) +{ + field_set_val(p->mp_ifg_max_adjust_slack_slack, (uint32_t *)&slack, 2); + field_flush_register(p->mp_ifg_max_adjust_slack_slack); +} + +void nthw_gmf_set_compensation(nthw_gmf_t *p, uint32_t compensation) +{ + field_set_val_flush32(p->mp_debug_lane_marker_compensation, compensation); +} + +uint32_t nthw_gmf_get_status_sticky(nthw_gmf_t *p) +{ + uint32_t status = 0; + + register_update(p->mp_stat_sticky); + + if (field_get_val32(p->mp_stat_sticky_data_underflowed)) + status |= GMF_STATUS_MASK_DATA_UNDERFLOWED; + if (field_get_val32(p->mp_stat_sticky_ifg_adjusted)) + status |= GMF_STATUS_MASK_IFG_ADJUSTED; + + return status; +} + +void nthw_gmf_set_status_sticky(nthw_gmf_t *p, uint32_t status) +{ + if (status & GMF_STATUS_MASK_DATA_UNDERFLOWED) + field_set_flush(p->mp_stat_sticky_data_underflowed); + if (status & GMF_STATUS_MASK_IFG_ADJUSTED) + field_set_flush(p->mp_stat_sticky_ifg_adjusted); +} + +uint64_t nthw_gmf_get_stat_next_pkt_ns(nthw_gmf_t *p) +{ + uint64_t value = ULONG_MAX; + + if (p->mp_stat_next_pkt) { + register_update(p->mp_stat_next_pkt); + field_get_val(p->mp_stat_next_pkt_ns, (uint32_t *)&value, 2); + } + return value; +} + +uint64_t nthw_gmf_get_stat_max_pk_delayedt_ns(nthw_gmf_t *p) +{ + uint64_t value = ULONG_MAX; + + if (p->mp_stat_max_delayed_pkt) { + register_update(p->mp_stat_max_delayed_pkt); + field_get_val(p->mp_stat_max_delayed_pkt_ns, (uint32_t *)&value, 2); + } + return value; +} + +void nthw_gmf_administrative_block(nthw_gmf_t *p) +{ + nthw_gmf_set_enable(p, false); + p->m_administrative_block = true; +} diff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.h b/drivers/net/ntnic/nthw/core/nthw_gmf.h new file mode 100644 index 0000000000..aec1342be7 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_gmf.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef __NTHW_GMF_H__ +#define __NTHW_GMF_H__ + +enum gmf_status_mask { + GMF_STATUS_MASK_DATA_UNDERFLOWED = 1, + GMF_STATUS_MASK_IFG_ADJUSTED +}; + +struct nthw_gmf { + nt_fpga_t *mp_fpga; + nt_module_t *mp_mod_gmf; + int mn_instance; + /* */ + + nt_register_t *mp_ctrl; + nt_field_t *mp_ctrl_enable; + nt_field_t *mp_ctrl_ifg_enable; + nt_field_t *mp_ctrl_ifg_tx_now_always; + nt_field_t *mp_ctrl_ifg_tx_on_ts_always; + nt_field_t *mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock; + nt_field_t *mp_ctrl_ifg_auto_adjust_enable; + + nt_register_t *mp_speed; + nt_field_t *mp_speed_ifg_speed; + + nt_register_t *mp_ifg_clock_delta; + nt_field_t *mp_ifg_clock_delta_delta; + + nt_register_t *mp_ifg_clock_delta_adjust; + nt_field_t *mp_ifg_clock_delta_adjust_delta; + + nt_register_t *mp_ifg_max_adjust_slack; + nt_field_t *mp_ifg_max_adjust_slack_slack; + + nt_register_t *mp_debug_lane_marker; + nt_field_t *mp_debug_lane_marker_compensation; + + nt_register_t *mp_stat_sticky; + nt_field_t *mp_stat_sticky_data_underflowed; + nt_field_t *mp_stat_sticky_ifg_adjusted; + + nt_register_t *mp_stat_next_pkt; + nt_field_t *mp_stat_next_pkt_ns; + + nt_register_t *mp_stat_max_delayed_pkt; + nt_field_t *mp_stat_max_delayed_pkt_ns; + + int mn_param_gmf_ifg_speed_mul; + int mn_param_gmf_ifg_speed_div; + + bool m_administrative_block; /* Used to enforce license expiry */ +}; + +typedef struct nthw_gmf nthw_gmf_t; +typedef struct nthw_gmf nthw_gmf; + +nthw_gmf_t *nthw_gmf_new(void); +void nthw_gmf_delete(nthw_gmf_t *p); +int nthw_gmf_init(nthw_gmf_t *p, nt_fpga_t *p_fpga, int n_instance); + +void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable); +void nthw_gmf_set_ifg_enable(nthw_gmf_t *p, bool enable); + +void nthw_gmf_set_tx_now_always_enable(nthw_gmf_t *p, bool enable); +void nthw_gmf_set_tx_on_ts_always_enable(nthw_gmf_t *p, bool enable); +void nthw_gmf_set_tx_on_ts_adjust_on_set_clock(nthw_gmf_t *p, bool enable); +void nthw_gmf_set_ifg_auto_adjust_enable(nthw_gmf_t *p, bool enable); + +int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p); + +int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val); +int nthw_gmf_set_ifg_speed_bits(nthw_gmf_t *p, const uint64_t n_rate_limit_bits, + const uint64_t n_link_speed); +int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double f_rate_limit_percent); + +void nthw_gmf_set_delta(nthw_gmf_t *p, uint64_t delta); +void nthw_gmf_set_delta_adjust(nthw_gmf_t *p, uint64_t delta_adjust); +void nthw_gmf_set_slack(nthw_gmf_t *p, uint64_t slack); +void nthw_gmf_set_compensation(nthw_gmf_t *p, uint32_t compensation); + +uint32_t nthw_gmf_get_status_sticky(nthw_gmf_t *p); +void nthw_gmf_set_status_sticky(nthw_gmf_t *p, uint32_t status); + +uint64_t nthw_gmf_get_stat_next_pkt_ns(nthw_gmf_t *p); +uint64_t nthw_gmf_get_stat_max_pk_delayedt_ns(nthw_gmf_t *p); + +void nthw_gmf_administrative_block(nthw_gmf_t *p); /* Used to enforce license expiry blocking */ + +#endif /* __NTHW_GMF_H__ */ diff --git a/drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h b/drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h new file mode 100644 index 0000000000..f063a1048a --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h @@ -0,0 +1,344 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef SI5340_REVD_REG_CONFIG_HEADER +#define SI5340_REVD_REG_CONFIG_HEADER + +#define SI5340_REVD_REG_CONFIG_NUM_REGS 326 + +typedef struct { + unsigned int address; /* 16-bit register address */ + unsigned char value; /* 8-bit register data */ +} si5340_revd_register_t; + +si5340_revd_register_t const si5340_revd_registers[SI5340_REVD_REG_CONFIG_NUM_REGS] = { + { 0x0B24, 0xC0 }, + { 0x0B25, 0x00 }, + { 0x0502, 0x01 }, + { 0x0505, 0x03 }, + { 0x0957, 0x17 }, + { 0x0B4E, 0x1A }, + { 0x0006, 0x00 }, + { 0x0007, 0x00 }, + { 0x0008, 0x00 }, + { 0x000B, 0x74 }, + { 0x0017, 0xF0 }, + { 0x0018, 0xFF }, + { 0x0021, 0x0F }, + { 0x0022, 0x00 }, + { 0x002B, 0x0A }, + { 0x002C, 0x20 }, + { 0x002D, 0x00 }, + { 0x002E, 0x00 }, + { 0x002F, 0x00 }, + { 0x0030, 0x00 }, + { 0x0031, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0037, 0x00 }, + { 0x0038, 0x00 }, + { 0x0039, 0x00 }, + { 0x003A, 0x00 }, + { 0x003B, 0x00 }, + { 0x003C, 0x00 }, + { 0x003D, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x00 }, + { 0x009E, 0x00 }, + { 0x0102, 0x01 }, + { 0x0112, 0x02 }, + { 0x0113, 0x09 }, + { 0x0114, 0x3E }, + { 0x0115, 0x19 }, + { 0x0117, 0x06 }, + { 0x0118, 0x09 }, + { 0x0119, 0x3E }, + { 0x011A, 0x18 }, + { 0x0126, 0x06 }, + { 0x0127, 0x09 }, + { 0x0128, 0x3E }, + { 0x0129, 0x18 }, + { 0x012B, 0x06 }, + { 0x012C, 0x09 }, + { 0x012D, 0x3E }, + { 0x012E, 0x1A }, + { 0x013F, 0x00 }, + { 0x0140, 0x00 }, + { 0x0141, 0x40 }, + { 0x0206, 0x00 }, + { 0x0208, 0x00 }, + { 0x0209, 0x00 }, + { 0x020A, 0x00 }, + { 0x020B, 0x00 }, + { 0x020C, 0x00 }, + { 0x020D, 0x00 }, + { 0x020E, 0x00 }, + { 0x020F, 0x00 }, + { 0x0210, 0x00 }, + { 0x0211, 0x00 }, + { 0x0212, 0x00 }, + { 0x0213, 0x00 }, + { 0x0214, 0x00 }, + { 0x0215, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x0218, 0x00 }, + { 0x0219, 0x00 }, + { 0x021A, 0x00 }, + { 0x021B, 0x00 }, + { 0x021C, 0x00 }, + { 0x021D, 0x00 }, + { 0x021E, 0x00 }, + { 0x021F, 0x00 }, + { 0x0220, 0x00 }, + { 0x0221, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0228, 0x00 }, + { 0x0229, 0x00 }, + { 0x022A, 0x00 }, + { 0x022B, 0x00 }, + { 0x022C, 0x00 }, + { 0x022D, 0x00 }, + { 0x022E, 0x00 }, + { 0x022F, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0238, 0xA6 }, + { 0x0239, 0x8B }, + { 0x023A, 0x00 }, + { 0x023B, 0x00 }, + { 0x023C, 0x00 }, + { 0x023D, 0x00 }, + { 0x023E, 0x80 }, + { 0x0250, 0x03 }, + { 0x0251, 0x00 }, + { 0x0252, 0x00 }, + { 0x0253, 0x00 }, + { 0x0254, 0x00 }, + { 0x0255, 0x00 }, + { 0x025C, 0x00 }, + { 0x025D, 0x00 }, + { 0x025E, 0x00 }, + { 0x025F, 0x00 }, + { 0x0260, 0x00 }, + { 0x0261, 0x00 }, + { 0x026B, 0x30 }, + { 0x026C, 0x35 }, + { 0x026D, 0x00 }, + { 0x026E, 0x00 }, + { 0x026F, 0x00 }, + { 0x0270, 0x00 }, + { 0x0271, 0x00 }, + { 0x0272, 0x00 }, + { 0x0302, 0x00 }, + { 0x0303, 0x00 }, + { 0x0304, 0x00 }, + { 0x0305, 0x00 }, + { 0x0306, 0x0D }, + { 0x0307, 0x00 }, + { 0x0308, 0x00 }, + { 0x0309, 0x00 }, + { 0x030A, 0x00 }, + { 0x030B, 0x80 }, + { 0x030C, 0x00 }, + { 0x030D, 0x00 }, + { 0x030E, 0x00 }, + { 0x030F, 0x00 }, + { 0x0310, 0x61 }, + { 0x0311, 0x08 }, + { 0x0312, 0x00 }, + { 0x0313, 0x00 }, + { 0x0314, 0x00 }, + { 0x0315, 0x00 }, + { 0x0316, 0x80 }, + { 0x0317, 0x00 }, + { 0x0318, 0x00 }, + { 0x0319, 0x00 }, + { 0x031A, 0x00 }, + { 0x031B, 0xD0 }, + { 0x031C, 0x1A }, + { 0x031D, 0x00 }, + { 0x031E, 0x00 }, + { 0x031F, 0x00 }, + { 0x0320, 0x00 }, + { 0x0321, 0xA0 }, + { 0x0322, 0x00 }, + { 0x0323, 0x00 }, + { 0x0324, 0x00 }, + { 0x0325, 0x00 }, + { 0x0326, 0x00 }, + { 0x0327, 0x00 }, + { 0x0328, 0x00 }, + { 0x0329, 0x00 }, + { 0x032A, 0x00 }, + { 0x032B, 0x00 }, + { 0x032C, 0x00 }, + { 0x032D, 0x00 }, + { 0x0338, 0x00 }, + { 0x0339, 0x1F }, + { 0x033B, 0x00 }, + { 0x033C, 0x00 }, + { 0x033D, 0x00 }, + { 0x033E, 0x00 }, + { 0x033F, 0x00 }, + { 0x0340, 0x00 }, + { 0x0341, 0x00 }, + { 0x0342, 0x00 }, + { 0x0343, 0x00 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x00 }, + { 0x0349, 0x00 }, + { 0x034A, 0x00 }, + { 0x034B, 0x00 }, + { 0x034C, 0x00 }, + { 0x034D, 0x00 }, + { 0x034E, 0x00 }, + { 0x034F, 0x00 }, + { 0x0350, 0x00 }, + { 0x0351, 0x00 }, + { 0x0352, 0x00 }, + { 0x0359, 0x00 }, + { 0x035A, 0x00 }, + { 0x035B, 0x00 }, + { 0x035C, 0x00 }, + { 0x035D, 0x00 }, + { 0x035E, 0x00 }, + { 0x035F, 0x00 }, + { 0x0360, 0x00 }, + { 0x0802, 0x00 }, + { 0x0803, 0x00 }, + { 0x0804, 0x00 }, + { 0x0805, 0x00 }, + { 0x0806, 0x00 }, + { 0x0807, 0x00 }, + { 0x0808, 0x00 }, + { 0x0809, 0x00 }, + { 0x080A, 0x00 }, + { 0x080B, 0x00 }, + { 0x080C, 0x00 }, + { 0x080D, 0x00 }, + { 0x080E, 0x00 }, + { 0x080F, 0x00 }, + { 0x0810, 0x00 }, + { 0x0811, 0x00 }, + { 0x0812, 0x00 }, + { 0x0813, 0x00 }, + { 0x0814, 0x00 }, + { 0x0815, 0x00 }, + { 0x0816, 0x00 }, + { 0x0817, 0x00 }, + { 0x0818, 0x00 }, + { 0x0819, 0x00 }, + { 0x081A, 0x00 }, + { 0x081B, 0x00 }, + { 0x081C, 0x00 }, + { 0x081D, 0x00 }, + { 0x081E, 0x00 }, + { 0x081F, 0x00 }, + { 0x0820, 0x00 }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, + { 0x0824, 0x00 }, + { 0x0825, 0x00 }, + { 0x0826, 0x00 }, + { 0x0827, 0x00 }, + { 0x0828, 0x00 }, + { 0x0829, 0x00 }, + { 0x082A, 0x00 }, + { 0x082B, 0x00 }, + { 0x082C, 0x00 }, + { 0x082D, 0x00 }, + { 0x082E, 0x00 }, + { 0x082F, 0x00 }, + { 0x0830, 0x00 }, + { 0x0831, 0x00 }, + { 0x0832, 0x00 }, + { 0x0833, 0x00 }, + { 0x0834, 0x00 }, + { 0x0835, 0x00 }, + { 0x0836, 0x00 }, + { 0x0837, 0x00 }, + { 0x0838, 0x00 }, + { 0x0839, 0x00 }, + { 0x083A, 0x00 }, + { 0x083B, 0x00 }, + { 0x083C, 0x00 }, + { 0x083D, 0x00 }, + { 0x083E, 0x00 }, + { 0x083F, 0x00 }, + { 0x0840, 0x00 }, + { 0x0841, 0x00 }, + { 0x0842, 0x00 }, + { 0x0843, 0x00 }, + { 0x0844, 0x00 }, + { 0x0845, 0x00 }, + { 0x0846, 0x00 }, + { 0x0847, 0x00 }, + { 0x0848, 0x00 }, + { 0x0849, 0x00 }, + { 0x084A, 0x00 }, + { 0x084B, 0x00 }, + { 0x084C, 0x00 }, + { 0x084D, 0x00 }, + { 0x084E, 0x00 }, + { 0x084F, 0x00 }, + { 0x0850, 0x00 }, + { 0x0851, 0x00 }, + { 0x0852, 0x00 }, + { 0x0853, 0x00 }, + { 0x0854, 0x00 }, + { 0x0855, 0x00 }, + { 0x0856, 0x00 }, + { 0x0857, 0x00 }, + { 0x0858, 0x00 }, + { 0x0859, 0x00 }, + { 0x085A, 0x00 }, + { 0x085B, 0x00 }, + { 0x085C, 0x00 }, + { 0x085D, 0x00 }, + { 0x085E, 0x00 }, + { 0x085F, 0x00 }, + { 0x0860, 0x00 }, + { 0x0861, 0x00 }, + { 0x090E, 0x02 }, + { 0x091C, 0x04 }, + { 0x0943, 0x00 }, + { 0x0949, 0x00 }, + { 0x094A, 0x00 }, + { 0x094E, 0x49 }, + { 0x094F, 0x02 }, + { 0x095E, 0x00 }, + { 0x0A02, 0x00 }, + { 0x0A03, 0x07 }, + { 0x0A04, 0x01 }, + { 0x0A05, 0x07 }, + { 0x0A14, 0x00 }, + { 0x0A1A, 0x00 }, + { 0x0A20, 0x00 }, + { 0x0A26, 0x00 }, + { 0x0B44, 0x0F }, + { 0x0B4A, 0x08 }, + { 0x0B57, 0x0E }, + { 0x0B58, 0x01 }, + { 0x001C, 0x01 }, + { 0x0B24, 0xC3 }, + { 0x0B25, 0x02 }, +}; + +#endif /* SI5340_REVD_REG_CONFIG_HEADER */ diff --git a/drivers/net/ntnic/nthw/core/nthw_rmc.c b/drivers/net/ntnic/nthw/core/nthw_rmc.c new file mode 100644 index 0000000000..c4c6779ce0 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_rmc.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_rmc.h" + +nthw_rmc_t *nthw_rmc_new(void) +{ + nthw_rmc_t *p = malloc(sizeof(nthw_rmc_t)); + + if (p) + memset(p, 0, sizeof(nthw_rmc_t)); + return p; +} + +void nthw_rmc_delete(nthw_rmc_t *p) +{ + if (p) { + memset(p, 0, sizeof(nthw_rmc_t)); + free(p); + } +} + +int nthw_rmc_init(nthw_rmc_t *p, nt_fpga_t *p_fpga, int n_instance) +{ + const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str; + nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_RMC, n_instance); + + if (p == NULL) + return p_mod == NULL ? -1 : 0; + + if (p_mod == NULL) { + NT_LOG(ERR, NTHW, "%s: RMC %d: no such instance\n", + p_adapter_id_str, n_instance); + return -1; + } + + p->mp_fpga = p_fpga; + p->mn_instance = n_instance; + p->mp_mod_rmc = p_mod; + + /* Params */ + p->mb_is_vswitch = p_fpga->p_fpga_info->profile == FPGA_INFO_PROFILE_VSWITCH; + p->mn_ports = fpga_get_product_param(p_fpga, NT_RX_PORTS, + fpga_get_product_param(p_fpga, NT_PORTS, 0)); + p->mn_nims = fpga_get_product_param(p_fpga, NT_NIMS, 0); + p->mb_administrative_block = false; + + NT_LOG(DBG, NTHW, "%s: RMC %d: vswitch=%d\n", p_adapter_id_str, + p->mn_instance, p->mb_is_vswitch); + + p->mp_reg_ctrl = module_get_register(p->mp_mod_rmc, RMC_CTRL); + + p->mp_fld_ctrl_block_stat_drop = + register_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_STATT); + p->mp_fld_ctrl_block_keep_alive = + register_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_KEEPA); + p->mp_fld_ctrl_block_mac_port = + register_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_MAC_PORT); + + p->mp_reg_status = module_query_register(p->mp_mod_rmc, RMC_STATUS); + if (p->mp_reg_status) { + p->mp_fld_sf_ram_of = + register_get_field(p->mp_reg_status, RMC_STATUS_SF_RAM_OF); + p->mp_fld_descr_fifo_of = + register_get_field(p->mp_reg_status, RMC_STATUS_DESCR_FIFO_OF); + } + + p->mp_reg_dbg = module_query_register(p->mp_mod_rmc, RMC_DBG); + if (p->mp_reg_dbg) { + p->mp_fld_dbg_merge = + register_get_field(p->mp_reg_dbg, RMC_DBG_MERGE); + } + + p->mp_reg_mac_if = module_query_register(p->mp_mod_rmc, RMC_MAC_IF); + if (p->mp_reg_mac_if) { + p->mp_fld_mac_if_err = + register_get_field(p->mp_reg_mac_if, RMC_MAC_IF_ERR); + } + return 0; +} + +uint32_t nthw_rmc_get_mac_block(nthw_rmc_t *p) +{ + return field_get_updated(p->mp_fld_ctrl_block_mac_port); +} + +uint32_t nthw_rmc_get_status_sf_ram_of(nthw_rmc_t *p) +{ + return (p->mp_reg_status) ? field_get_updated(p->mp_fld_sf_ram_of) : + 0xffffffff; +} + +uint32_t nthw_rmc_get_status_descr_fifo_of(nthw_rmc_t *p) +{ + return (p->mp_reg_status) ? field_get_updated(p->mp_fld_descr_fifo_of) : + 0xffffffff; +} + +uint32_t nthw_rmc_get_dbg_merge(nthw_rmc_t *p) +{ + return (p->mp_reg_dbg) ? field_get_updated(p->mp_fld_dbg_merge) : 0xffffffff; +} + +uint32_t nthw_rmc_get_mac_if_err(nthw_rmc_t *p) +{ + return (p->mp_reg_mac_if) ? field_get_updated(p->mp_fld_mac_if_err) : + 0xffffffff; +} + +void nthw_rmc_set_mac_block(nthw_rmc_t *p, uint32_t mask) +{ + field_set_val_flush32(p->mp_fld_ctrl_block_mac_port, mask); +} + +void nthw_rmc_block(nthw_rmc_t *p) +{ + /* BLOCK_STATT(0)=1 BLOCK_KEEPA(1)=1 BLOCK_MAC_PORT(8:11)=~0 */ + if (!p->mb_administrative_block) { + field_set_flush(p->mp_fld_ctrl_block_stat_drop); + field_set_flush(p->mp_fld_ctrl_block_keep_alive); + field_set_flush(p->mp_fld_ctrl_block_mac_port); + } +} + +void nthw_rmc_unblock(nthw_rmc_t *p, bool b_is_slave) +{ + uint32_t n_block_mask = ~0U << (b_is_slave ? p->mn_nims : p->mn_ports); + + if (p->mb_is_vswitch) { + /* + * VSWITCH: NFV: block bits: phy_nim_ports(2) + rtd_ports(4) + + * roa_recirculate_port(1) + */ + n_block_mask = 1 << (2 + 4); /* block only ROA recirculate */ + } + + /* BLOCK_STATT(0)=0 BLOCK_KEEPA(1)=0 BLOCK_MAC_PORT(8:11)=0 */ + if (!p->mb_administrative_block) { + field_clr_flush(p->mp_fld_ctrl_block_stat_drop); + field_clr_flush(p->mp_fld_ctrl_block_keep_alive); + field_set_val_flush32(p->mp_fld_ctrl_block_mac_port, n_block_mask); + } +} + +void nthw_rmc_administrative_block(nthw_rmc_t *p) +{ + /* block all MAC ports */ + field_set_flush(p->mp_fld_ctrl_block_mac_port); + p->mb_administrative_block = true; +} diff --git a/drivers/net/ntnic/nthw/core/nthw_rmc.h b/drivers/net/ntnic/nthw/core/nthw_rmc.h new file mode 100644 index 0000000000..b40f0a0994 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_rmc.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NTHW_RMC_H_ +#define NTHW_RMC_H_ + +struct nthw_rmc { + nt_fpga_t *mp_fpga; + nt_module_t *mp_mod_rmc; + int mn_instance; + + int mn_ports; + int mn_nims; + bool mb_is_vswitch; + + bool mb_administrative_block; + + /* RMC CTRL register */ + nt_register_t *mp_reg_ctrl; + nt_field_t *mp_fld_ctrl_block_stat_drop; + nt_field_t *mp_fld_ctrl_block_keep_alive; + nt_field_t *mp_fld_ctrl_block_mac_port; + + /* RMC Status register */ + nt_register_t *mp_reg_status; + nt_field_t *mp_fld_sf_ram_of; + nt_field_t *mp_fld_descr_fifo_of; + + /* RMC DBG register */ + nt_register_t *mp_reg_dbg; + nt_field_t *mp_fld_dbg_merge; + + /* RMC MAC_IF register */ + nt_register_t *mp_reg_mac_if; + nt_field_t *mp_fld_mac_if_err; +}; + +typedef struct nthw_rmc nthw_rmc_t; +typedef struct nthw_rmc nthw_rmc; + +nthw_rmc_t *nthw_rmc_new(void); +void nthw_rmc_delete(nthw_rmc_t *p); +int nthw_rmc_init(nthw_rmc_t *p, nt_fpga_t *p_fpga, int n_instance); + +uint32_t nthw_rmc_get_mac_block(nthw_rmc_t *p); +void nthw_rmc_set_mac_block(nthw_rmc_t *p, uint32_t mask); +void nthw_rmc_block(nthw_rmc_t *p); +void nthw_rmc_unblock(nthw_rmc_t *p, bool b_is_slave); +void nthw_rmc_administrative_block(nthw_rmc_t *p); + +uint32_t nthw_rmc_get_status_sf_ram_of(nthw_rmc_t *p); +uint32_t nthw_rmc_get_status_descr_fifo_of(nthw_rmc_t *p); +uint32_t nthw_rmc_get_dbg_merge(nthw_rmc_t *p); +uint32_t nthw_rmc_get_mac_if_err(nthw_rmc_t *p); + +#endif /* NTHW_RMC_H_ */ diff --git a/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c new file mode 100644 index 0000000000..bf120ccb39 --- /dev/null +++ b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "avr_sensors.h" +#include "ntlog.h" + +#define MAX_ADAPTERS 2 + +uint8_t s_fpga_indexes[MAX_ADAPTERS] = { 0 }; /* _NTSD_MAX_NUM_ADAPTERS_ */ +static uint8_t get_fpga_idx(unsigned int adapter_no); + +/* + * This function setups monitoring of AVR sensors + */ +static uint8_t _avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no, + const char *p_name, + enum sensor_mon_device avr_dev, + uint8_t avr_dev_reg, enum sensor_mon_endian end, + enum sensor_mon_sign si, uint16_t mask) +{ + uint8_t fpga_idx = get_fpga_idx(m_adapter_no); + struct sensor_mon_setup16 avr_sensor_setup; + + /* Setup monitoring in AVR placing results in FPGA */ + avr_sensor_setup.setup_cnt = 1; + avr_sensor_setup.setup_data[0].fpga_idx = fpga_idx; + avr_sensor_setup.setup_data[0].device = avr_dev; + avr_sensor_setup.setup_data[0].device_register = avr_dev_reg; + avr_sensor_setup.setup_data[0].format = (uint16_t)(end | si << 2); + + avr_sensor_setup.setup_data[0].mask = mask; + avr_sensor_setup.setup_data[0].pos = + 0; /* So far for all sensors in table */ + + /* + * At first it is the task of ntservice to test limit_low and limit_high on all + * board sensors. Later the test is going to be carried out by the AVR + */ + if (si == SENSOR_MON_SIGNED) { + avr_sensor_setup.setup_data[0].int16.limit_low = + SENSOR_MON_INT16_NAN; + avr_sensor_setup.setup_data[0].int16.limit_high = + SENSOR_MON_INT16_NAN; + } else { + avr_sensor_setup.setup_data[0].uint16.limit_low = + SENSOR_MON_UINT16_NAN; + avr_sensor_setup.setup_data[0].uint16.limit_high = + SENSOR_MON_UINT16_NAN; + } + + int result = nt_avr_sensor_mon_setup(&avr_sensor_setup, s_spi); + + if (result) + NT_LOG(ERR, ETHDEV, "%s: sensor initialization error\n", p_name); + + return fpga_idx; +} + +static void avr_read(struct nt_sensor_group *sg, nthw_spis_t *t_spi) +{ + uint32_t p_sensor_result; + + if (sg == NULL || sg->sensor == NULL) + return; + + sensor_read(t_spi, sg->sensor->fpga_idx, &p_sensor_result); + update_sensor_value(sg->sensor, sg->conv_func(p_sensor_result)); +} + +struct nt_sensor_group * +avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no, const char *p_name, + enum nt_sensor_source_e ssrc, enum nt_sensor_type_e type, + unsigned int index, enum sensor_mon_device avr_dev, + uint8_t avr_dev_reg, enum sensor_mon_endian end, + enum sensor_mon_sign si, int (*conv_func)(uint32_t), + uint16_t mask) +{ + struct nt_sensor_group *sg = malloc(sizeof(struct nt_sensor_group)); + + if (sg == NULL) { + NT_LOG(ERR, ETHDEV, "%s: sensor group is NULL", __func__); + return NULL; + } + init_sensor_group(sg); + sg->sensor = allocate_sensor(m_adapter_no, p_name, ssrc, type, index, + NT_SENSOR_DISABLE_ALARM, si); + sg->sensor->fpga_idx = _avr_sensor_init(s_spi, m_adapter_no, p_name, avr_dev, + avr_dev_reg, end, si, mask); + sg->read = &avr_read; + sg->conv_func = conv_func; + sg->monitor = NULL; + sg->next = NULL; + return sg; +} + +static uint8_t get_fpga_idx(unsigned int adapter_no) +{ + uint8_t tmp = s_fpga_indexes[adapter_no]; + + s_fpga_indexes[adapter_no] = (uint8_t)(tmp + 1); + + return tmp; +} diff --git a/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h new file mode 100644 index 0000000000..b8c37a12cb --- /dev/null +++ b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _AVR_SENSORS_H +#define _AVR_SENSORS_H + +#include + +#include "sensors.h" +#include "avr_intf.h" +#include "ntavr.h" + +struct nt_sensor_group * +avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no, const char *p_name, + enum nt_sensor_source_e ssrc, enum nt_sensor_type_e type, + unsigned int index, enum sensor_mon_device avr_dev, + uint8_t avr_dev_reg, enum sensor_mon_endian end, + enum sensor_mon_sign si, int (*conv_func)(uint32_t), + uint16_t mask); + +#endif /* _AVR_SENSORS_H */ diff --git a/drivers/net/ntnic/sensors/board_sensors/board_sensors.c b/drivers/net/ntnic/sensors/board_sensors/board_sensors.c new file mode 100644 index 0000000000..8e52379df8 --- /dev/null +++ b/drivers/net/ntnic/sensors/board_sensors/board_sensors.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include +#include + +#include "tempmon.h" +#include "board_sensors.h" +#include "ntlog.h" + +static void fpga_temperature_sensor_read(struct nt_sensor_group *sg, + nthw_spis_t *t_spi) +{ + int temp = 0; + (void)t_spi; + if (sg == NULL || sg->sensor == NULL) { + NT_LOG(ERR, ETHDEV, "failed to read FPGA temperature\n"); + return; + } + struct nt_fpga_sensor_monitor *temp_monitor = sg->monitor; + uint32_t val = field_get_updated(temp_monitor->fields[0]); + + temp = (val * 20159 - 44752896) / 16384; + + update_sensor_value(sg->sensor, temp); +} + +struct nt_sensor_group *fpga_temperature_sensor_init(uint8_t adapter_no, + unsigned int sensor_idx, + nt_fpga_t *p_fpga) +{ + struct nt_sensor_group *sg = malloc(sizeof(struct nt_sensor_group)); + + if (sg == NULL) { + NT_LOG(ERR, ETHDEV, "%s: sensor group is NULL", __func__); + return NULL; + } + init_sensor_group(sg); + sg->monitor = tempmon_new(); + tempmon_init(sg->monitor, p_fpga); + sg->sensor = + allocate_sensor(adapter_no, "FPGA", NT_SENSOR_SOURCE_ADAPTER, + NT_SENSOR_TYPE_TEMPERATURE, sensor_idx, + NT_SENSOR_DISABLE_ALARM, SENSOR_MON_UNSIGNED); + sg->read = &fpga_temperature_sensor_read; + return sg; +} diff --git a/drivers/net/ntnic/sensors/board_sensors/board_sensors.h b/drivers/net/ntnic/sensors/board_sensors/board_sensors.h new file mode 100644 index 0000000000..a7f75b7ae4 --- /dev/null +++ b/drivers/net/ntnic/sensors/board_sensors/board_sensors.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _BOARD_SENSORS_H +#define _BOARD_SENSORS_H + +#include + +#include "sensors.h" + +#include "nthw_fpga_model.h" + +struct nt_sensor_group *fpga_temperature_sensor_init(uint8_t adapter_no, + unsigned int sensor_idx, + nt_fpga_t *p_fpga); + +#endif /* _BOARD_SENSORS_H */ diff --git a/drivers/net/ntnic/sensors/board_sensors/tempmon.c b/drivers/net/ntnic/sensors/board_sensors/tempmon.c new file mode 100644 index 0000000000..2cd3709205 --- /dev/null +++ b/drivers/net/ntnic/sensors/board_sensors/tempmon.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "tempmon.h" +#include "ntlog.h" +#include "nthw_register.h" + +struct nt_fpga_sensor_monitor *tempmon_new(void) +{ + struct nt_fpga_sensor_monitor *temp = + malloc(sizeof(struct nt_fpga_sensor_monitor)); + if (temp == NULL) + NT_LOG(ERR, ETHDEV, "%s: monitor is NULL\n", __func__); + return temp; +} + +void tempmon_init(struct nt_fpga_sensor_monitor *t, nt_fpga_t *p_fpga) +{ + if (t == NULL || p_fpga == NULL) { + NT_LOG(ERR, ETHDEV, "%s: bad argument(s)\n", __func__); + return; + } + /* fetch initialized module */ + t->fpga = p_fpga; + t->mod = nthw_get_module(t->fpga, MOD_TEMPMON, 0); + if (t->mod == NULL) + NT_LOG(ERR, ETHDEV, "module is NULL\n"); + /* fetch register */ + t->reg = module_get_register(t->mod, TEMPMON_STAT); + if (t->reg == NULL) + NT_LOG(ERR, ETHDEV, "register is NULL\n"); + /* fetch fields */ + t->fields = malloc(sizeof(nt_field_t *)); + if (t->fields == NULL) { + NT_LOG(ERR, ETHDEV, "%s: field is NULL", __func__); + return; + } + t->fields[0] = register_get_field(t->reg, TEMPMON_STAT_TEMP); + if (t->fields[0] == NULL) + NT_LOG(ERR, ETHDEV, "field is NULL\n"); +} diff --git a/drivers/net/ntnic/sensors/board_sensors/tempmon.h b/drivers/net/ntnic/sensors/board_sensors/tempmon.h new file mode 100644 index 0000000000..6f2017b714 --- /dev/null +++ b/drivers/net/ntnic/sensors/board_sensors/tempmon.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _TEMPMON_H +#define _TEMPMON_H + +#include "nthw_fpga_model.h" +#include + +#include "sensors.h" + +struct nt_fpga_sensor_monitor *tempmon_new(void); +void tempmon_init(struct nt_fpga_sensor_monitor *t, nt_fpga_t *p_fpga); + +#endif /* _TEMPMON_H */ diff --git a/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c new file mode 100644 index 0000000000..e130855a35 --- /dev/null +++ b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include + +#include "nim_sensors.h" +#include "ntlog.h" + +#define TEMP NT_SENSOR_TYPE_TEMPERATURE +#define VOLT NT_SENSOR_TYPE_VOLTAGE +#define CURR NT_SENSOR_TYPE_CURRENT +#define PWR NT_SENSOR_TYPE_POWER + +#define SNA NT_SENSOR_SUBTYPE_NA +#define AVG NT_SENSOR_SUBTYPE_POWER_AVERAGE + +#define ENA NT_SENSOR_ENABLE_ALARM +#define DIA NT_SENSOR_DISABLE_ALARM + +/* + * Sensors for SFP/SFP+/SFP28. The name of the level 0 temperature sensor is + * empty and will then be set automatically + */ +struct nt_adapter_sensor_description sfp_sensors_level0[1] = { + { TEMP, SNA, NT_SENSOR_SFP_TEMP, DIA, "" }, +}; + +struct nt_adapter_sensor_description sfp_sensors_level1[4] = { + { VOLT, SNA, NT_SENSOR_SFP_SUPPLY, DIA, "Supply" }, + { CURR, SNA, NT_SENSOR_SFP_TX_BIAS, DIA, "Tx Bias" }, + { PWR, AVG, NT_SENSOR_SFP_TX_POWER, DIA, "Tx" }, + { PWR, AVG, NT_SENSOR_SFP_RX_POWER, DIA, "Rx" } +}; + +struct nt_adapter_sensor_description qsfp_sensor_level0[1] = { + { TEMP, SNA, NT_SENSOR_QSFP_TEMP, DIA, "" }, +}; + +struct nt_adapter_sensor_description qsfp_sensor_level1[13] = { + { VOLT, SNA, NT_SENSOR_QSFP_SUPPLY, DIA, "Supply" }, + { CURR, SNA, NT_SENSOR_QSFP_TX_BIAS1, DIA, "Tx Bias 1" }, + { CURR, SNA, NT_SENSOR_QSFP_TX_BIAS2, DIA, "Tx Bias 2" }, + { CURR, SNA, NT_SENSOR_QSFP_TX_BIAS3, DIA, "Tx Bias 3" }, + { CURR, SNA, NT_SENSOR_QSFP_TX_BIAS4, DIA, "Tx Bias 4" }, + { PWR, AVG, NT_SENSOR_QSFP_TX_POWER1, DIA, "Tx 1" }, + { PWR, AVG, NT_SENSOR_QSFP_TX_POWER2, DIA, "Tx 2" }, + { PWR, AVG, NT_SENSOR_QSFP_TX_POWER3, DIA, "Tx 3" }, + { PWR, AVG, NT_SENSOR_QSFP_TX_POWER4, DIA, "Tx 4" }, + { PWR, AVG, NT_SENSOR_QSFP_RX_POWER1, DIA, "Rx 1" }, + { PWR, AVG, NT_SENSOR_QSFP_RX_POWER2, DIA, "Rx 2" }, + { PWR, AVG, NT_SENSOR_QSFP_RX_POWER3, DIA, "Rx 3" }, + { PWR, AVG, NT_SENSOR_QSFP_RX_POWER4, DIA, "Rx 4" } +}; diff --git a/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h new file mode 100644 index 0000000000..c68c9aa924 --- /dev/null +++ b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NIM_SENSORS_H +#define _NIM_SENSORS_H + +#include +#include +#include "sensors.h" + +#define XFP_TEMP_LIN_ADDR 96 + +extern struct nt_adapter_sensor_description sfp_sensors_level0[1]; +extern struct nt_adapter_sensor_description sfp_sensors_level1[4]; +extern struct nt_adapter_sensor_description qsfp_sensor_level0[1]; +extern struct nt_adapter_sensor_description qsfp_sensor_level1[13]; + +#endif /* _NIM_SENSORS_H */ diff --git a/drivers/net/ntnic/sensors/ntavr/avr_intf.h b/drivers/net/ntnic/sensors/ntavr/avr_intf.h new file mode 100644 index 0000000000..feeec6e13a --- /dev/null +++ b/drivers/net/ntnic/sensors/ntavr/avr_intf.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _AVR_INTF +#define _AVR_INTF + +#include + +#define SENSOR_MON_UINT16_NAN 0xFFFF /* Most positive number used as NaN */ +#define SENSOR_MON_INT16_NAN \ + ((int16_t)0x8000) /* Most negative number used as NaN */ + +/* + * Specify the nature of the raw data. AVR and ntservice must use this + * information when comparing or converting to native format which is little endian + */ +enum sensor_mon_endian { SENSOR_MON_LITTLE_ENDIAN, SENSOR_MON_BIG_ENDIAN }; + +enum sensor_mon_sign { + SENSOR_MON_UNSIGNED, + SENSOR_MON_SIGNED, /* 2's complement */ +}; + +/* Define sensor devices */ +enum sensor_mon_device { + SENSOR_MON_PSU_EXAR_7724_0 = 0, /* NT40E3, NT100E3 */ + SENSOR_MON_PSU_EXAR_7724_1, /* NT40E3, NT100E3 */ + SENSOR_MON_PSU_LTM_4676_0, /* na NT100E3, page-0 */ + SENSOR_MON_PSU_LTM_4676_1, /* na NT100E3, page-0 */ + SENSOR_MON_INA219_1, /* NT40E3, NT100E3 */ + SENSOR_MON_INA219_2, /* NT40E3, NT100E3 */ + SENSOR_MON_MAX6642, /* NT40E3, NT100E3 */ + SENSOR_MON_DS1775, /* NT40E3, NT100E3 */ + SENSOR_MON_FAN, /* NT40E3, NT100E3 */ + SENSOR_MON_AVR, /* NT40E3, NT100E3 */ + SENSOR_MON_PEX8734, /* na NT100E3 */ + SENSOR_MON_RATE_COUNT, /* NT40E3, NT100E3 */ + SENSOR_MON_PSU_LTM_4676_0_1, /* na NT100E3, page-1 */ + SENSOR_MON_PSU_LTM_4676_1_1, /* na NT100E3, page-1 */ + SENSOR_MON_MP2886A, /* na, na, NT200A02, */ + SENSOR_MON_PSU_EM2260_1, /* na, na, na, na, NT200D01^M */ + SENSOR_MON_PSU_EM2120_2, /* na, na, na, na, NT200D01^M */ + SENSOR_MON_MP2886A_PSU_1, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_MP8869S_PSU_2, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_MP8645PGVT_PSU_3, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_MP8645PGVT_PSU_4, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_MP8869S_PSU_5, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_MP8869S_PSU_6, /* na, na, na, NT200A02, na, NT50B01, */ + SENSOR_MON_DEVICE_COUNT +}; + +#pragma pack(1) +struct sensor_mon_setup_data16 { + uint8_t fpga_idx; /* Destination of results */ + uint8_t device; /* Device to monitor */ + uint8_t device_register; /* Sensor within device */ + uint16_t mask; /* Indicates active bits */ + uint8_t pos; /* Position of first active bit */ + uint16_t format; /* b0,1:sensor_mon_endian_t endian */ + /* b2,3:sensor_mon_sign_t sign */ + union { + struct { + int16_t limit_low; /* Signed alarm limit low */ + int16_t limit_high; /* Signed alarm limit high */ + } int16; + + struct { + uint16_t limit_low; /* Unsigned alarm limit low */ + uint16_t limit_high; /* Unsigned alarm limit high */ + } uint16; + }; +}; + +#pragma pack() +struct sensor_mon_setup16 { + uint8_t setup_cnt; /* Number of entries in setup_data */ + struct sensor_mon_setup_data16 setup_data[40]; +}; + +/* Define sensor monitoring control */ +enum sensor_mon_control { + SENSOR_MON_CTRL_STOP = 0, /* Stop sensor monitoring */ + SENSOR_MON_CTRL_RUN = 1, /* Start sensor monitoring */ + SENSOR_MON_CTRL_REM_ALL_SENSORS = + 2, /* Stop and remove all sensor monitoring setup */ +}; + +#endif /* _AVR_INTF */ diff --git a/drivers/net/ntnic/sensors/ntavr/ntavr.c b/drivers/net/ntnic/sensors/ntavr/ntavr.c new file mode 100644 index 0000000000..6d8c3042b1 --- /dev/null +++ b/drivers/net/ntnic/sensors/ntavr/ntavr.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "ntavr.h" +#include "ntlog.h" + +static int txrx(nthw_spi_v3_t *s_spi, enum avr_opcodes opcode, size_t txsz, + uint16_t *tx, size_t *rxsz, uint16_t *rx) +{ + int res = 1; + struct tx_rx_buf m_tx = { .size = (uint16_t)txsz, .p_buf = tx }; + struct tx_rx_buf m_rx = { .size = (uint16_t)*rxsz, .p_buf = rx }; + + res = nthw_spi_v3_transfer(s_spi, opcode, &m_tx, &m_rx); + if (res) { + NT_LOG(ERR, ETHDEV, "%s transfer failed - %i", __func__, res); + return res; + } + + if (rxsz != NULL) + *rxsz = m_rx.size; + + return res; +} + +uint32_t sensor_read(nthw_spis_t *t_spi, uint8_t fpga_idx, + uint32_t *p_sensor_result) +{ + return nthw_spis_read_sensor(t_spi, fpga_idx, p_sensor_result); +} + +int nt_avr_sensor_mon_setup(struct sensor_mon_setup16 *p_setup, nthw_spi_v3_t *s_spi) +{ + int error; + size_t tx_size; + size_t rx_size = 0; + + tx_size = sizeof(struct sensor_mon_setup16) - sizeof(p_setup->setup_data); + tx_size += sizeof(p_setup->setup_data[0]) * p_setup->setup_cnt; + + error = txrx(s_spi, AVR_OP_SENSOR_MON_SETUP, tx_size, (uint16_t *)p_setup, + &rx_size, NULL); + + if (error) { + NT_LOG(ERR, ETHDEV, "%s failed\n", __func__); + return error; + } + + if (rx_size != 0) { + NT_LOG(ERR, ETHDEV, + "%s: Returned data: Expected size = 0, Actual = %zu", + __func__, rx_size); + return 1; + } + return 0; +} + +int nt_avr_sensor_mon_ctrl(nthw_spi_v3_t *s_spi, enum sensor_mon_control ctrl) +{ + int error; + size_t rx_size = 0; + + error = txrx(s_spi, AVR_OP_SENSOR_MON_CONTROL, sizeof(ctrl), + (uint16_t *)(&ctrl), &rx_size, NULL); + + if (error != 0) + return error; + + if (rx_size != 0) { + NT_LOG(ERR, ETHDEV, + "%s: Returned data: Expected size = 0, Actual = %zu", + __func__, rx_size); + return 1; + } + + return 0; +} diff --git a/drivers/net/ntnic/sensors/ntavr/ntavr.h b/drivers/net/ntnic/sensors/ntavr/ntavr.h new file mode 100644 index 0000000000..b7a7aeb908 --- /dev/null +++ b/drivers/net/ntnic/sensors/ntavr/ntavr.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _NTAVR_H +#define _NTAVR_H + +#include +#include + +#include "avr_intf.h" +#include "nthw_drv.h" +#include "nthw_spi_v3.h" + +/* + * @internal + * @brief AVR Device Enum + * + * Global names for identifying an AVR device for Generation2 adapters + */ +enum ntavr_device { + NTAVR_MAINBOARD, /* Mainboard AVR device */ + NTAVR_FRONTBOARD /* Frontboard AVR device */ +}; + +int nt_avr_sensor_mon_setup(struct sensor_mon_setup16 *p_setup, + nthw_spi_v3_t *s_spi); +int nt_avr_sensor_mon_ctrl(nthw_spi_v3_t *s_spi, enum sensor_mon_control ctrl); +uint32_t sensor_read(nthw_spis_t *t_spi, uint8_t fpga_idx, + uint32_t *p_sensor_result); + +#endif /* _NTAVR_H */ diff --git a/drivers/net/ntnic/sensors/sensor_types.h b/drivers/net/ntnic/sensors/sensor_types.h new file mode 100644 index 0000000000..bac4e925f9 --- /dev/null +++ b/drivers/net/ntnic/sensors/sensor_types.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _SENSOR_TYPES_H +#define _SENSOR_TYPES_H + +/* + * Sensor types + */ +enum nt_sensor_type_e { + NT_SENSOR_TYPE_UNKNOWN = 0, + NT_SENSOR_TYPE_TEMPERATURE = 1, /* Unit: 0.1 degree Celsius */ + NT_SENSOR_TYPE_VOLTAGE = 2, /* Unit: 1 mV */ + NT_SENSOR_TYPE_CURRENT = 3, /* Unit: 1 uA */ + NT_SENSOR_TYPE_POWER = 4, /* Unit: 0.1 uW */ + NT_SENSOR_TYPE_FAN = 5, /* Unit: 1 RPM (Revolutions Per Minute) */ + NT_SENSOR_TYPE_HIGH_POWER = 6, /* Unit: 1 mW */ + NT_SENSOR_TYPE_NUMBER = 7, +}; + +/* + * Generic SFP/SFP+/SFP28 sensors + * + * These sensors should be used instead of all adapter specific SFP sensors + * that have been deprecated.. + */ +enum nt_sensors_sfp { + NT_SENSOR_SFP_TEMP, + NT_SENSOR_SFP_SUPPLY, + NT_SENSOR_SFP_TX_BIAS, + NT_SENSOR_SFP_TX_POWER, + NT_SENSOR_SFP_RX_POWER, +}; + +/* + * Generic QSFP/QSFP+/QSFP28 sensors + * + * These sensors should be used instead of all adapter specific QSFP sensors + * that have been deprecated.. + */ +enum nt_sensors_qsfp { + NT_SENSOR_QSFP_TEMP, + NT_SENSOR_QSFP_SUPPLY, + NT_SENSOR_QSFP_TX_BIAS1, + NT_SENSOR_QSFP_TX_BIAS2, + NT_SENSOR_QSFP_TX_BIAS3, + NT_SENSOR_QSFP_TX_BIAS4, + NT_SENSOR_QSFP_TX_POWER1, + NT_SENSOR_QSFP_TX_POWER2, + NT_SENSOR_QSFP_TX_POWER3, + NT_SENSOR_QSFP_TX_POWER4, + NT_SENSOR_QSFP_RX_POWER1, + NT_SENSOR_QSFP_RX_POWER2, + NT_SENSOR_QSFP_RX_POWER3, + NT_SENSOR_QSFP_RX_POWER4, +}; + +typedef enum nt_sensor_type_e nt_sensor_type_t; + +/* + * Sensor subtypes + */ +enum nt_sensor_sub_type_e { + NT_SENSOR_SUBTYPE_NA = 0, + /* + * Subtype for NT_SENSOR_TYPE_POWER type on optical modules (optical modulation + * amplitude measured) + */ + NT_SENSOR_SUBTYPE_POWER_OMA, + /* Subtype for NT_SENSOR_TYPE_POWER type on optical modules (average power measured) */ + NT_SENSOR_SUBTYPE_POWER_AVERAGE, + /* Subtype for NT_SENSOR_TYPE_HIGH_POWER type on adapters (total power consumption) */ + NT_SENSOR_SUBTYPE_POWER_TOTAL +}; + +typedef enum nt_sensor_sub_type_e nt_sensor_sub_type_t; + +/* + * Sensor source + */ +enum nt_sensor_source_e { + NT_SENSOR_SOURCE_UNKNOWN = 0x00, /* Unknown source */ + /* + * Sensors located in a port. These are primary sensors - usually NIM temperature. Presence + * depends on adapter and NIM type. + */ + NT_SENSOR_SOURCE_PORT = + 0x01, + /* + * Level 1 sensors located in a port. These are secondary sensors - usually NIM supply + * voltage, Tx bias and Rx/Tx optical power. Presence depends on adapter and NIM type. + */ + NT_SENSOR_SOURCE_LEVEL1_PORT = + 0x02, +#ifndef DOXYGEN_INTERNAL_ONLY + NT_SENSOR_SOURCE_LEVEL2_PORT = + 0x04, /* Level 2 sensors located in a port */ +#endif + NT_SENSOR_SOURCE_ADAPTER = 0x08, /* Sensors mounted on the adapter */ + NT_SENSOR_SOURCE_LEVEL1_ADAPTER = + 0x10, /* Level 1 sensors mounted on the adapter */ +#ifndef DOXYGEN_INTERNAL_ONLY + NT_SENSOR_SOURCE_LEVEL2_ADAPTER = + 0x20, /* Level 2 sensors mounted on the adapter */ +#endif +}; + +/* + * Sensor state + */ +enum nt_sensor_state_e { + NT_SENSOR_STATE_UNKNOWN = 0, /* Unknown state */ + NT_SENSOR_STATE_INITIALIZING = 1, /* The sensor is initializing */ + NT_SENSOR_STATE_NORMAL = 2, /* Sensor values are within range */ + NT_SENSOR_STATE_ALARM = 3, /* Sensor values are out of range */ + NT_SENSOR_STATE_NOT_PRESENT = + 4 /* The sensor is not present, for example, SFP without diagnostics */ +}; + +typedef enum nt_sensor_state_e nt_sensor_state_t; + +/* + * Sensor value + */ +#define NT_SENSOR_NAN \ + (0x80000000) /* Indicates that sensor value or sensor limit is not valid (Not a Number) */ + +/* + * Master/Slave + */ +enum nt_bonding_type_e { + NT_BONDING_UNKNOWN, /* Unknown bonding type */ + NT_BONDING_MASTER, /* Adapter is master in the bonding */ + NT_BONDING_SLAVE, /* Adapter is slave in the bonding */ + NT_BONDING_PEER /* Adapter is bonded, but relationship is symmetric */ +}; + +enum nt_sensors_e { + /* Public sensors (Level 0) */ + NT_SENSOR_FPGA_TEMP, /* FPGA temperature sensor */ +}; + +/* + * Adapter types + */ +enum nt_adapter_type_e { + NT_ADAPTER_TYPE_UNKNOWN = 0, /* Unknown adapter type */ + NT_ADAPTER_TYPE_NT4E, /* NT4E network adapter */ + NT_ADAPTER_TYPE_NT20E, /* NT20E network adapter */ + NT_ADAPTER_TYPE_NT4E_STD, /* NT4E-STD network adapter */ + NT_ADAPTER_TYPE_NT4E_PORT, /* NTPORT4E expansion adapter */ + NT_ADAPTER_TYPE_NTBPE, /* NTBPE bypass adapter */ + NT_ADAPTER_TYPE_NT20E2, /* NT20E2 network adapter */ + NT_ADAPTER_TYPE_RESERVED1, /* Reserved */ + NT_ADAPTER_TYPE_RESERVED2, /* Reserved */ + NT_ADAPTER_TYPE_NT40E2_1, /* NT40E2-1 network adapter */ + NT_ADAPTER_TYPE_NT40E2_4, /* NT40E2-4 network adapter */ + NT_ADAPTER_TYPE_NT4E2_4T_BP, /* NT4E2-4T-BP bypass network adapter */ + NT_ADAPTER_TYPE_NT4E2_4_PTP, /* NT4E2-4 PTP network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT20E2_PTP, /* NT20E2 PTP network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT40E3_4_PTP, /* NT40E3 network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT100E3_1_PTP, /* NT100E3 network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT20E3_2_PTP, /* NT20E3 network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT80E3_2_PTP, /* NT80E3 network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT200E3_2, /* NT200E3 network adapter */ + NT_ADAPTER_TYPE_NT200A01, /* NT200A01 network adapter */ + NT_ADAPTER_TYPE_NT200A01_2X100 = + NT_ADAPTER_TYPE_NT200A01, /* NT200A01 2 x 100 Gbps network adapter */ + NT_ADAPTER_TYPE_NT40A01_4X1, /* NT40A01_4X1 network adapter with IEEE1588 */ + NT_ADAPTER_TYPE_NT200A01_2X40, /* NT200A01 2 x 40 Gbps network adapter */ + NT_ADAPTER_TYPE_NT80E3_2_PTP_8X10, /* NT80E3 8 x 10 Gbps network adapter with IEEE1588 */ + /* */ + NT_ADAPTER_TYPE_INTEL_A10_4X10, /* Intel PAC A10 GX 4 x 10 Gbps network adapter */ + NT_ADAPTER_TYPE_INTEL_A10_1X40, /* Intel PAC A10 GX 1 x 40 Gbps network adapter */ + /* */ + NT_ADAPTER_TYPE_NT200A01_8X10, /* NT200A01 8 x 10 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_2X100, /* NT200A02 2 x 100 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_2X40, /* NT200A02 2 x 40 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A01_2X25, /* Deprecated */ + NT_ADAPTER_TYPE_NT200A01_2X10_25 = + NT_ADAPTER_TYPE_NT200A01_2X25, /* NT200A01 2 x 10/25 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_2X25, /* Deprecated */ + NT_ADAPTER_TYPE_NT200A02_2X10_25 = + NT_ADAPTER_TYPE_NT200A02_2X25, /* NT200A02 2 x 10/25 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_4X25, /* Deprecated */ + NT_ADAPTER_TYPE_NT200A02_4X10_25 = + NT_ADAPTER_TYPE_NT200A02_4X25, /* NT200A02 4 x 10/25 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_8X10, /* NT200A02 8 x 10 Gbps network adapter */ + NT_ADAPTER_TYPE_NT50B01_2X25, /* Deprecated */ + NT_ADAPTER_TYPE_NT50B01_2X10_25 = + NT_ADAPTER_TYPE_NT50B01_2X25, /* NT50B01 2 x 10/25 Gbps network adapter */ + NT_ADAPTER_TYPE_NT200A02_2X1_10, /* NT200A02 2 x 1/10 Gbps network adapter */ + NT_ADAPTER_TYPE_NT100A01_4X1_10, /* NT100A01 4 x 1/10 Gbps network adapter */ + NT_ADAPTER_TYPE_NT100A01_4X10_25, /* NT100A01 4 x 10/25 Gbps network adapter */ + NT_ADAPTER_TYPE_NT50B01_2X1_10, /* NT50B01 2 x 1/10 Gbps network adapter */ + NT_ADAPTER_TYPE_NT40A11_4X1_10, /* NT40A11 4 x 1/10 Gbps network adapter */ +#ifndef DOXYGEN_INTERNAL_ONLY + NT_ADAPTER_TYPE_ML605 = 10000, /* NT20E2 eval board */ +#endif + NT_ADAPTER_TYPE_4GARCH_HAMOA = + (1U + << 29), /* Bit to mark to adapters as a 4GArch Hamoa adapter */ + NT_ADAPTER_TYPE_4GARCH = + (1U << 30), /* Bit to mark to adapters as a 4GArch adapter */ + /* NOTE: do *NOT* add normal adapters after the group bit mark enums */ +}; + +/* The NT200E3 adapter sensor id's */ +typedef enum nt_sensors_adapter_nt200_e3_e { + /* Public sensors (Level 0) */ + NT_SENSOR_NT200E3_FPGA_TEMP, /* FPGA temperature sensor */ + NT_SENSOR_NT200E3_FAN_SPEED, /* FAN speed sensor */ + /* MCU (Micro Controller Unit) temperature sensor located inside enclosure below FAN */ + NT_SENSOR_NT200E3_MCU_TEMP, + NT_SENSOR_NT200E3_PSU0_TEMP, /* Power supply 0 temperature sensor */ + NT_SENSOR_NT200E3_PSU1_TEMP, /* Power supply 1 temperature sensor */ + NT_SENSOR_NT200E3_PCB_TEMP, /* PCB temperature sensor */ + + /* Diagnostic sensors (Level 1) */ + /* Total power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200E3_NT200E3_POWER, + /* FPGA power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200E3_FPGA_POWER, + /* DDR4 RAM power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200E3_DDR4_POWER, + /* NIM power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200E3_NIM_POWER, + + NT_SENSOR_NT200E3_L1_MAX, /* Number of NT200E3 level 0,1 board sensors */ +} nt_sensors_adapter_nt200_e3_t; + +/* + * The following sensors are deprecated - generic types should be used instead + * The NIM temperature sensor must be the one with the lowest sensor_index + * (enum value) in order to be shown by the monitoring tool in port mode + */ +enum nt_sensors_port_nt200_e3_2_e { + /* Public sensors */ + NT_SENSOR_NT200E3_NIM, /* QSFP28 temperature sensor */ + + /* Diagnostic sensors (Level 1) */ + NT_SENSOR_NT200E3_SUPPLY, /* QSFP28 supply voltage sensor */ + NT_SENSOR_NT200E3_TX_BIAS1, /* QSFP28 TX bias line 0 current sensor */ + NT_SENSOR_NT200E3_TX_BIAS2, /* QSFP28 TX bias line 1 current sensor */ + NT_SENSOR_NT200E3_TX_BIAS3, /* QSFP28 TX bias line 2 current sensor */ + NT_SENSOR_NT200E3_TX_BIAS4, /* QSFP28 TX bias line 3 current sensor */ + NT_SENSOR_NT200E3_RX1, /* QSFP28 RX line 0 power sensor */ + NT_SENSOR_NT200E3_RX2, /* QSFP28 RX line 1 power sensor */ + NT_SENSOR_NT200E3_RX3, /* QSFP28 RX line 2 power sensor */ + NT_SENSOR_NT200E3_RX4, /* QSFP28 RX line 3 power sensor */ + NT_SENSOR_NT200E3_TX1, /* QSFP28 TX line 0 power sensor */ + NT_SENSOR_NT200E3_TX2, /* QSFP28 TX line 1 power sensor */ + NT_SENSOR_NT200E3_TX3, /* QSFP28 TX line 2 power sensor */ + NT_SENSOR_NT200E3_TX4, /* QSFP28 TX line 3 power sensor */ + NT_SENSOR_NT200E3_PORT_MAX, /* Number of NT200E3 port sensors */ +}; + +#endif diff --git a/drivers/net/ntnic/sensors/sensors.c b/drivers/net/ntnic/sensors/sensors.c new file mode 100644 index 0000000000..2a85843196 --- /dev/null +++ b/drivers/net/ntnic/sensors/sensors.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "sensors.h" +#include "ntlog.h" + +void sensor_deinit(struct nt_sensor_group *sg) +{ + if (sg) { + if (sg->sensor) + free(sg->sensor); + if (sg->monitor) + free(sg->monitor); + free(sg); + } +} + +struct nt_adapter_sensor * +allocate_sensor(uint8_t adapter_or_port_index, const char *p_name, + enum nt_sensor_source_e ssrc, enum nt_sensor_type_e type, + unsigned int index, enum nt_sensor_event_alarm_e event_alarm, + enum sensor_mon_sign si) +{ + struct nt_adapter_sensor *sensor = + (struct nt_adapter_sensor *)malloc(sizeof(struct nt_adapter_sensor)); + if (sensor == NULL) { + NT_LOG(ERR, ETHDEV, "%s: sensor is NULL", __func__); + return NULL; + } + + sensor->alarm = event_alarm; + sensor->m_enable_alarm = true; + sensor->m_intf_no = 0xFF; + sensor->m_adapter_no = 0xFF; + sensor->si = si; + + sensor->info.source = ssrc; + sensor->info.source_index = adapter_or_port_index; + sensor->info.sensor_index = index; + sensor->info.type = type; + sensor->info.sub_type = NT_SENSOR_SUBTYPE_NA; + sensor->info.state = NT_SENSOR_STATE_INITIALIZING; + sensor->info.value = NT_SENSOR_NAN; + sensor->info.value_lowest = NT_SENSOR_NAN; + sensor->info.value_highest = NT_SENSOR_NAN; + memset(sensor->info.name, 0, NT_INFO_SENSOR_NAME); + memcpy(sensor->info.name, p_name, + (strlen(p_name) > NT_INFO_SENSOR_NAME) ? NT_INFO_SENSOR_NAME : + strlen(p_name)); + sensor->info.name[NT_INFO_SENSOR_NAME] = '\0'; + + return sensor; +} + +void update_sensor_value(struct nt_adapter_sensor *sensor, int32_t value) +{ + if (sensor == NULL) + return; + sensor->info.value = value; + if (sensor->info.value_highest < value || + (unsigned int)sensor->info.value_highest == NT_SENSOR_NAN) + sensor->info.value_highest = value; + if (sensor->info.value_lowest > value || + (unsigned int)sensor->info.value_lowest == NT_SENSOR_NAN) + sensor->info.value_lowest = value; +} + +struct nt_adapter_sensor * +allocate_sensor_by_description(uint8_t adapter_or_port_index, + enum nt_sensor_source_e ssrc, + struct nt_adapter_sensor_description *descr) +{ + struct nt_adapter_sensor *sensor = + (struct nt_adapter_sensor *)malloc(sizeof(struct nt_adapter_sensor)); + if (sensor == NULL) { + NT_LOG(ERR, ETHDEV, "%s: sensor is NULL", __func__); + return NULL; + } + + sensor->alarm = descr->event_alarm; + sensor->m_enable_alarm = true; + sensor->m_intf_no = 0xFF; + sensor->m_adapter_no = 0xFF; + sensor->si = SENSOR_MON_UNSIGNED; + + sensor->info.source_index = adapter_or_port_index; + sensor->info.source = ssrc; + sensor->info.type = descr->type; + sensor->info.sensor_index = descr->index; + memset(sensor->info.name, 0, NT_INFO_SENSOR_NAME); + memcpy(sensor->info.name, descr->name, + (strlen(descr->name) > NT_INFO_SENSOR_NAME) ? + NT_INFO_SENSOR_NAME : + strlen(descr->name)); + sensor->info.name[NT_INFO_SENSOR_NAME] = '\0'; + + return sensor; +} + +void init_sensor_group(struct nt_sensor_group *sg) +{ + /* Set all pointers to NULL */ + sg->sensor = NULL; + sg->monitor = NULL; + sg->next = NULL; + sg->read = NULL; + sg->conv_func = NULL; +} + +/* Getters */ +int32_t get_value(struct nt_sensor_group *sg) +{ + return sg->sensor->info.value; +}; + +int32_t get_lowest(struct nt_sensor_group *sg) +{ + return sg->sensor->info.value_lowest; +}; + +int32_t get_highest(struct nt_sensor_group *sg) +{ + return sg->sensor->info.value_highest; +}; + +char *get_name(struct nt_sensor_group *sg) +{ + return sg->sensor->info.name; +}; + +/* Conversion functions */ +int null_signed(uint32_t p_sensor_result) +{ + return (int16_t)p_sensor_result; +} + +int null_unsigned(uint32_t p_sensor_result) +{ + return (uint16_t)p_sensor_result; +} + +/* + * ****************************************************************************** + * For EXAR7724: Convert a read Vch value to Napatech internal representation + * Doc: Vout = ReadVal * 0.015 (PRESCALE is accounted for) + * ****************************************************************************** + */ +int exar7724_vch(uint32_t p_sensor_result) +{ + return p_sensor_result * 15; /* NT unit: 1mV */ +} + +/* + * ****************************************************************************** + * For EXAR7724: Convert a read Vin value to Napatech internal representation + * Doc: Vout = ReadVal * 0.0125 + * ****************************************************************************** + */ +int exar7724_vin(uint32_t p_sensor_result) +{ + return (p_sensor_result * 25) / 2; /* NT unit: 1mV */ +} + +/* + * ****************************************************************************** + * For EXAR7724: Convert a read Tj value to Napatech internal representation + * Doc: Temp (in Kelvin) = (((ReadVal * 10mV) - 600mV) / (2mV/K)) + 300K = + * = ReadVal * 5K + * ****************************************************************************** + */ +int exar7724_tj(uint32_t p_sensor_result) +{ + /* + * A value of 2730 is used instead of 2732 which is more correct but since + * the temperature step is 5 degrees it is more natural to show these steps + */ + return p_sensor_result * 50 - 2730; /* NT unit: 0.1C */ +} + +/* + * ****************************************************************************** + * Conversion function for Linear Tecnology Linear_5s_11s format. + * The functions returns Y * 2**N, where N = b[15:11] is a 5-bit two's complement + * integer and Y = b[10:0] is an 11-bit two's complement integer. + * The multiplier value is used for scaling to Napatech units. + * ****************************************************************************** + */ +static int conv5s_11s(uint16_t value, int multiplier) +{ + int n, y; + + y = value & 0x07FF; + + if (value & 0x0400) + y -= 0x0800; /* The MSBit is a sign bit */ + + n = (value >> 11) & 0x1F; + + if (n & 0x10) + n -= 0x20; /* The MSBit is a sign bit */ + + y *= multiplier; + + if (n > 0) + y *= (1 << n); + + else if (n < 0) + y /= (1 << (-n)); + + return y; +} + +/* + * ****************************************************************************** + * Temperature conversion from Linear_5s_11s format. + * ****************************************************************************** + */ +int ltm4676_tj(uint32_t p_sensor_result) +{ + return (uint16_t)conv5s_11s(p_sensor_result, 10); /* NT unit: 0.1C */ +} + +/* + * ****************************************************************************** + * For MP2886a: Convert a read Tj value to Napatech internal representation + * ****************************************************************************** + */ +int mp2886a_tj(uint32_t p_sensor_result) +{ + /* + * MPS-2886p: READ_TEMPERATURE (register 0x8Dh) + * READ_TEMPERATURE is a 2-byte, unsigned integer. + */ + return (uint16_t)p_sensor_result; /* NT unit: 0.1C */ +} + +/* + * ****************************************************************************** + * For MAX6642: Convert a read temperature value to Napatech internal representation + * ****************************************************************************** + */ +int max6642_t(uint32_t p_sensor_result) +{ + if ((p_sensor_result >> 8) == 0xFF) + return NT_SENSOR_NAN; + + /* The six lower bits are not used */ + return (int)(((p_sensor_result >> 6) * 5) / + 2); /* NT unit: 0.25 deg, Native unit: 0.1C */ +} + +/* + * ****************************************************************************** + * For DS1775: Convert a read temperature value to Napatech internal representation + * ****************************************************************************** + */ +int ds1775_t(uint32_t p_sensor_result) +{ + return (p_sensor_result * 10) / + 256; /* NT unit: 0.1 deg, Native unit: 1/256 C */ +} + +/* + * ****************************************************************************** + * For FAN: Convert a tick count to RPM + * NT unit: RPM, Native unit: 2 ticks/revolution + * ****************************************************************************** + */ +int fan(uint32_t p_sensor_result) +{ + return (p_sensor_result * 60U / 4); +} diff --git a/drivers/net/ntnic/sensors/sensors.h b/drivers/net/ntnic/sensors/sensors.h new file mode 100644 index 0000000000..1424b8bc83 --- /dev/null +++ b/drivers/net/ntnic/sensors/sensors.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _SENSORS_H +#define _SENSORS_H + +#include "sensor_types.h" +#include "stream_info.h" +#include "nthw_platform_drv.h" +#include "nthw_drv.h" +#include "nthw_spi_v3.h" +#include "nthw_fpga_model.h" + +#include +#include +#include +#include +#include +#include "avr_intf.h" + +enum nt_sensor_event_alarm_e { + NT_SENSOR_ENABLE_ALARM, + NT_SENSOR_LOG_ALARM, + NT_SENSOR_DISABLE_ALARM, +}; + +/* + * Sensor Class types + */ +enum nt_sensor_class_e { + NT_SENSOR_CLASS_FPGA = + 0, /* Class for FPGA based sensors e.g FPGA temperature */ + NT_SENSOR_CLASS_MCU = + 1, /* Class for MCU based sensors e.g MCU temperature */ + NT_SENSOR_CLASS_PSU = + 2, /* Class for PSU based sensors e.g PSU temperature */ + NT_SENSOR_CLASS_PCB = + 3, /* Class for PCB based sensors e.g PCB temperature */ + NT_SENSOR_CLASS_NIM = + 4, /* Class for NIM based sensors e.g NIM temperature */ + NT_SENSOR_CLASS_ANY = 5, /* Class for ANY sensors e.g any sensors */ +}; + +typedef enum nt_sensor_class_e nt_sensor_class_t; + +/* + * Port of the sensor class + */ +struct nt_adapter_sensor { + uint8_t m_adapter_no; + uint8_t m_intf_no; + uint8_t fpga_idx; /* for AVR sensors */ + enum sensor_mon_sign si; + struct nt_info_sensor_s info; + enum nt_sensor_event_alarm_e alarm; + bool m_enable_alarm; +}; + +struct nt_fpga_sensor_monitor { + nt_fpga_t *fpga; + nt_module_t *mod; + + nt_register_t *reg; + nt_field_t **fields; + uint8_t fields_num; +}; + +/* + * Sensor description. + * Describe the static behavior of the sensor. + */ +struct nt_adapter_sensor_description { + enum nt_sensor_type_e type; /* Sensor type. */ + enum nt_sensor_sub_type_e sub_type; /* Sensor subtype (if any applicable) */ + unsigned int index; /* Sensor group index. */ + enum nt_sensor_event_alarm_e event_alarm; /* Enable/Disable event alarm */ + char name[20]; /* Sensor name. */ +}; + +struct nt_sensor_group { + struct nt_adapter_sensor *sensor; + struct nt_fpga_sensor_monitor *monitor; + void (*read)(struct nt_sensor_group *sg, nthw_spis_t *t_spi); + + /* conv params are needed to call current conversion functions */ + int (*conv_func)(uint32_t p_sensor_result); + /* i2c interface for NIM sensors */ + + struct nt_sensor_group *next; +}; + +void init_sensor_group(struct nt_sensor_group *sg); + +void update_sensor_value(struct nt_adapter_sensor *sensor, int32_t value); + +void sensor_deinit(struct nt_sensor_group *sg); + +/* getters */ +int32_t get_value(struct nt_sensor_group *sg); +int32_t get_lowest(struct nt_sensor_group *sg); +int32_t get_highest(struct nt_sensor_group *sg); +char *get_name(struct nt_sensor_group *sg); + +struct nt_adapter_sensor * +allocate_sensor(uint8_t adapter_or_port_index, const char *p_name, + enum nt_sensor_source_e ssrc, enum nt_sensor_type_e type, + unsigned int index, enum nt_sensor_event_alarm_e event_alarm, + enum sensor_mon_sign si); +struct nt_adapter_sensor * +allocate_sensor_by_description(uint8_t adapter_or_port_index, + enum nt_sensor_source_e ssrc, + struct nt_adapter_sensor_description *descr); + +/* conversion functions */ +int null_signed(uint32_t p_sensor_result); +int null_unsigned(uint32_t p_sensor_result); +int exar7724_tj(uint32_t p_sensor_result); +int max6642_t(uint32_t p_sensor_result); +int ds1775_t(uint32_t p_sensor_result); +int ltm4676_tj(uint32_t p_sensor_result); +int exar7724_vch(uint32_t p_sensor_result); +int exar7724_vin(uint32_t p_sensor_result); +int mp2886a_tj(uint32_t p_sensor_result); +int fan(uint32_t p_sensor_result); + +#endif /* _SENSORS_H */ diff --git a/drivers/net/ntnic/sensors/stream_info.h b/drivers/net/ntnic/sensors/stream_info.h new file mode 100644 index 0000000000..b94231fd8b --- /dev/null +++ b/drivers/net/ntnic/sensors/stream_info.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef _STREAM_INFO_H +#define _STREAM_INFO_H + +#include "sensor_types.h" + +#include + +/* + * This structure will return the sensor specific information + * + * The units used for the fields: value, value_lowest, value_highest, limit_low and + * limit_high depend on the type field. See @ref nt_sensor_type_e. + * + * For the limit_low and limit_high fields the following applies:\n + * If the sensor is located in a NIM (Network Interface Module), the limits are read + * from the NIM module via the DMI (Diagnostic Monitoring Interface) from the alarm + * and warning thresholds section, and the units are changed to internal representation. + * Only the alarm thresholds are used and are read only once during initialization. + * The limits cannot be changed. + * + * The value field is updated internally on a regular basis and is also based on a + * value read from the NIM which is also changed to internal representation. + * + * Not all NIM types support DMI data, and its presence must be determined by reading an + * option flag. In general, a NIM can read out: temperature, supply voltage, + * TX bias, TX optical power and RX optical power but not all NIM types support all + * 5 values. + * + * If external calibration is used (most NIM use internal calibration), both the + * current value and the threshold values are subjected to the specified calibration + * along with the change to internal calibration. + */ +#define NT_INFO_SENSOR_NAME 50 +struct nt_info_sensor_s { + enum nt_sensor_source_e + source; /* The source of the sensor (port or adapter on which the sensor resides) */ + /* + * The source index - the adapter number for adapter sensors and port number for port + * sensors + */ + uint32_t source_index; + /* + * The sensor index within the source index (sensor number on the adapter or sensor number + * on the port) + */ + uint32_t sensor_index; + enum nt_sensor_type_e type; /* The sensor type */ + enum nt_sensor_sub_type_e sub_type; /* The sensor subtype (if applicable) */ + enum nt_sensor_state_e state; /* The current state (normal or alarm) */ + int32_t value; /* The current value */ + int32_t value_lowest; /* The lowest value registered */ + int32_t value_highest; /* The highest value registered */ + char name[NT_INFO_SENSOR_NAME + 1]; /* The sensor name */ + enum nt_adapter_type_e + adapter_type; /* The adapter type where the sensor resides */ +}; + +/* The NT200A02 adapter sensor id's */ +enum nt_sensors_adapter_nt200a02_e { + /* Public sensors (Level 0) */ + NT_SENSOR_NT200A02_FPGA_TEMP, /* FPGA temperature sensor */ + NT_SENSOR_NT200A02_FAN_SPEED, /* FAN speed sensor */ + + NT_SENSOR_NT200A02_MCU_TEMP, + NT_SENSOR_NT200A02_PSU0_TEMP, /* Power supply 0 temperature sensor */ + NT_SENSOR_NT200A02_PSU1_TEMP, /* Power supply 1 temperature sensor */ + NT_SENSOR_NT200A02_PCB_TEMP, /* PCB temperature sensor */ + + /* Diagnostic sensors (Level 1) */ + /* Total power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200A02_NT200A02_POWER, + /* FPGA power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200A02_FPGA_POWER, + /* DDR4 RAM power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200A02_DDR4_POWER, + /* NIM power consumption (calculated value) - does not generate alarms */ + NT_SENSOR_NT200A02_NIM_POWER, + + NT_SENSOR_NT200A02_L1_MAX, /* Number of NT200A01 level 0,1 board sensors */ +}; + +#endif -- 2.39.3