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 99CB04411D; Fri, 31 May 2024 17:50:36 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 19CBA42DC0; Fri, 31 May 2024 17:48:14 +0200 (CEST) Received: from egress-ip11a.ess.de.barracuda.com (egress-ip11a.ess.de.barracuda.com [18.184.203.234]) by mails.dpdk.org (Postfix) with ESMTP id 0AD1642DD4 for ; Fri, 31 May 2024 17:48:02 +0200 (CEST) Received: from EUR04-VI1-obe.outbound.protection.outlook.com (mail-vi1eur04lp2042.outbound.protection.outlook.com [104.47.14.42]) by mx-outbound13-64.eu-central-1a.ess.aws.cudaops.com (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Fri, 31 May 2024 15:47:58 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=ceHJ2fscJssFzZHagH5rAqRW6O58TmTyZlIRYmuIQLn8NbgaJAttlTW6GpvFLgxS5ijoiMFWzJQkRrh0WmEGiR7meyGczuy+fhD5HKXx6cuiyhOMyWKYlmbhxzjD18Er6kWSdrxIIvPE1sxJwf75cHui3PLxlooN5iliauY2lf5y11p4zDw091bmOOYJMoHi0anUOy4F0FX51fZQkj71B+cChAjlQal5IXjFUjO+G7YI/Ign6cNWqtmMX4nLdZcegrQNObxk4IeW/WuuDQYb/o0uOboBdT0lzRtrLqyO8FdC21cS5aZKHkWLVkOShgHt8r9p1T8OrlF4FC7+oEvwdA== 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=m77mgn434vqZLRJf+5+fFiNqFa/3l5mzZBBjhz/Rews=; b=TeZdrOTgd0U6l9UDoa4ejEORRSyX0THWLFaqsa2TOye6MFtYxt1aC1pybYVHZhBsiLA0FdVWlV9fCOEjP1cts3ca8CBjSbRYupp2C0Dj8uWD8LoD/aMo/MfCIwN9uB40WotCM4fTKUfSek1OLkyRMuvd98QimPZMoL1DD6hsEYrwZ7bED3fdxz6fowOaaWzw2lb+oEcI7kdy+G3d4K64jQSePVY7XIPEe5YOJsJf1MdS9SgijfCBbCKNnXZBrSpaLYza5r33sgvQBz76Twu5CuZebKuMRmTqHhfVCWzIuIUrBaZt+Ycno0oRfrYekw3SFxz2/riJ/cwmjrj1/jCiMQ== 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=reject sp=reject pct=100) action=oreject header.from=napatech.com; dkim=none (message not signed); arc=none (0) 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=m77mgn434vqZLRJf+5+fFiNqFa/3l5mzZBBjhz/Rews=; b=HaBI4rBzPpTdogvUHunErQU3fRdYYXfYu6dwg9JhZltooiowx6YwNB6wqgvjXikZBm8cpZEKoV3veDN0xKSYaF+wQ1RQaz6sZv8DieYePjIN390xGB1FfdswAru0Iu7oiN4DgBf40uIjLa19AmXrxkLvC90ZRRWXtqVP8tl1QVY= Received: from AS4P251CA0020.EURP251.PROD.OUTLOOK.COM (2603:10a6:20b:5d3::9) by AM0P190MB0593.EURP190.PROD.OUTLOOK.COM (2603:10a6:208:193::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7633.21; Fri, 31 May 2024 15:47:47 +0000 Received: from AMS1EPF00000047.eurprd04.prod.outlook.com (2603:10a6:20b:5d3:cafe::5f) by AS4P251CA0020.outlook.office365.com (2603:10a6:20b:5d3::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7633.23 via Frontend Transport; Fri, 31 May 2024 15:47:47 +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=oreject 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; Received: from localhost.localdomain (178.72.21.4) by AMS1EPF00000047.mail.protection.outlook.com (10.167.16.135) with Microsoft SMTP Server id 15.20.7633.15 via Frontend Transport; Fri, 31 May 2024 15:47:47 +0000 From: Serhii Iliushyk To: dev@dpdk.org Cc: mko-plv@napatech.com, ckm@napatech.com, andrew.rybchenko@oktetlabs.ru, ferruh.yigit@amd.com Subject: [PATCH v2 16/17] net/ntnic: add link 100G module Date: Fri, 31 May 2024 17:47:20 +0200 Message-ID: <20240531154731.1856874-16-sil-plv@napatech.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240531154731.1856874-1-sil-plv@napatech.com> References: <20240530144929.4127931-1-sil-plv@napatech.com> <20240531154731.1856874-1-sil-plv@napatech.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AMS1EPF00000047:EE_|AM0P190MB0593:EE_ Content-Type: text/plain X-MS-Office365-Filtering-Correlation-Id: 7c8d8bc7-b53b-4908-5db1-08dc8189050d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230031|376005|1800799015|36860700004|82310400017; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?yRPkCWhzi1HYpOfffeKS1TC1rxuFCP2Gl5gk2rehNJz8dIff04jn1WxVUe/a?= =?us-ascii?Q?G4EQV7qjS1I7yezMtMM61NrxiT6VGkv9wMklBxIM2hFAF7Whyevs8dbPEEpT?= =?us-ascii?Q?Edp6Sx4sxeNbAUqTjYvFaEOXjNrOYykArw35gqlTOYnRf2nLtWLcdD7r5VQS?= =?us-ascii?Q?NoZ+kggm+Ax4+Zretn+vwqNU5wrutvFsofFk8bAqn7ZyjBzgc93ipVDKrjbI?= =?us-ascii?Q?XOYbQgCQAC1LXAKKExe1BFr+6iHteSNjRGuXQSw0UgohiiLXq2VvpyKjPi7B?= =?us-ascii?Q?3K4A3tQG2vaY8ciYj7qIurb/2LJs6WPcg9KWCV8txnpWp/No70Cpi7UYKsWi?= =?us-ascii?Q?9L1KB+uVadtQIbRGvHErRztsiG5g4icbTLKH2olG2KBhjpBZoOavZlgKxyEL?= =?us-ascii?Q?bajD3gOLQm2ZSxl7hElhWdCPozVQ0n2dkjmD+kbuE83LM27ZMbh5sxlyhakc?= =?us-ascii?Q?5AnVsuJsw5LNvzTojfFIOX14HDtAt4FRxTg7h7+HlYOpRgtMN8brRUreV13O?= =?us-ascii?Q?g3pGHHP/c6+Iie8ZJ/4nyE49bU//n0DYHgQAxgCY8VpdUWEyRxNbS4eeuqFF?= =?us-ascii?Q?ilbKGWhLcDuBxdugOsVc36FWBhF0KxSgk+nlD/jxESX8Wvt6UtTkfPeCQvo4?= =?us-ascii?Q?lIx3IdpqhsWEsMuYZ7jM8sws/VFB5Wtjqy7dzvWrESTXHTI51ya7flc4Ez3D?= =?us-ascii?Q?b3D6L8V7Gs4OSQ4ndNY+1G7X5wgAgWgTdRvhi92Zkz3wtdx3WlHRULez8vQQ?= =?us-ascii?Q?R4nGk7yc3DiQMMKOb0Kp8OpiFpCzAVJ9VFr2EvCROB922NLUnEcDximsMch7?= =?us-ascii?Q?3FxJ04KQopEHTq45AcxrPxIHQylSTdA6zxAZmF5y0XlzFTmO703H4GxwY07j?= =?us-ascii?Q?J0s1/viQwGUeG442k/OWVa0hFe4wIMqJo+o9/DdUi8aBk9zDsFDxUNAZk8HX?= =?us-ascii?Q?cC3jA+bFcweAOX81Ho/Jd1UWBCf+sQOwTC4C52sLHWCWN8O6iLcc0zXsl8t6?= =?us-ascii?Q?3XYhPoRBYM2ACRfRRMJ4BQZ+P+XpPN6N9Q+59a8AJcp/IbbVMXJwP0KB8kJs?= =?us-ascii?Q?JO9lk4UwnQKqUS/6aZnY0n/6fsI6XAIOjzhAsM2h5iP/OyTXtQflklMgY31q?= =?us-ascii?Q?Rs7JoEao6YCBtWh9NKmoe/cMDg2i7yYhrQAek/lR6yDVCoUj+taNY07/TPAN?= =?us-ascii?Q?14rriMnKPJoLt4OciSMuuuFCCJGaSoOaw8bW+4bxpfvFwBfszism2lm78U34?= =?us-ascii?Q?Kl4ZEsX2MGgOjo6MnSW6yjT8ZWrTddKwTtn0SSYzP3qrj9zukGHLBGFystnk?= =?us-ascii?Q?2Yg1ntRX/KQwLLxOuYJPALKF?= X-Forefront-Antispam-Report: CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:localhost.localdomain; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230031)(376005)(1800799015)(36860700004)(82310400017); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: 1Rok3YL+ydzQplOUHcC4YIJk38Y5RmtduB8SE0qSD749PzgAlh/sCPG6+O3n3HYCKnmBWF76oZT1mDc7SBc7kUoBLY+x9MSPlvvFBLjYUjaFKel14Us2XTziJ7+gEMChDJNzlTAVr2qw/ylFeI3z8XafZZBnbULK51GGnMF5Wcj5U+eLekTrfVjaCEV/tcD0MlVOcqmoBl6H5F0SUHkvuZqA6GqKwOdh21VjWYgmy30Dbaz9y5okmso61+y4CcUQHr0L7E+EoicF6hjTmiZS7Eo50ujvRCkaaFSM+Egz2N99s0CnxfCMxV1U+wi81w5btZY3rx5XCbe0tlbzgx8dD6YdYoDJQeXi6kQI5KuxPYMTDV2ZdqYW3aq23NAKqIghle3tYU5sVgDGlRDtNZwq+hI7wSxhMS8gH3sZhDT/O5cjQCxgSVf+O6rdBXqYzdZLV79gUuC5S7J6dxdEQ/slWmfFJJdV39edJBtAkjpsuaWhVilqXy1KBtC98AdFTQ4kMEN9IzGWnLGzcQveQIKZEwY1cVnsY3/JWAJwTeQM7cnogX35/a3wahMMuZDOhgeQETeIqyWQPLdX+nh+oGO86zSojU1CR5Lx67PChalCI9dpavjvKosN8AFGFbLqfVA11woVep6iYP9O/ttrXwJfnw== X-OriginatorOrg: napatech.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 31 May 2024 15:47:47.3782 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 7c8d8bc7-b53b-4908-5db1-08dc8189050d 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] X-MS-Exchange-CrossTenant-AuthSource: AMS1EPF00000047.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0P190MB0593 X-BESS-ID: 1717170470-303392-12642-39835-2 X-BESS-VER: 2019.1_20240530.1612 X-BESS-Apparent-Source-IP: 104.47.14.42 X-BESS-Parts: H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVuZmJoYGQGYGUDQxJcUk0TTV3N TYxMzIJNXAKCUt0TTRJMXS1CDVwsw4Tak2FgD7+SeYQgAAAA== X-BESS-Outbound-Spam-Score: 0.00 X-BESS-Outbound-Spam-Report: Code version 3.2, rules version 3.2.2.256627 [from cloudscan23-24.eu-central-1b.ess.aws.cudaops.com] Rule breakdown below pts rule name description ---- ---------------------- -------------------------------- 0.00 BSF_BESS_OUTBOUND META: BESS Outbound X-BESS-Outbound-Spam-Status: SCORE=0.00 using account:ESS113687 scores of KILL_LEVEL=7.0 tests=BSF_BESS_OUTBOUND 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 Add ntnic 100G link support. Signed-off-by: Serhii Iliushyk --- v2: * Fixed WARNING:REPEATED_WORD --- drivers/net/ntnic/include/nt4ga_link.h | 8 + .../link_mgmt/link_100g/nt4ga_link_100g.c | 840 ++++++++++++++++ .../link_mgmt/link_100g/nt4ga_link_100g.h | 11 + .../net/ntnic/nthw/core/include/nthw_core.h | 2 + .../ntnic/nthw/core/include/nthw_gpio_phy.h | 50 + .../ntnic/nthw/core/include/nthw_mac_pcs.h | 263 +++++ drivers/net/ntnic/nthw/core/nthw_gpio_phy.c | 144 +++ drivers/net/ntnic/nthw/core/nthw_mac_pcs.c | 950 ++++++++++++++++++ 8 files changed, 2268 insertions(+) create mode 100644 drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c create mode 100644 drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h create mode 100644 drivers/net/ntnic/nthw/core/nthw_gpio_phy.c create mode 100644 drivers/net/ntnic/nthw/core/nthw_mac_pcs.c diff --git a/drivers/net/ntnic/include/nt4ga_link.h b/drivers/net/ntnic/include/nt4ga_link.h index 49e1c5d672..1d7565f614 100644 --- a/drivers/net/ntnic/include/nt4ga_link.h +++ b/drivers/net/ntnic/include/nt4ga_link.h @@ -115,8 +115,16 @@ typedef struct port_action_s { 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_gfg_t gfg[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 { diff --git a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c new file mode 100644 index 0000000000..a429b96450 --- /dev/null +++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c @@ -0,0 +1,840 @@ +/* + * 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 "ntnic_mod_reg.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); +static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nthw_fpga_t *fpga); + +/* + * 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 + */ + +/* + * Init 100G link ops variables + */ +static struct link_ops_s link_100g_ops = { + .link_init = nt4ga_link_100g_ports_init, +}; + +static void __attribute__((constructor(65535))) link_100g_init(void) +{ + register_100g_link_ops(&link_100g_ops); +} + +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) { + snprintf(lsbuf[adapter_no][port], sizeof(lsbuf[adapter_no][port]), "%s", + buf); + 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, nthw_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, nthw_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; + nthw_gfg_t *gfg_mod_p; + + 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]; + gfg_mod_p = &link_info->u.var100g.gfg[port]; + + /* + * Phase 1. Pre-state machine (`port init` functions) + * 1.1) nt4ga_adapter::port_init() + */ + + /* 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_sr4[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_sr4[port][lane][0]; + diff = tuning_sr4[port][lane][1]; + post = tuning_sr4[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); + + (void)gfg_mod_p;/* unused; */ + + nthw_mac_pcs_set_host_loopback(mac_pcs, false); + + return res; +} + +static void read_nim_sensors(adapter_info_t *drv, int port) +{ + if (drv->nim_sensors[port] != 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[port]; ptr != NULL; + ptr = ptr->next) { + if (ptr->read) + ptr->read(ptr, t_spi); + } + + nthw_spis_delete(t_spi); + } + } +} + +/* + * 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; + nthw_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 */ + read_nim_sensors(drv, i); + + /* Has the administrative port state changed? */ + assert(!(disable_port && enable_port)); + + if (disable_port) { + memset(&link_state[i], 0, sizeof(link_state[i])); + link_info->link_info[i].link_speed = NT_LINK_SPEED_UNKNOWN; + 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_info->link_info[i].link_speed = + (new_link_state.link_up ? NT_LINK_SPEED_100G + : NT_LINK_SPEED_UNKNOWN); + 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). + */ +static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nthw_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_gfg_t *gfg_mod = p_adapter_info->nt4ga_link.u.var100g.gfg; + nthw_gpio_phy_t *gpio_phy = p_adapter_info->nt4ga_link.u.var100g.gpio_phy; + int i; + + for (i = 0; i < nb_ports; i++) { + /* 2 + adapter port number */ + const uint8_t instance = (uint8_t)(2U + i); + res = nthw_mac_pcs_init(&mac_pcs[i], fpga, i /* int n_instance */); + + 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; + nim_ctx[i].type = I2C_HWIIC; + res = nthw_gfg_init(&gfg_mod[i], fpga, 0 /* Only one instance */); + + if (res != 0) + break; + + 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.speed_capa = NT_LINK_SPEED_100G; + 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/link_mgmt/link_100g/nt4ga_link_100g.h b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h new file mode 100644 index 0000000000..0e6103d9a0 --- /dev/null +++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h @@ -0,0 +1,11 @@ +/* + * 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" + +#endif /* NT4GA_LINK_100G_H_ */ diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h b/drivers/net/ntnic/nthw/core/include/nthw_core.h index b72766cc5c..ba7d85b92a 100644 --- a/drivers/net/ntnic/nthw/core/include/nthw_core.h +++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h @@ -21,6 +21,8 @@ #include "nthw_gfg.h" #include "nthw_gmf.h" +#include "nthw_gpio_phy.h" +#include "nthw_mac_pcs.h" #include "nthw_mac_pcs_xxv.h" #include "nthw_mac_tfg.h" #include "nthw_sdc.h" diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h new file mode 100644 index 0000000000..2cc8b65eab --- /dev/null +++ b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NTHW_GPIO_PHY_H_ +#define NTHW_GPIO_PHY_H_ + +#define GPIO_PHY_INTERFACES (2) + +typedef struct { + nthw_field_t *cfg_fld_lp_mode; /* Cfg Low Power Mode */ + nthw_field_t *cfg_int; /* Cfg Port Interrupt */ + nthw_field_t *cfg_reset;/* Cfg Reset */ + nthw_field_t *cfg_mod_prs; /* Cfg Module Present */ + nthw_field_t *cfg_pll_int; /* Cfg PLL Interrupt */ + nthw_field_t *cfg_port_rxlos; /* Emulate Cfg Port RXLOS */ + + nthw_field_t *gpio_fld_lp_mode; /* Gpio Low Power Mode */ + nthw_field_t *gpio_int; /* Gpio Port Interrupt */ + nthw_field_t *gpio_reset; /* Gpio Reset */ + nthw_field_t *gpio_mod_prs; /* Gpio Module Present */ + nthw_field_t *gpio_pll_int; /* Gpio PLL Interrupt */ + nthw_field_t *gpio_port_rxlos; /* Emulate Gpio Port RXLOS */ +} gpio_phy_fields_t; + +struct nthw_gpio_phy { + nthw_fpga_t *mp_fpga; + nthw_module_t *mp_mod_gpio_phy; + int mn_instance; + + /* Registers */ + nthw_register_t *mp_reg_config; + nthw_register_t *mp_reg_gpio; + + /* Fields */ + gpio_phy_fields_t mpa_fields[GPIO_PHY_INTERFACES]; +}; + +typedef struct nthw_gpio_phy nthw_gpio_phy_t; +typedef struct nthw_gpio_phy nthw_gpio_phy; + +nthw_gpio_phy_t *nthw_gpio_phy_new(void); +int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int n_instance); + +bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no); +void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool enable); +void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable); + +#endif /* NTHW_GPIO_PHY_H_ */ diff --git a/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h b/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h new file mode 100644 index 0000000000..9243389bef --- /dev/null +++ b/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h @@ -0,0 +1,263 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#ifndef NTHW_MAC_PCS_H_ +#define NTHW_MAC_PCS_H_ + +enum nthw_mac_pcs_led_mode_e { + NTHW_MAC_PCS_LED_AUTO = 0x00, + NTHW_MAC_PCS_LED_ON = 0x01, + NTHW_MAC_PCS_LED_OFF = 0x02, + NTHW_MAC_PCS_LED_PORTID = 0x03, +}; + +#define nthw_mac_pcs_receiver_mode_dfe (0) +#define nthw_mac_pcs_receiver_mode_lpm (1) + +struct nthw_mac_pcs { + uint8_t m_port_no; + + nthw_fpga_t *mp_fpga; + nthw_module_t *mp_mod_mac_pcs; + int mn_instance; + + /* Block lock status */ + nthw_field_t *mp_fld_block_lock_lock; + uint32_t m_fld_block_lock_lock_mask; + + /* Lane lock status */ + nthw_field_t *mp_fld_vl_demuxed_lock; + uint32_t m_fld_vl_demuxed_lock_mask; + + /* GTY_STAT */ + nthw_field_t *mp_fld_gty_stat_rx_rst_done0; + nthw_field_t *mp_fld_gty_stat_rx_rst_done1; + nthw_field_t *mp_fld_gty_stat_rx_rst_done2; + nthw_field_t *mp_fld_gty_stat_rx_rst_done3; + nthw_field_t *mp_fld_gty_stat_tx_rst_done0; + nthw_field_t *mp_fld_gty_stat_tx_rst_done1; + nthw_field_t *mp_fld_gty_stat_tx_rst_done2; + nthw_field_t *mp_fld_gty_stat_tx_rst_done3; + uint32_t m_fld_gty_stat_rx_rst_done_mask; + uint32_t m_fld_gty_stat_tx_rst_done_mask; + + /* GTY_LOOP */ + nthw_register_t *mp_reg_gty_loop; + nthw_field_t *mp_fld_gty_loop_gt_loop0; + nthw_field_t *mp_fld_gty_loop_gt_loop1; + nthw_field_t *mp_fld_gty_loop_gt_loop2; + nthw_field_t *mp_fld_gty_loop_gt_loop3; + + /* MAC_PCS_CONFIG */ + nthw_field_t *mp_fld_pcs_config_tx_path_rst; + nthw_field_t *mp_fld_pcs_config_rx_path_rst; + nthw_field_t *mp_fld_pcs_config_rx_enable; + nthw_field_t *mp_fld_pcs_config_rx_force_resync; + nthw_field_t *mp_fld_pcs_config_rx_test_pattern; + nthw_field_t *mp_fld_pcs_config_tx_enable; + nthw_field_t *mp_fld_pcs_config_tx_send_idle; + nthw_field_t *mp_fld_pcs_config_tx_send_rfi; + nthw_field_t *mp_fld_pcs_config_tx_test_pattern; + + /* STAT PCS */ + nthw_field_t *mp_fld_stat_pcs_rx_status; + nthw_field_t *mp_fld_stat_pcs_rx_aligned; + nthw_field_t *mp_fld_stat_pcs_rx_aligned_err; + nthw_field_t *mp_fld_stat_pcs_rx_misaligned; + nthw_field_t *mp_fld_stat_pcs_rx_internal_local_fault; + nthw_field_t *mp_fld_stat_pcs_rx_received_local_fault; + nthw_field_t *mp_fld_stat_pcs_rx_local_fault; + nthw_field_t *mp_fld_stat_pcs_rx_remote_fault; + nthw_field_t *mp_fld_stat_pcs_rx_hi_ber; + + /* STAT_PCS_RX_LATCH */ + nthw_field_t *mp_fld_stat_pcs_rx_latch_status; + + /* PHYMAC_MISC */ + nthw_field_t *mp_fld_phymac_misc_tx_sel_host; + nthw_field_t *mp_fld_phymac_misc_tx_sel_tfg; + nthw_field_t *mp_fld_phymac_misc_tx_sel_rx_loop; + nthw_field_t *mp_fld_phymac_misc_ts_eop; + + /* LINK_SUMMARY */ + nthw_register_t *mp_reg_link_summary; + nthw_field_t *mp_fld_link_summary_abs; + nthw_field_t *mp_fld_link_summary_nt_phy_link_state; + nthw_field_t *mp_fld_link_summary_lh_abs; + nthw_field_t *mp_fld_link_summary_ll_nt_phy_link_state; + nthw_field_t *mp_fld_link_summary_link_down_cnt; + nthw_field_t *mp_fld_link_summary_nim_interr; + nthw_field_t *mp_fld_link_summary_lh_local_fault; + nthw_field_t *mp_fld_link_summary_lh_remote_fault; + nthw_field_t *mp_fld_link_summary_local_fault; + nthw_field_t *mp_fld_link_summary_remote_fault; + + /* BIP_ERR */ + nthw_register_t *mp_reg_bip_err; + nthw_field_t *mp_fld_reg_bip_err_bip_err; + + /* FEC_CTRL */ + nthw_register_t *mp_reg_fec_ctrl; + nthw_field_t *mp_field_fec_ctrl_reg_rs_fec_ctrl_in; + + /* FEC_STAT */ + nthw_register_t *mp_reg_fec_stat; + nthw_field_t *mp_field_fec_stat_bypass; + nthw_field_t *mp_field_fec_stat_valid; + nthw_field_t *mp_field_fec_stat_am_lock0; + nthw_field_t *mp_field_fec_stat_am_lock1; + nthw_field_t *mp_field_fec_stat_am_lock2; + nthw_field_t *mp_field_fec_stat_am_lock3; + nthw_field_t *mp_field_fec_stat_fec_lane_algn; + + /* FEC Corrected code word count */ + nthw_register_t *mp_reg_fec_cw_cnt; + nthw_field_t *mp_field_fec_cw_cnt_cw_cnt; + + /* FEC Uncorrected code word count */ + nthw_register_t *mp_reg_fec_ucw_cnt; + nthw_field_t *mp_field_fec_ucw_cnt_ucw_cnt; + + /* GTY_RX_BUF_STAT */ + nthw_register_t *mp_reg_gty_rx_buf_stat; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat0; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat1; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat2; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat3; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed0; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed1; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed2; + nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed3; + + /* GTY_PRE_CURSOR */ + nthw_register_t *mp_reg_gty_pre_cursor; + nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr0; + nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr1; + nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr2; + nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr3; + + /* GTY_DIFF_CTL */ + nthw_register_t *mp_reg_gty_diff_ctl; + nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl0; + nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl1; + nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl2; + nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl3; + + /* GTY_POST_CURSOR */ + nthw_register_t *mp_reg_gty_post_cursor; + nthw_field_t *mp_field_gty_post_cursor_tx_post_csr0; + nthw_field_t *mp_field_gty_post_cursor_tx_post_csr1; + nthw_field_t *mp_field_gty_post_cursor_tx_post_csr2; + nthw_field_t *mp_field_gty_post_cursor_tx_post_csr3; + + /* GTY_CTL */ + nthw_register_t *mp_reg_gty_ctl; + nthw_register_t *mp_reg_gty_ctl_tx; + nthw_field_t *mp_field_gty_ctl_tx_pol0; + nthw_field_t *mp_field_gty_ctl_tx_pol1; + nthw_field_t *mp_field_gty_ctl_tx_pol2; + nthw_field_t *mp_field_gty_ctl_tx_pol3; + nthw_field_t *mp_field_gty_ctl_rx_pol0; + nthw_field_t *mp_field_gty_ctl_rx_pol1; + nthw_field_t *mp_field_gty_ctl_rx_pol2; + nthw_field_t *mp_field_gty_ctl_rx_pol3; + nthw_field_t *mp_field_gty_ctl_rx_lpm_en0; + nthw_field_t *mp_field_gty_ctl_rx_lpm_en1; + nthw_field_t *mp_field_gty_ctl_rx_lpm_en2; + nthw_field_t *mp_field_gty_ctl_rx_lpm_en3; + nthw_field_t *mp_field_gty_ctl_rx_equa_rst0; + nthw_field_t *mp_field_gty_ctl_rx_equa_rst1; + nthw_field_t *mp_field_gty_ctl_rx_equa_rst2; + nthw_field_t *mp_field_gty_ctl_rx_equa_rst3; + + /* DEBOUNCE_CTRL */ + nthw_register_t *mp_reg_debounce_ctrl; + nthw_field_t *mp_field_debounce_ctrl_nt_port_ctrl; + + /* TIMESTAMP_COMP */ + nthw_register_t *mp_reg_time_stamp_comp; + nthw_field_t *mp_field_time_stamp_comp_rx_dly; + nthw_field_t *mp_field_time_stamp_comp_tx_dly; + + /* STAT_PCS_RX */ + nthw_register_t *mp_reg_stat_pcs_rx; + + /* STAT_PCS_RX */ + nthw_register_t *mp_reg_stat_pcs_rx_latch; + + /* PHYMAC_MISC */ + nthw_register_t *mp_reg_phymac_misc; + + /* BLOCK_LOCK */ + nthw_register_t *mp_reg_block_lock; +}; + +typedef struct nthw_mac_pcs nthw_mac_pcs_t; +typedef struct nthw_mac_pcs nthw_mac_pcs; + +nthw_mac_pcs_t *nthw_mac_pcs_new(void); +int nthw_mac_pcs_init(nthw_mac_pcs_t *p, nthw_fpga_t *p_fpga, int n_instance); +void nthw_mac_pcs_delete(nthw_mac_pcs_t *p); + +void nthw_mac_pcs_tx_path_rst(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_rx_path_rst(nthw_mac_pcs_t *p, bool enable); +bool nthw_mac_pcs_is_rx_path_rst(nthw_mac_pcs_t *p); +bool nthw_mac_pcs_is_ddr3_calib_done(nthw_mac_pcs_t *p); +/* wrapper - for ease of use */ +void nthw_mac_pcs_tx_host_enable(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_rx_enable(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_tx_enable(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_tx_sel_host(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_tx_sel_tfg(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_ts_eop(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_host_loopback(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_set_line_loopback(nthw_mac_pcs_t *p, bool enable); +void nthw_mac_pcs_reset_bip_counters(nthw_mac_pcs_t *p); +void nthw_mac_pcs_get_status(nthw_mac_pcs_t *p, uint8_t *status); +bool nthw_mac_pcs_get_hi_ber(nthw_mac_pcs_t *p); + +void nthw_mac_pcs_get_link_summary1(nthw_mac_pcs_t *p, + uint32_t *p_status, + uint32_t *p_status_latch, + uint32_t *p_aligned, + uint32_t *p_local_fault, + uint32_t *p_remote_fault); + +void nthw_mac_pcs_get_link_summary(nthw_mac_pcs_t *p, + uint32_t *p_abs, + uint32_t *p_nt_phy_link_state, + uint32_t *p_lh_abs, + uint32_t *p_ll_nt_phy_link_state, + uint32_t *p_link_down_cnt, + uint32_t *p_nim_interr, + uint32_t *p_lh_local_fault, + uint32_t *p_lh_remote_fault, + uint32_t *p_local_fault, + uint32_t *p_remote_fault); + +bool nthw_mac_pcs_reset_required(nthw_mac_pcs_t *p); +void nthw_mac_pcs_set_fec(nthw_mac_pcs_t *p, bool enable); +bool nthw_mac_pcs_get_fec_bypass(nthw_mac_pcs_t *p); +bool nthw_mac_pcs_get_fec_valid(nthw_mac_pcs_t *p); +bool nthw_mac_pcs_get_fec_stat_all_am_locked(nthw_mac_pcs_t *p); +void nthw_mac_pcs_reset_fec_counters(nthw_mac_pcs_t *p); +void nthw_mac_pcs_set_gty_tx_tuning(nthw_mac_pcs_t *p, + uint8_t lane, + uint8_t tx_pre_csr, + uint8_t tx_diff_ctl, + uint8_t tx_post_csr); +void nthw_mac_pcs_swap_gty_tx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap); +void nthw_mac_pcs_swap_gty_rx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap); +void nthw_mac_pcs_set_receiver_equalization_mode(nthw_mac_pcs_t *p, uint8_t mode); +void nthw_mac_pcs_set_led_mode(nthw_mac_pcs_t *p, uint8_t mode); +void nthw_mac_pcs_set_timestamp_comp_rx(nthw_mac_pcs_t *p, uint16_t rx_dly); +void nthw_mac_pcs_set_port_no(nthw_mac_pcs_t *p, uint8_t port_no); + +uint32_t nthw_mac_pcs_get_fld_block_lock_lock(nthw_mac_pcs_t *p); +uint32_t nthw_mac_pcs_get_fld_block_lock_lock_mask(nthw_mac_pcs_t *p); +uint32_t nthw_mac_pcs_get_fld_lane_lock_lock(nthw_mac_pcs_t *p); +uint32_t nthw_mac_pcs_get_fld_lane_lock_lock_mask(nthw_mac_pcs_t *p); + +#endif /* NTHW_MAC_PCS_H_ */ diff --git a/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c new file mode 100644 index 0000000000..c67977341b --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c @@ -0,0 +1,144 @@ +/* + * 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_gpio_phy.h" + +int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int n_instance) +{ + nthw_module_t *p_mod = nthw_fpga_query_module(p_fpga, MOD_GPIO_PHY, n_instance); + + if (p == NULL) + return p_mod == NULL ? -1 : 0; + + if (p_mod == NULL) { + NT_LOG(ERR, NTHW, "%s: GPIO_PHY %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_gpio_phy = p_mod; + + /* Registers */ + p->mp_reg_config = nthw_module_get_register(p->mp_mod_gpio_phy, GPIO_PHY_CFG); + p->mp_reg_gpio = nthw_module_get_register(p->mp_mod_gpio_phy, GPIO_PHY_GPIO); + + /* PORT-0, config fields */ + p->mpa_fields[0].cfg_fld_lp_mode = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_LPMODE); + p->mpa_fields[0].cfg_int = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_INT_B); + p->mpa_fields[0].cfg_reset = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_RESET_B); + p->mpa_fields[0].cfg_mod_prs = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_MODPRS_B); + + /* PORT-0, Non-mandatory fields (query_field) */ + p->mpa_fields[0].cfg_pll_int = + nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_PORT0_PLL_INTR); + p->mpa_fields[0].cfg_port_rxlos = + nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_E_PORT0_RXLOS); + + /* PORT-1, config fields */ + p->mpa_fields[1].cfg_fld_lp_mode = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_LPMODE); + p->mpa_fields[1].cfg_int = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_INT_B); + p->mpa_fields[1].cfg_reset = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_RESET_B); + p->mpa_fields[1].cfg_mod_prs = + nthw_register_get_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_MODPRS_B); + + /* PORT-1, Non-mandatory fields (query_field) */ + p->mpa_fields[1].cfg_pll_int = + nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_PORT1_PLL_INTR); + p->mpa_fields[1].cfg_port_rxlos = + nthw_register_query_field(p->mp_reg_config, GPIO_PHY_CFG_E_PORT1_RXLOS); + + /* PORT-0, gpio fields */ + p->mpa_fields[0].gpio_fld_lp_mode = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_LPMODE); + p->mpa_fields[0].gpio_int = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_INT_B); + p->mpa_fields[0].gpio_reset = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_RESET_B); + p->mpa_fields[0].gpio_mod_prs = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_MODPRS_B); + + /* PORT-0, Non-mandatory fields (query_field) */ + p->mpa_fields[0].gpio_pll_int = + nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT0_PLL_INTR); + p->mpa_fields[0].gpio_port_rxlos = + nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_E_PORT0_RXLOS); + + /* PORT-1, gpio fields */ + p->mpa_fields[1].gpio_fld_lp_mode = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_LPMODE); + p->mpa_fields[1].gpio_int = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_INT_B); + p->mpa_fields[1].gpio_reset = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_RESET_B); + p->mpa_fields[1].gpio_mod_prs = + nthw_register_get_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_MODPRS_B); + + /* PORT-1, Non-mandatory fields (query_field) */ + p->mpa_fields[1].gpio_pll_int = + nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_PORT1_PLL_INTR); + p->mpa_fields[1].gpio_port_rxlos = + nthw_register_query_field(p->mp_reg_gpio, GPIO_PHY_GPIO_E_PORT1_RXLOS); + + nthw_register_update(p->mp_reg_config); + + return 0; +} + +bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no) +{ + if (if_no >= ARRAY_SIZE(p->mpa_fields)) { + assert(false); + return false; + } + + /* NOTE: This is a negated GPIO PIN "MODPRS_B" */ + return nthw_field_get_updated(p->mpa_fields[if_no].gpio_mod_prs) == 0U ? true : false; +} + +void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool enable) +{ + if (if_no >= ARRAY_SIZE(p->mpa_fields)) { + assert(false); + return; + } + + if (enable) + nthw_field_set_flush(p->mpa_fields[if_no].gpio_fld_lp_mode); + + else + nthw_field_clr_flush(p->mpa_fields[if_no].gpio_fld_lp_mode); + + nthw_field_clr_flush(p->mpa_fields[if_no].cfg_fld_lp_mode); /* enable output */ +} + +void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable) +{ + if (if_no >= ARRAY_SIZE(p->mpa_fields)) { + assert(false); + return; + } + + if (enable) + nthw_field_clr_flush(p->mpa_fields[if_no].gpio_reset); + + else + nthw_field_set_flush(p->mpa_fields[if_no].gpio_reset); + + nthw_field_clr_flush(p->mpa_fields[if_no].cfg_reset); /* enable output */ +} diff --git a/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c new file mode 100644 index 0000000000..1e2c9587b5 --- /dev/null +++ b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c @@ -0,0 +1,950 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Napatech A/S + */ + +#include "nt_util.h" +#include "ntlog.h" + +#include "nthw_drv.h" +#include "nthw_register.h" + +#include "nthw_mac_pcs.h" + +#define NTHW_MAC_PCS_LANES (20) + +static const uint8_t c_pcs_lanes = NTHW_MAC_PCS_LANES; +static const uint8_t c_mac_pcs_receiver_mode_dfe; + +nthw_mac_pcs_t *nthw_mac_pcs_new(void) +{ + nthw_mac_pcs_t *p = malloc(sizeof(nthw_mac_pcs_t)); + + if (p) + memset(p, 0, sizeof(nthw_mac_pcs_t)); + + return p; +} + +void nthw_mac_pcs_delete(nthw_mac_pcs_t *p) +{ + if (p) { + memset(p, 0, sizeof(nthw_mac_pcs_t)); + free(p); + } +} + +/* + * Parameters: + * p != NULL: init struct pointed to by p + * p == NULL: check fpga module(s) presence (but no struct to init) + * + * Return value: + * <0: if p == NULL then fpga module(s) is/are not present. + * if p != NULL then fpga module(s) is/are not present, struct undefined + * ==0: if p == NULL then fpga module(s) is/are present (no struct to init) + * : if p != NULL then fpga module(s) is/are present and struct initialized + */ +int nthw_mac_pcs_init(nthw_mac_pcs_t *p, nthw_fpga_t *p_fpga, int n_instance) +{ + nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_MAC_PCS, n_instance); + + if (p == NULL) + return mod == NULL ? -1 : 0; + + if (mod == NULL) { + NT_LOG(ERR, NTHW, "%s: MAC_PCS %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_mac_pcs = mod; + + assert(n_instance >= 0 && n_instance <= 255); + nthw_mac_pcs_set_port_no(p, (uint8_t)n_instance); + + { + nthw_register_t *p_reg_block_lock, *p_reg_stat_pcs_rx, *p_reg_stat_pcs_rx_latch; + nthw_register_t *p_reg_vl_demuxed, *p_reg_gty_stat, *p_reg_pcs_config, + *p_reg_phymac_misc; + const int product_id = nthw_fpga_get_product_id(p_fpga); + + p_reg_block_lock = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_BLOCK_LOCK); + p->mp_reg_block_lock = p_reg_block_lock; + p->mp_fld_block_lock_lock = + nthw_register_get_field(p_reg_block_lock, MAC_PCS_BLOCK_LOCK_LOCK); + + p_reg_stat_pcs_rx = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_STAT_PCS_RX); + p->mp_reg_stat_pcs_rx = p_reg_stat_pcs_rx; + p->mp_fld_stat_pcs_rx_status = + nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_STATUS); + p->mp_fld_stat_pcs_rx_aligned = + nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_ALIGNED); + p->mp_fld_stat_pcs_rx_aligned_err = + nthw_register_get_field(p_reg_stat_pcs_rx, + MAC_PCS_STAT_PCS_RX_ALIGNED_ERR); + p->mp_fld_stat_pcs_rx_misaligned = + nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_MISALIGNED); + p->mp_fld_stat_pcs_rx_internal_local_fault = + nthw_register_get_field(p_reg_stat_pcs_rx, + MAC_PCS_STAT_PCS_RX_INTERNAL_LOCAL_FAULT); + p->mp_fld_stat_pcs_rx_received_local_fault = + nthw_register_get_field(p_reg_stat_pcs_rx, + MAC_PCS_STAT_PCS_RX_RECEIVED_LOCAL_FAULT); + p->mp_fld_stat_pcs_rx_local_fault = + nthw_register_get_field(p_reg_stat_pcs_rx, + MAC_PCS_STAT_PCS_RX_LOCAL_FAULT); + p->mp_fld_stat_pcs_rx_remote_fault = + nthw_register_get_field(p_reg_stat_pcs_rx, + MAC_PCS_STAT_PCS_RX_REMOTE_FAULT); + p->mp_fld_stat_pcs_rx_hi_ber = + nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_HI_BER); + + p_reg_stat_pcs_rx_latch = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_STAT_PCS_RX_LATCH); + p->mp_reg_stat_pcs_rx_latch = p_reg_stat_pcs_rx_latch; + p->mp_fld_stat_pcs_rx_latch_status = + nthw_register_get_field(p_reg_stat_pcs_rx_latch, + MAC_PCS_STAT_PCS_RX_LATCH_STATUS); + + p_reg_vl_demuxed = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_VL_DEMUXED); + p->mp_fld_vl_demuxed_lock = + nthw_register_get_field(p_reg_vl_demuxed, MAC_PCS_VL_DEMUXED_LOCK); + + p_reg_gty_stat = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_STAT); + p->mp_fld_gty_stat_tx_rst_done0 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_0); + p->mp_fld_gty_stat_tx_rst_done1 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_1); + p->mp_fld_gty_stat_tx_rst_done2 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_2); + p->mp_fld_gty_stat_tx_rst_done3 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_3); + p->mp_fld_gty_stat_rx_rst_done0 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_0); + p->mp_fld_gty_stat_rx_rst_done1 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_1); + p->mp_fld_gty_stat_rx_rst_done2 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_2); + p->mp_fld_gty_stat_rx_rst_done3 = + nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_3); + + p->m_fld_block_lock_lock_mask = 0; + p->m_fld_vl_demuxed_lock_mask = 0; + p->m_fld_gty_stat_tx_rst_done_mask = 0; + p->m_fld_gty_stat_rx_rst_done_mask = 0; + + if (product_id == 9563) { + /* NT200A01_2X100 implements 20 virtual lanes */ + p->m_fld_block_lock_lock_mask = (1 << 20) - 1; + /* NT200A01_2X100 implements 20 virtual lanes */ + p->m_fld_vl_demuxed_lock_mask = (1 << 20) - 1; + p->m_fld_gty_stat_tx_rst_done_mask = + 1; /* NT200A01_2X100 implements 4 GTY */ + p->m_fld_gty_stat_rx_rst_done_mask = + 1; /* NT200A01_2X100 implements 4 GTY */ + + } else { + /* Remember to add new product_ids */ + assert(0); + } + + p_reg_pcs_config = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_MAC_PCS_CONFIG); + p->mp_fld_pcs_config_tx_path_rst = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_TX_PATH_RST); + p->mp_fld_pcs_config_rx_path_rst = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_RX_PATH_RST); + p->mp_fld_pcs_config_rx_enable = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_RX_ENABLE); + p->mp_fld_pcs_config_rx_force_resync = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_RX_FORCE_RESYNC); + p->mp_fld_pcs_config_rx_test_pattern = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_RX_TEST_PATTERN); + p->mp_fld_pcs_config_tx_enable = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_TX_ENABLE); + p->mp_fld_pcs_config_tx_send_idle = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_TX_SEND_IDLE); + p->mp_fld_pcs_config_tx_send_rfi = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_TX_SEND_RFI); + p->mp_fld_pcs_config_tx_test_pattern = + nthw_register_get_field(p_reg_pcs_config, + MAC_PCS_MAC_PCS_CONFIG_TX_TEST_PATTERN); + + p->mp_reg_gty_loop = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_LOOP); + p->mp_fld_gty_loop_gt_loop0 = + nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_0); + p->mp_fld_gty_loop_gt_loop1 = + nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_1); + p->mp_fld_gty_loop_gt_loop2 = + nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_2); + p->mp_fld_gty_loop_gt_loop3 = + nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_3); + + p_reg_phymac_misc = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_PHYMAC_MISC); + p->mp_reg_phymac_misc = p_reg_phymac_misc; + p->mp_fld_phymac_misc_tx_sel_host = + nthw_register_get_field(p_reg_phymac_misc, + MAC_PCS_PHYMAC_MISC_TX_SEL_HOST); + p->mp_fld_phymac_misc_tx_sel_tfg = + nthw_register_get_field(p_reg_phymac_misc, MAC_PCS_PHYMAC_MISC_TX_SEL_TFG); + p->mp_fld_phymac_misc_tx_sel_rx_loop = + nthw_register_get_field(p_reg_phymac_misc, + MAC_PCS_PHYMAC_MISC_TX_SEL_RX_LOOP); + + /* SOP or EOP TIMESTAMP */ + p->mp_fld_phymac_misc_ts_eop = + nthw_register_query_field(p_reg_phymac_misc, MAC_PCS_PHYMAC_MISC_TS_EOP); + + p->mp_reg_link_summary = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_LINK_SUMMARY); + p->mp_fld_link_summary_abs = + nthw_register_get_field(p->mp_reg_link_summary, MAC_PCS_LINK_SUMMARY_ABS); + p->mp_fld_link_summary_nt_phy_link_state = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_NT_PHY_LINK_STATE); + p->mp_fld_link_summary_lh_abs = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LH_ABS); + p->mp_fld_link_summary_ll_nt_phy_link_state = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LL_PHY_LINK_STATE); + p->mp_fld_link_summary_link_down_cnt = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LINK_DOWN_CNT); + p->mp_fld_link_summary_nim_interr = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_NIM_INTERR); + p->mp_fld_link_summary_lh_local_fault = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LH_LOCAL_FAULT); + p->mp_fld_link_summary_lh_remote_fault = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LH_REMOTE_FAULT); + p->mp_fld_link_summary_local_fault = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_LOCAL_FAULT); + p->mp_fld_link_summary_remote_fault = + nthw_register_get_field(p->mp_reg_link_summary, + MAC_PCS_LINK_SUMMARY_REMOTE_FAULT); + + p->mp_reg_bip_err = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_BIP_ERR); + p->mp_fld_reg_bip_err_bip_err = + nthw_register_get_field(p->mp_reg_bip_err, MAC_PCS_BIP_ERR_BIP_ERR); + + p->mp_reg_fec_ctrl = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_CTRL); + p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in = + nthw_register_get_field(p->mp_reg_fec_ctrl, + MAC_PCS_FEC_CTRL_RS_FEC_CTRL_IN); + + p->mp_reg_fec_stat = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_STAT); + p->mp_field_fec_stat_bypass = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_BYPASS); + p->mp_field_fec_stat_valid = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_VALID); + p->mp_field_fec_stat_am_lock0 = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_0); + p->mp_field_fec_stat_am_lock1 = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_1); + p->mp_field_fec_stat_am_lock2 = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_2); + p->mp_field_fec_stat_am_lock3 = + nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_3); + p->mp_field_fec_stat_fec_lane_algn = + nthw_register_get_field(p->mp_reg_fec_stat, + MAC_PCS_FEC_STAT_FEC_LANE_ALGN); + + p->mp_reg_fec_cw_cnt = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_CW_CNT); + p->mp_field_fec_cw_cnt_cw_cnt = + nthw_register_get_field(p->mp_reg_fec_cw_cnt, MAC_PCS_FEC_CW_CNT_CW_CNT); + + p->mp_reg_fec_ucw_cnt = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_UCW_CNT); + p->mp_field_fec_ucw_cnt_ucw_cnt = + nthw_register_get_field(p->mp_reg_fec_ucw_cnt, + MAC_PCS_FEC_UCW_CNT_UCW_CNT); + + /* GTY_RX_BUF_STAT */ +#ifdef RXBUFSTAT + p->mp_reg_gty_rx_buf_stat = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_RX_BUF_STAT); + p->mp_field_gty_rx_buf_stat_rx_buf_stat0 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_0); + p->mp_field_gty_rx_buf_stat_rx_buf_stat1 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_1); + p->mp_field_gty_rx_buf_stat_rx_buf_stat2 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_2); + p->mp_field_gty_rx_buf_stat_rx_buf_stat3 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_3); + p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed0 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_0); + p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed1 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_1); + p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed2 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_2); + p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed3 = + nthw_register_get_field(p->mp_reg_gty_rx_buf_stat, + MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_3); +#endif + + /* GTY_PRE_CURSOR */ + p->mp_reg_gty_pre_cursor = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_PRE_CURSOR); + p->mp_field_gty_pre_cursor_tx_pre_csr0 = + nthw_register_get_field(p->mp_reg_gty_pre_cursor, + MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_0); + p->mp_field_gty_pre_cursor_tx_pre_csr1 = + nthw_register_get_field(p->mp_reg_gty_pre_cursor, + MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_1); + p->mp_field_gty_pre_cursor_tx_pre_csr2 = + nthw_register_get_field(p->mp_reg_gty_pre_cursor, + MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_2); + p->mp_field_gty_pre_cursor_tx_pre_csr3 = + nthw_register_get_field(p->mp_reg_gty_pre_cursor, + MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_3); + + /* GTY_DIFF_CTL */ + p->mp_reg_gty_diff_ctl = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_DIFF_CTL); + p->mp_field_gty_gty_diff_ctl_tx_diff_ctl0 = + nthw_register_get_field(p->mp_reg_gty_diff_ctl, + MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_0); + p->mp_field_gty_gty_diff_ctl_tx_diff_ctl1 = + nthw_register_get_field(p->mp_reg_gty_diff_ctl, + MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_1); + p->mp_field_gty_gty_diff_ctl_tx_diff_ctl2 = + nthw_register_get_field(p->mp_reg_gty_diff_ctl, + MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_2); + p->mp_field_gty_gty_diff_ctl_tx_diff_ctl3 = + nthw_register_get_field(p->mp_reg_gty_diff_ctl, + MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_3); + + /* GTY_POST_CURSOR */ + p->mp_reg_gty_post_cursor = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_POST_CURSOR); + p->mp_field_gty_post_cursor_tx_post_csr0 = + nthw_register_get_field(p->mp_reg_gty_post_cursor, + MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_0); + p->mp_field_gty_post_cursor_tx_post_csr1 = + nthw_register_get_field(p->mp_reg_gty_post_cursor, + MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_1); + p->mp_field_gty_post_cursor_tx_post_csr2 = + nthw_register_get_field(p->mp_reg_gty_post_cursor, + MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_2); + p->mp_field_gty_post_cursor_tx_post_csr3 = + nthw_register_get_field(p->mp_reg_gty_post_cursor, + MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_3); + + /* GTY_CTL */ + p->mp_reg_gty_ctl = nthw_module_query_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL); + + if (p->mp_reg_gty_ctl) { + p->mp_field_gty_ctl_tx_pol0 = + nthw_register_get_field(p->mp_reg_gty_ctl, + MAC_PCS_GTY_CTL_TX_POLARITY_0); + p->mp_field_gty_ctl_tx_pol1 = + nthw_register_get_field(p->mp_reg_gty_ctl, + MAC_PCS_GTY_CTL_TX_POLARITY_1); + p->mp_field_gty_ctl_tx_pol2 = + nthw_register_get_field(p->mp_reg_gty_ctl, + MAC_PCS_GTY_CTL_TX_POLARITY_2); + p->mp_field_gty_ctl_tx_pol3 = + nthw_register_get_field(p->mp_reg_gty_ctl, + MAC_PCS_GTY_CTL_TX_POLARITY_3); + + } else { + p->mp_reg_gty_ctl = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL_RX); + p->mp_reg_gty_ctl_tx = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL_TX); + p->mp_field_gty_ctl_tx_pol0 = + nthw_register_get_field(p->mp_reg_gty_ctl_tx, + MAC_PCS_GTY_CTL_TX_POLARITY_0); + p->mp_field_gty_ctl_tx_pol1 = + nthw_register_get_field(p->mp_reg_gty_ctl_tx, + MAC_PCS_GTY_CTL_TX_POLARITY_1); + p->mp_field_gty_ctl_tx_pol2 = + nthw_register_get_field(p->mp_reg_gty_ctl_tx, + MAC_PCS_GTY_CTL_TX_POLARITY_2); + p->mp_field_gty_ctl_tx_pol3 = + nthw_register_get_field(p->mp_reg_gty_ctl_tx, + MAC_PCS_GTY_CTL_TX_POLARITY_3); + } + + p->mp_field_gty_ctl_rx_pol0 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_0); + p->mp_field_gty_ctl_rx_pol1 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_1); + p->mp_field_gty_ctl_rx_pol2 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_2); + p->mp_field_gty_ctl_rx_pol3 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_3); + p->mp_field_gty_ctl_rx_lpm_en0 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_0); + p->mp_field_gty_ctl_rx_lpm_en1 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_1); + p->mp_field_gty_ctl_rx_lpm_en2 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_2); + p->mp_field_gty_ctl_rx_lpm_en3 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_3); + p->mp_field_gty_ctl_rx_equa_rst0 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_0); + p->mp_field_gty_ctl_rx_equa_rst1 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_1); + p->mp_field_gty_ctl_rx_equa_rst2 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_2); + p->mp_field_gty_ctl_rx_equa_rst3 = + nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_3); + + /* DEBOUNCE_CTRL */ + p->mp_reg_debounce_ctrl = + nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_DEBOUNCE_CTRL); + p->mp_field_debounce_ctrl_nt_port_ctrl = + nthw_register_get_field(p->mp_reg_debounce_ctrl, + MAC_PCS_DEBOUNCE_CTRL_NT_PORT_CTRL); + + p->mp_reg_time_stamp_comp = + nthw_module_query_register(p->mp_mod_mac_pcs, MAC_PCS_TIMESTAMP_COMP); + + if (p->mp_reg_time_stamp_comp) { + /* TIMESTAMP_COMP */ + p->mp_field_time_stamp_comp_rx_dly = + nthw_register_get_field(p->mp_reg_time_stamp_comp, + MAC_PCS_TIMESTAMP_COMP_RX_DLY); + p->mp_field_time_stamp_comp_tx_dly = + nthw_register_get_field(p->mp_reg_time_stamp_comp, + MAC_PCS_TIMESTAMP_COMP_TX_DLY); + } + } + return 0; +} + +/* wrapper - for ease of use */ +void nthw_mac_pcs_tx_host_enable(nthw_mac_pcs_t *p, bool enable) +{ + nthw_mac_pcs_set_tx_sel_host(p, enable); + nthw_mac_pcs_set_tx_sel_tfg(p, !enable); +} + +void nthw_mac_pcs_set_rx_enable(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_pcs_config_rx_enable); + + if (enable) + nthw_field_set_flush(p->mp_fld_pcs_config_rx_enable); + + else + nthw_field_clr_flush(p->mp_fld_pcs_config_rx_enable); +} + +void nthw_mac_pcs_set_tx_enable(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_pcs_config_tx_enable); + + if (enable) + nthw_field_set_flush(p->mp_fld_pcs_config_tx_enable); + + else + nthw_field_clr_flush(p->mp_fld_pcs_config_tx_enable); +} + +void nthw_mac_pcs_set_tx_sel_host(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_phymac_misc_tx_sel_host); + + if (enable) + nthw_field_set_flush(p->mp_fld_phymac_misc_tx_sel_host); + + else + nthw_field_clr_flush(p->mp_fld_phymac_misc_tx_sel_host); +} + +void nthw_mac_pcs_set_tx_sel_tfg(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_phymac_misc_tx_sel_tfg); + + if (enable) + nthw_field_set_flush(p->mp_fld_phymac_misc_tx_sel_tfg); + + else + nthw_field_clr_flush(p->mp_fld_phymac_misc_tx_sel_tfg); +} + +void nthw_mac_pcs_set_ts_eop(nthw_mac_pcs_t *p, bool enable) +{ + if (p->mp_fld_phymac_misc_ts_eop) { + nthw_field_get_updated(p->mp_fld_phymac_misc_ts_eop); + + if (enable) + nthw_field_set_flush(p->mp_fld_phymac_misc_ts_eop); + + else + nthw_field_clr_flush(p->mp_fld_phymac_misc_ts_eop); + } +} + +void nthw_mac_pcs_tx_path_rst(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_pcs_config_tx_path_rst); + + if (enable) + nthw_field_set_flush(p->mp_fld_pcs_config_tx_path_rst); + + else + nthw_field_clr_flush(p->mp_fld_pcs_config_tx_path_rst); +} + +void nthw_mac_pcs_rx_path_rst(nthw_mac_pcs_t *p, bool enable) +{ + nthw_field_get_updated(p->mp_fld_pcs_config_rx_path_rst); + + if (enable) + nthw_field_set_flush(p->mp_fld_pcs_config_rx_path_rst); + + else + nthw_field_clr_flush(p->mp_fld_pcs_config_rx_path_rst); +} + +bool nthw_mac_pcs_is_rx_path_rst(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_fld_pcs_config_rx_path_rst); +} + +void nthw_mac_pcs_set_host_loopback(nthw_mac_pcs_t *p, bool enable) +{ + nthw_register_update(p->mp_reg_gty_loop); + + if (enable) { + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 2); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 2); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 2); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 2); + + } else { + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 0); + } + + nthw_register_flush(p->mp_reg_gty_loop, 1); +} + +void nthw_mac_pcs_set_line_loopback(nthw_mac_pcs_t *p, bool enable) +{ + nthw_register_update(p->mp_reg_gty_loop); + + if (enable) { + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 4); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 4); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 4); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 4); + + } else { + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 0); + nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 0); + } + + nthw_register_flush(p->mp_reg_gty_loop, 1); +} + +void nthw_mac_pcs_reset_bip_counters(nthw_mac_pcs_t *p) +{ + uint32_t lane_bit_errors[NTHW_MAC_PCS_LANES]; + nthw_register_update(p->mp_reg_bip_err); + nthw_field_get_val(p->mp_fld_reg_bip_err_bip_err, (uint32_t *)lane_bit_errors, + ARRAY_SIZE(lane_bit_errors)); + +#if defined(DEBUG) + { + uint8_t lane; + + for (lane = 0; lane < c_pcs_lanes; lane++) { + if (lane_bit_errors[lane]) { + NT_LOG(DBG, NTHW, "Port %u: pcsLane %2u: BIP8 errors: %u\n", + p->m_port_no, lane, lane_bit_errors[lane]); + } + } + } +#else + (void)c_pcs_lanes; /* unused - kill warning */ +#endif +} + +void nthw_mac_pcs_get_status(nthw_mac_pcs_t *p, uint8_t *status) +{ + *status = nthw_field_get_updated(p->mp_fld_stat_pcs_rx_status) & 0x01; +} + +bool nthw_mac_pcs_get_hi_ber(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_fld_stat_pcs_rx_hi_ber); +} + +void nthw_mac_pcs_get_link_summary(nthw_mac_pcs_t *p, + uint32_t *p_abs, + uint32_t *p_nt_phy_link_state, + uint32_t *p_lh_abs, + uint32_t *p_ll_nt_phy_link_state, + uint32_t *p_link_down_cnt, + uint32_t *p_nim_interr, + uint32_t *p_lh_local_fault, + uint32_t *p_lh_remote_fault, + uint32_t *p_local_fault, + uint32_t *p_remote_fault) +{ + nthw_register_update(p->mp_reg_link_summary); + + if (p_abs) + *p_abs = nthw_field_get_val32(p->mp_fld_link_summary_abs); + + if (p_nt_phy_link_state) { + *p_nt_phy_link_state = + nthw_field_get_val32(p->mp_fld_link_summary_nt_phy_link_state); + } + + if (p_lh_abs) + *p_lh_abs = nthw_field_get_val32(p->mp_fld_link_summary_lh_abs); + + if (p_ll_nt_phy_link_state) { + *p_ll_nt_phy_link_state = + nthw_field_get_val32(p->mp_fld_link_summary_ll_nt_phy_link_state); + } + + if (p_link_down_cnt) + *p_link_down_cnt = nthw_field_get_val32(p->mp_fld_link_summary_link_down_cnt); + + if (p_nim_interr) + *p_nim_interr = nthw_field_get_val32(p->mp_fld_link_summary_nim_interr); + + if (p_lh_local_fault) + *p_lh_local_fault = nthw_field_get_val32(p->mp_fld_link_summary_lh_local_fault); + + if (p_lh_remote_fault) + *p_lh_remote_fault = nthw_field_get_val32(p->mp_fld_link_summary_lh_remote_fault); + + if (p_local_fault) + *p_local_fault = nthw_field_get_val32(p->mp_fld_link_summary_local_fault); + + if (p_remote_fault) + *p_remote_fault = nthw_field_get_val32(p->mp_fld_link_summary_remote_fault); +} + +/* + * Returns true if the lane/block lock bits indicate that a reset is required. + * This is the case if Block/Lane lock is not all zero but not all set either. + */ +bool nthw_mac_pcs_reset_required(nthw_mac_pcs_t *p) +{ + uint32_t block_lock = nthw_mac_pcs_get_fld_block_lock_lock(p); + uint32_t lane_lock = nthw_mac_pcs_get_fld_lane_lock_lock(p); + uint32_t block_lock_mask = nthw_mac_pcs_get_fld_block_lock_lock_mask(p); + uint32_t lane_lock_mask = nthw_mac_pcs_get_fld_lane_lock_lock_mask(p); + + return ((block_lock != 0) && (block_lock != block_lock_mask)) || + ((lane_lock != 0) && (lane_lock != lane_lock_mask)); +} + +void nthw_mac_pcs_set_fec(nthw_mac_pcs_t *p, bool enable) +{ + NT_LOG(DBG, NTHW, "Port %u: Set FEC: %u\n", p->m_port_no, enable); + + nthw_field_get_updated(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in); + + if (enable) + nthw_field_set_val_flush32(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in, 0); + + else + nthw_field_set_val_flush32(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in, (1 << 5) - 1); + + /* Both Rx and Tx must be reset for new FEC state to become active */ + nthw_mac_pcs_rx_path_rst(p, true); + nthw_mac_pcs_tx_path_rst(p, true); + nt_os_wait_usec(10000); /* 10ms */ + + nthw_mac_pcs_rx_path_rst(p, false); + nthw_mac_pcs_tx_path_rst(p, false); + nt_os_wait_usec(10000); /* 10ms */ + +#ifdef DEBUG + + if (enable) { + NT_LOG(DBG, NTHW, "Port %u: FEC valid: %u\n", p->m_port_no, + nthw_field_get_updated(p->mp_field_fec_stat_valid)); + + } else { + NT_LOG(DBG, NTHW, "Port %u: FEC bypass: %u\n", p->m_port_no, + nthw_field_get_updated(p->mp_field_fec_stat_bypass)); + } + +#endif +} + +bool nthw_mac_pcs_get_fec_bypass(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_field_fec_stat_bypass); +} + +bool nthw_mac_pcs_get_fec_valid(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_field_fec_stat_valid); +} + +bool nthw_mac_pcs_get_fec_stat_all_am_locked(nthw_mac_pcs_t *p) +{ + nthw_register_update(p->mp_reg_fec_stat); + + if ((nthw_field_get_val32(p->mp_field_fec_stat_am_lock0)) && + (nthw_field_get_val32(p->mp_field_fec_stat_am_lock1)) && + (nthw_field_get_val32(p->mp_field_fec_stat_am_lock2)) && + (nthw_field_get_val32(p->mp_field_fec_stat_am_lock3))) { + return true; + } + + return false; +} + +void nthw_mac_pcs_reset_fec_counters(nthw_mac_pcs_t *p) +{ + nthw_register_update(p->mp_reg_fec_cw_cnt); + nthw_register_update(p->mp_reg_fec_ucw_cnt); + + if (nthw_field_get_val32(p->mp_field_fec_cw_cnt_cw_cnt)) { + NT_LOG(DBG, NTHW, "Port %u: FEC_CW_CNT: %u\n", p->m_port_no, + nthw_field_get_val32(p->mp_field_fec_cw_cnt_cw_cnt)); + } + + if (nthw_field_get_val32(p->mp_field_fec_ucw_cnt_ucw_cnt)) { + NT_LOG(DBG, NTHW, "Port %u: FEC_UCW_CNT: %u\n", p->m_port_no, + nthw_field_get_val32(p->mp_field_fec_ucw_cnt_ucw_cnt)); + } +} + +void nthw_mac_pcs_set_gty_tx_tuning(nthw_mac_pcs_t *p, uint8_t lane, uint8_t tx_pre_csr, + uint8_t tx_diff_ctl, uint8_t tx_post_csr) +{ + /* GTY_PRE_CURSOR */ + nthw_register_update(p->mp_reg_gty_pre_cursor); + + switch (lane) { + case 0: + nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr0, + tx_pre_csr & 0x1F); + break; + + case 1: + nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr1, + tx_pre_csr & 0x1F); + break; + + case 2: + nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr2, + tx_pre_csr & 0x1F); + break; + + case 3: + nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr3, + tx_pre_csr & 0x1F); + break; + } + + /* GTY_DIFF_CTL */ + nthw_register_update(p->mp_reg_gty_diff_ctl); + + switch (lane) { + case 0: + nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl0, + tx_diff_ctl & 0x1F); + break; + + case 1: + nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl1, + tx_diff_ctl & 0x1F); + break; + + case 2: + nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl2, + tx_diff_ctl & 0x1F); + break; + + case 3: + nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl3, + tx_diff_ctl & 0x1F); + break; + } + + /* GTY_POST_CURSOR */ + nthw_register_update(p->mp_reg_gty_post_cursor); + + switch (lane) { + case 0: + nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr0, + tx_post_csr & 0x1F); + break; + + case 1: + nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr1, + tx_post_csr & 0x1F); + break; + + case 2: + nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr2, + tx_post_csr & 0x1F); + break; + + case 3: + nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr3, + tx_post_csr & 0x1F); + break; + } + + NT_LOG(DBG, NTHW, + "Port %u, lane %u: GTY tx_pre_csr: %d, tx_diff_ctl: %d, tx_post_csr: %d\n", + p->m_port_no, lane, tx_pre_csr, tx_diff_ctl, tx_post_csr); +} + +/* + * Set receiver equalization mode + * 0: enable DFE + * 1: enable LPM + * + * See UltraScale Architecture GTY Transceivers www.xilinx.com page 181, + * UG578 (v1.1) November 24, 2015 + */ +void nthw_mac_pcs_set_receiver_equalization_mode(nthw_mac_pcs_t *p, uint8_t mode) +{ + nthw_register_update(p->mp_reg_gty_ctl); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en0, mode & 0x1); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en1, mode & 0x1); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en2, mode & 0x1); + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_lpm_en3, mode & 0x1); + + /* Toggle reset */ + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst0, 1); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst1, 1); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst2, 1); + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_equa_rst3, 1); + + nt_os_wait_usec(1000); /* 1ms */ + + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst0, 0); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst1, 0); + nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst2, 0); + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_equa_rst3, 0); + + NT_LOG(DBG, NTHW, "Port %u: GTY receiver mode: %s\n", p->m_port_no, + (mode == c_mac_pcs_receiver_mode_dfe ? "DFE" : "LPM")); +} + +void nthw_mac_pcs_swap_gty_tx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap) +{ + nthw_register_update(p->mp_reg_gty_ctl); + + switch (lane) { + case 0: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol0, swap); + break; + + case 1: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol1, swap); + break; + + case 2: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol2, swap); + break; + + case 3: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol3, swap); + break; + } + + NT_LOG(DBG, NTHW, "Port %u: set GTY Tx lane (%d) polarity: %d\n", p->m_port_no, lane, + swap); +} + +void nthw_mac_pcs_swap_gty_rx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap) +{ + nthw_register_update(p->mp_reg_gty_ctl); + + switch (lane) { + case 0: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol0, swap); + break; + + case 1: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol1, swap); + break; + + case 2: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol2, swap); + break; + + case 3: + nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol3, swap); + break; + } + + NT_LOG(DBG, NTHW, "Port %u: set GTY Rx lane (%d) polarity: %d\n", p->m_port_no, lane, + swap); +} + +void nthw_mac_pcs_set_led_mode(nthw_mac_pcs_t *p, uint8_t mode) +{ + nthw_field_get_updated(p->mp_field_debounce_ctrl_nt_port_ctrl); + nthw_field_set_val_flush32(p->mp_field_debounce_ctrl_nt_port_ctrl, mode); +} + +void nthw_mac_pcs_set_timestamp_comp_rx(nthw_mac_pcs_t *p, uint16_t rx_dly) +{ + if (p->mp_field_time_stamp_comp_rx_dly) { + nthw_field_get_updated(p->mp_field_time_stamp_comp_rx_dly); + nthw_field_set_val_flush32(p->mp_field_time_stamp_comp_rx_dly, rx_dly); + } +} + +void nthw_mac_pcs_set_port_no(nthw_mac_pcs_t *p, uint8_t port_no) +{ + p->m_port_no = port_no; +} + +uint32_t nthw_mac_pcs_get_fld_block_lock_lock(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_fld_block_lock_lock); +} + +uint32_t nthw_mac_pcs_get_fld_block_lock_lock_mask(nthw_mac_pcs_t *p) +{ + return p->m_fld_block_lock_lock_mask; +} + +uint32_t nthw_mac_pcs_get_fld_lane_lock_lock(nthw_mac_pcs_t *p) +{ + return nthw_field_get_updated(p->mp_fld_vl_demuxed_lock); +} + +uint32_t nthw_mac_pcs_get_fld_lane_lock_lock_mask(nthw_mac_pcs_t *p) +{ + return p->m_fld_vl_demuxed_lock_mask; +} -- 2.44.0