get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/130515/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 130515,
    "url": "http://patchwork.dpdk.org/api/patches/130515/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/20230818105630.30290-3-mko-plv@napatech.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230818105630.30290-3-mko-plv@napatech.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230818105630.30290-3-mko-plv@napatech.com",
    "date": "2023-08-18T10:56:25",
    "name": "[v3,3/8] net/ntnic: adds NT200A02 adapter support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "020d7404b8925ed2b60ce61a1394d1238aefcf59",
    "submitter": {
        "id": 3153,
        "url": "http://patchwork.dpdk.org/api/people/3153/?format=api",
        "name": "Mykola Kostenok",
        "email": "mko-plv@napatech.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/20230818105630.30290-3-mko-plv@napatech.com/mbox/",
    "series": [
        {
            "id": 29278,
            "url": "http://patchwork.dpdk.org/api/series/29278/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=29278",
            "date": "2023-08-18T10:56:26",
            "name": "[v3,1/8] net/ntnic: initial commit which adds register defines",
            "version": 3,
            "mbox": "http://patchwork.dpdk.org/series/29278/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/130515/comments/",
    "check": "warning",
    "checks": "http://patchwork.dpdk.org/api/patches/130515/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id DAB4C43095;\n\tFri, 18 Aug 2023 12:56:52 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 6B1FD42D35;\n\tFri, 18 Aug 2023 12:56:45 +0200 (CEST)",
            "from egress-ip4b.ess.de.barracuda.com\n (egress-ip4b.ess.de.barracuda.com [18.185.115.208])\n by mails.dpdk.org (Postfix) with ESMTP id 6344040ED9\n for <dev@dpdk.org>; Fri, 18 Aug 2023 12:56:42 +0200 (CEST)",
            "from EUR05-AM6-obe.outbound.protection.outlook.com\n (mail-am6eur05lp2112.outbound.protection.outlook.com [104.47.18.112]) by\n mx-outbound19-62.eu-central-1b.ess.aws.cudaops.com (version=TLSv1.2\n cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO);\n Fri, 18 Aug 2023 10:56:41 +0000",
            "from DUZPR01CA0054.eurprd01.prod.exchangelabs.com\n (2603:10a6:10:469::10) by DB9P190MB1844.EURP190.PROD.OUTLOOK.COM\n (2603:10a6:10:37f::8) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6699.20; Fri, 18 Aug\n 2023 10:56:31 +0000",
            "from DB5PEPF00014B97.eurprd02.prod.outlook.com\n (2603:10a6:10:469:cafe::26) by DUZPR01CA0054.outlook.office365.com\n (2603:10a6:10:469::10) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6699.20 via Frontend\n Transport; Fri, 18 Aug 2023 10:56:31 +0000",
            "from localhost.localdomain.com (178.72.21.4) by\n DB5PEPF00014B97.mail.protection.outlook.com (10.167.8.235) with Microsoft\n SMTP Server id 15.20.6699.14 via Frontend Transport; Fri, 18 Aug 2023\n 10:56:31 +0000"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=b6/IiMFIi3Y+7httThCEw8uiNfshOpMGnb5TWpbpTeRCrjLx3xu7C6sQEkAogBElIxiHzOHg9JOiDAlNOy+NUSZHYibDwwG0G15socXKu2u38xTj/IsoZ2FUE7LnbSga4o0I0ntMf6jatTnkW20hqfM2pa/xdVOGCC1qT1G7yaPyxwVolcz55SVM9SLepTxTifqR0JOO6hRixkzguCPeJ4OKnF9zuUtJHIL6qoCW1s15DZN/1H2T3wEX1CWaLatiqRBjHlFLxzeFmRv4Lsn2kOU8uY6lj+R/ol/enstMuANL/CADT4AgscuK8kkLUOKIZnxBvqt3at4sr//7CTOzYw==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n 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;\n bh=EshrCHWYfp6qtjLvd25fvC5sWsRpBlgl1pg4Pexr7VM=;\n b=EcoxEjC75FNx5MRotvlYnKbrWbuppaluh9GmjQNfVknOEhU7AWVqYUWq5Xx/Ko1J3dLrU6VtnoFZ0pJIION65hdQ8u5uUGGsEkDCUPRwigY6s2s6sVUFcJyQGXEkqUaYo9nesAWVg+XBdxOLIrEW6p+BdC/bWXh5AdPCQfKiyylH6wcWpApK6nik3ozmr+LGvcJQg+FHv5Pr1aWY+h9WfFDCMJ26ncZP8/urCux5FrKwMMoXj2rNv7Xd7rTI6B2kt5KoVgTdf22Qps3R4BXcL8FlSplsdrrnrH82bgk/j79rqOZ5nrlIipC2RVwmh7w/nlUYNuO9KY+NxDA6JwtUuA==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=fail (sender ip is\n 178.72.21.4) smtp.rcpttodomain=dpdk.org smtp.mailfrom=napatech.com;\n dmarc=fail (p=none sp=none pct=100) action=none header.from=napatech.com;\n dkim=none (message not signed); arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=napatech.com;\n s=selector1;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=EshrCHWYfp6qtjLvd25fvC5sWsRpBlgl1pg4Pexr7VM=;\n b=OvLwnsOGW7skwFNDnoZ07fN10y2bXS/X1fxJ6JBEMSvAnhqcKJdRgLGuVsrvB/0uebAV/elF4jFbX5CyRqZ7qnlAiT6SRi/ijR0dSQtONMAQLDBfMqiXYtcysr/vg846uzUtlmoaNfvD4y7Of1wblCsU7yJ5rmwyp+mIJtYOsfA=",
        "X-MS-Exchange-Authentication-Results": "spf=fail (sender IP is 178.72.21.4)\n smtp.mailfrom=napatech.com; dkim=none (message not signed)\n header.d=none;dmarc=fail action=none header.from=napatech.com;",
        "Received-SPF": "Fail (protection.outlook.com: domain of napatech.com does not\n designate 178.72.21.4 as permitted sender) receiver=protection.outlook.com;\n client-ip=178.72.21.4; helo=localhost.localdomain.com;",
        "From": "Mykola Kostenok <mko-plv@napatech.com>",
        "To": "dev@dpdk.org,\n\tmko-plv@napatech.com",
        "Cc": "ckm@napatech.com",
        "Subject": "[PATCH v3 3/8] net/ntnic: adds NT200A02 adapter support",
        "Date": "Fri, 18 Aug 2023 12:56:25 +0200",
        "Message-Id": "<20230818105630.30290-3-mko-plv@napatech.com>",
        "X-Mailer": "git-send-email 2.39.3",
        "In-Reply-To": "<20230818105630.30290-1-mko-plv@napatech.com>",
        "References": "<20230818105630.30290-1-mko-plv@napatech.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "DB5PEPF00014B97:EE_|DB9P190MB1844:EE_",
        "Content-Type": "text/plain",
        "X-MS-Office365-Filtering-Correlation-Id": "c5203b38-143c-4fea-1b24-08db9fd9c809",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n yJ+ZH9dqL1hGoWRCq8KUFn/OXi96xVaXf03GcGkeL23Es6XsvqKHmZSKfVEbwGb5kVxRuw6jtpwku0AXDiz2Q+l7h5wFCcUrgeNKA9df+fWbsqLL525//bQ9FIfhWSW2llsjRSYbeRYrzushBIWFNt48NPbWxQGkzYRQwcxdyNWGft1smaGNO7Okabt+8RmfgWpSTrcrzf7R0XZXuPk3cr3w5TylBXD/r5tll+3b/0dOjBMMx8HpJAOV7KCf0xPD9/z+M2PaVlmdq0D6W4Xn2QrCfwlA+tl75RzBirsCxUCqjHIrKUpmK7WjYtrKl0uJ9LVI+JhAUTNSmph9MClJ6k5XfpNh20Rrg85oqfKae4438zoAd2Q7fwmbtasW/XwWvIwnWEpaGTAgdfwDuGhgO455oP94ehbQDhXbZ828LNrieWoSbGnBwxoUwyDqFMKyw5ErZOnjg3Q+HOVQiz1hrPD6hTaoT8+/NV8JqU45cf5W6aoCbFd/Psp8EdXBVqLUgtn6lyE9lS4/URXbT4+rP4sBrLz48NuCvyJrcuj7M31WrNf53SfOdvIY2UX8pBeg2yDd4dl/R0tWXWqoWWR8bdrpvF9KeOcRtXcO8aJ+T3/RmxoJ8EPRpjh4od8Ui9uMjoCoIHMRaXrI61FfT51MHZUGND6vAW5Hk34dBlXeLaM/DvxxPqVGpTvTxr1scgG7AuyOXb2F96x79qB1Mp+R77dMtZBydO8QGTohR+l8MpnRztDUtvbl5pkd96iopKmfM/pNClnxILt+qYMTCRdXhA==",
        "X-Forefront-Antispam-Report": "CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:localhost.localdomain.com; PTR:InfoDomainNonexistent;\n CAT:NONE;\n SFS:(13230031)(6069001)(346002)(39830400003)(136003)(376002)(396003)(186009)(1800799009)(82310400011)(451199024)(36840700001)(46966006)(6666004)(6486002)(6506007)(6512007)(9316004)(86362001)(66899024)(336012)(1076003)(107886003)(26005)(40480700001)(83380400001)(36756003)(36860700001)(47076005)(34020700004)(118246002)(81166007)(356005)(2616005)(956004)(2906002)(36736006)(316002)(30864003)(41300700001)(70206006)(70586007)(5660300002)(8676002)(4326008)(8936002)(478600001)(19627235002)(36900700001)(559001)(579004);\n DIR:OUT; SFP:1101;",
        "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0": "\n 8NqXZnq51hHoL1mesgUEZ3X7MEB4oOhvEnXflTrUi/BTCJu9wQcJH5g5v5N53Pyb2x1vfAqGn9/iLHQtcswRQQAcKBY9PywA9SzXzJMW/WUCk1gXkZz8xocSWpRteevsCpgMJWkCPhWhFrAeTJwXPz1lpoim8xHLqR8qCOsH2foxIcBNFF/3zcpPTSxuJEoK2p5SoC4veo1Er1GP4xPI0uXKy5F6qxCEmHQEl6UOfoLA17hUSJnh8o9+pBMn4fySayROQTG2Rg76UvuXP9zT73Mz6zfhw4PNMVJNXpEkVnTw9mwJCPklXFvKXKkk7+nABzPC8NZndsPr3llhvOGQ8hvvPkVIE2vq6VA3/WC2sII0OqhLahhMXPNkW0oKX6VEkKNXi7gS65zvb7mGQvNDvq+Byp6QMb5qb/+mHXgGSWddub8f4tAIe8GCwqkhWI3Wcb1Mp5fY6hL33SiNC+vL+xZgKbPCHMrPx09KTH4+AwUc/4w7Z4njwE1Z+RGfif0QXLLXMw0/JjH6T93hkmyfwmd8zEuoXBgBXyEORPkh3lY8T5pdrczjhGkJu8NMNII+MjWQgm1ZEb1lCC7ipatNxhyCR0yJ3lGcHiCyctNDmw1SOLJ1GxjsuRlwCgtJ9ct+52xZLFLwsuBzUVlUXIR37z1+wbbKzx36+QhYyd3Jw5GVLufmYQOYzSsvOpjg1CoSu/CM3sNYGjWPub4/RvS7uyw4GTJhNsiomkrR6xciSh/qwpXf7cUfBYsRwH82E99EZij7I6KHmPIKRXa0BzXai55/WMhKFaAUFfC0NEHeaQM=",
        "X-OriginatorOrg": "napatech.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "18 Aug 2023 10:56:31.3941 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n c5203b38-143c-4fea-1b24-08db9fd9c809",
        "X-MS-Exchange-CrossTenant-Id": "c4540d0b-728a-4233-9da5-9ea30c7ec3ed",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=c4540d0b-728a-4233-9da5-9ea30c7ec3ed; Ip=[178.72.21.4];\n Helo=[localhost.localdomain.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n DB5PEPF00014B97.eurprd02.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DB9P190MB1844",
        "X-BESS-ID": "1692356201-304926-12333-15217-1",
        "X-BESS-VER": "2019.1_20230817.1828",
        "X-BESS-Apparent-Source-IP": "104.47.18.112",
        "X-BESS-Parts": "H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVkYWBsZm5kB2BlDY2MwoKTXRyN\n AgNcXE0DTJMsksxcjcLMkwNc082cTAxFCpNhYAoOlHs0MAAAA=",
        "X-BESS-Outbound-Spam-Score": "0.00",
        "X-BESS-Outbound-Spam-Report": "Code version 3.2,\n rules version 3.2.2.250228 [from\n cloudscan9-178.eu-central-1a.ess.aws.cudaops.com]\n Rule breakdown below\n pts rule name              description\n ---- ---------------------- --------------------------------\n 0.00 LARGE_BODY_SHORTCUT    META:  ",
        "X-BESS-Outbound-Spam-Status": "SCORE=0.00 using account:ESS113687 scores of\n 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 <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "From: Christian Koue Muf <ckm@napatech.com>\n\nThe PMD is designed to support multiple different adapters, and this commit\nadds support for NT200A02 2x100G. Sensor and NIM code is included.\n\nSigned-off-by: Christian Koue Muf <ckm@napatech.com>\nReviewed-by: Mykola Kostenok <mko-plv@napatech.com>\n---\nv2:\n* Fixed WARNING:TYPO_SPELLING\n---\n .../net/ntnic/adapter/common_adapter_defs.h   |   14 +\n drivers/net/ntnic/adapter/nt4ga_adapter.c     |  477 ++++\n drivers/net/ntnic/adapter/nt4ga_adapter.h     |  108 +\n drivers/net/ntnic/adapter/nt4ga_filter.h      |   15 +\n drivers/net/ntnic/adapter/nt4ga_link.c        |  178 ++\n drivers/net/ntnic/adapter/nt4ga_link.h        |  179 ++\n drivers/net/ntnic/adapter/nt4ga_link_100g.c   |  825 +++++++\n drivers/net/ntnic/adapter/nt4ga_link_100g.h   |   12 +\n drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c   |  598 +++++\n drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h   |   41 +\n drivers/net/ntnic/adapter/nt4ga_stat.c        |  705 ++++++\n drivers/net/ntnic/adapter/nt4ga_stat.h        |  202 ++\n drivers/net/ntnic/meson.build                 |   24 +\n drivers/net/ntnic/nim/i2c_nim.c               | 1974 +++++++++++++++++\n drivers/net/ntnic/nim/i2c_nim.h               |  122 +\n drivers/net/ntnic/nim/nim_defines.h           |  146 ++\n drivers/net/ntnic/nim/nt_link_speed.c         |  105 +\n drivers/net/ntnic/nim/nt_link_speed.h         |   34 +\n drivers/net/ntnic/nim/qsfp_registers.h        |   57 +\n drivers/net/ntnic/nim/qsfp_sensors.c          |  174 ++\n drivers/net/ntnic/nim/qsfp_sensors.h          |   18 +\n drivers/net/ntnic/nim/sfp_p_registers.h       |  100 +\n drivers/net/ntnic/nim/sfp_sensors.c           |  288 +++\n drivers/net/ntnic/nim/sfp_sensors.h           |   18 +\n .../net/ntnic/nthw/core/nthw_clock_profiles.c |   11 +-\n drivers/net/ntnic/nthw/core/nthw_core.h       |    2 +\n drivers/net/ntnic/nthw/core/nthw_gmf.c        |  290 +++\n drivers/net/ntnic/nthw/core/nthw_gmf.h        |   93 +\n .../nthw/core/nthw_nt200a02_u23_si5340_v5.h   |  344 +++\n drivers/net/ntnic/nthw/core/nthw_rmc.c        |  156 ++\n drivers/net/ntnic/nthw/core/nthw_rmc.h        |   57 +\n .../ntnic/sensors/avr_sensors/avr_sensors.c   |  104 +\n .../ntnic/sensors/avr_sensors/avr_sensors.h   |   22 +\n .../sensors/board_sensors/board_sensors.c     |   48 +\n .../sensors/board_sensors/board_sensors.h     |   18 +\n .../net/ntnic/sensors/board_sensors/tempmon.c |   42 +\n .../net/ntnic/sensors/board_sensors/tempmon.h |   16 +\n .../ntnic/sensors/nim_sensors/nim_sensors.c   |   54 +\n .../ntnic/sensors/nim_sensors/nim_sensors.h   |   19 +\n drivers/net/ntnic/sensors/ntavr/avr_intf.h    |   89 +\n drivers/net/ntnic/sensors/ntavr/ntavr.c       |   78 +\n drivers/net/ntnic/sensors/ntavr/ntavr.h       |   32 +\n drivers/net/ntnic/sensors/sensor_types.h      |  259 +++\n drivers/net/ntnic/sensors/sensors.c           |  273 +++\n drivers/net/ntnic/sensors/sensors.h           |  127 ++\n drivers/net/ntnic/sensors/stream_info.h       |   86 +\n 46 files changed, 8632 insertions(+), 2 deletions(-)\n create mode 100644 drivers/net/ntnic/adapter/common_adapter_defs.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_adapter.c\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_adapter.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_filter.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_link.c\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_link.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_link_100g.c\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_link_100g.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_stat.c\n create mode 100644 drivers/net/ntnic/adapter/nt4ga_stat.h\n create mode 100644 drivers/net/ntnic/nim/i2c_nim.c\n create mode 100644 drivers/net/ntnic/nim/i2c_nim.h\n create mode 100644 drivers/net/ntnic/nim/nim_defines.h\n create mode 100644 drivers/net/ntnic/nim/nt_link_speed.c\n create mode 100644 drivers/net/ntnic/nim/nt_link_speed.h\n create mode 100644 drivers/net/ntnic/nim/qsfp_registers.h\n create mode 100644 drivers/net/ntnic/nim/qsfp_sensors.c\n create mode 100644 drivers/net/ntnic/nim/qsfp_sensors.h\n create mode 100644 drivers/net/ntnic/nim/sfp_p_registers.h\n create mode 100644 drivers/net/ntnic/nim/sfp_sensors.c\n create mode 100644 drivers/net/ntnic/nim/sfp_sensors.h\n create mode 100644 drivers/net/ntnic/nthw/core/nthw_gmf.c\n create mode 100644 drivers/net/ntnic/nthw/core/nthw_gmf.h\n create mode 100644 drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h\n create mode 100644 drivers/net/ntnic/nthw/core/nthw_rmc.c\n create mode 100644 drivers/net/ntnic/nthw/core/nthw_rmc.h\n create mode 100644 drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c\n create mode 100644 drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h\n create mode 100644 drivers/net/ntnic/sensors/board_sensors/board_sensors.c\n create mode 100644 drivers/net/ntnic/sensors/board_sensors/board_sensors.h\n create mode 100644 drivers/net/ntnic/sensors/board_sensors/tempmon.c\n create mode 100644 drivers/net/ntnic/sensors/board_sensors/tempmon.h\n create mode 100644 drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c\n create mode 100644 drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h\n create mode 100644 drivers/net/ntnic/sensors/ntavr/avr_intf.h\n create mode 100644 drivers/net/ntnic/sensors/ntavr/ntavr.c\n create mode 100644 drivers/net/ntnic/sensors/ntavr/ntavr.h\n create mode 100644 drivers/net/ntnic/sensors/sensor_types.h\n create mode 100644 drivers/net/ntnic/sensors/sensors.c\n create mode 100644 drivers/net/ntnic/sensors/sensors.h\n create mode 100644 drivers/net/ntnic/sensors/stream_info.h",
    "diff": "diff --git a/drivers/net/ntnic/adapter/common_adapter_defs.h b/drivers/net/ntnic/adapter/common_adapter_defs.h\nnew file mode 100644\nindex 0000000000..79167806f1\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/common_adapter_defs.h\n@@ -0,0 +1,14 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _COMMON_ADAPTER_DEFS_H_\n+#define _COMMON_ADAPTER_DEFS_H_\n+\n+/*\n+ * Declarations shared by NT adapter types.\n+ */\n+#define NUM_ADAPTER_MAX (8)\n+#define NUM_ADAPTER_PORTS_MAX (128)\n+\n+#endif /* _COMMON_ADAPTER_DEFS_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_adapter.c b/drivers/net/ntnic/adapter/nt4ga_adapter.c\nnew file mode 100644\nindex 0000000000..259aae2831\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_adapter.c\n@@ -0,0 +1,477 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntlog.h\"\n+\n+#include \"nthw_drv.h\"\n+#include \"nthw_fpga.h\"\n+#include \"nt4ga_adapter.h\"\n+#include \"nt4ga_pci_ta_tg.h\"\n+#include \"nt4ga_link_100g.h\"\n+\n+/* Sensors includes */\n+#include \"board_sensors.h\"\n+#include \"avr_sensors.h\"\n+\n+/*\n+ * Global variables shared by NT adapter types\n+ */\n+pthread_t monitor_tasks[NUM_ADAPTER_MAX];\n+volatile int monitor_task_is_running[NUM_ADAPTER_MAX];\n+\n+/*\n+ * Signal-handler to stop all monitor threads\n+ */\n+static void stop_monitor_tasks(int signum)\n+{\n+\tconst size_t n = ARRAY_SIZE(monitor_task_is_running);\n+\tsize_t i;\n+\n+\t/* Stop all monitor tasks */\n+\tfor (i = 0; i < n; i++) {\n+\t\tconst int is_running = monitor_task_is_running[i];\n+\n+\t\tmonitor_task_is_running[i] = 0;\n+\t\tif (signum == -1 && is_running != 0) {\n+\t\t\tvoid *ret_val = NULL;\n+\n+\t\t\tpthread_join(monitor_tasks[i], &ret_val);\n+\t\t\tmemset(&monitor_tasks[i], 0, sizeof(monitor_tasks[0]));\n+\t\t}\n+\t}\n+}\n+\n+int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *pfh)\n+{\n+\tconst char *const p_dev_name = p_adapter_info->p_dev_name;\n+\tconst char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;\n+\tfpga_info_t *p_fpga_info = &p_adapter_info->fpga_info;\n+\thw_info_t *p_hw_info = &p_adapter_info->hw_info;\n+\tchar a_pci_ident_str[32];\n+\n+\tsnprintf(a_pci_ident_str, sizeof(a_pci_ident_str), \"\" PCIIDENT_PRINT_STR \"\",\n+\t\tPCIIDENT_TO_DOMAIN(p_fpga_info->pciident),\n+\t\tPCIIDENT_TO_BUSNR(p_fpga_info->pciident),\n+\t\tPCIIDENT_TO_DEVNR(p_fpga_info->pciident),\n+\t\tPCIIDENT_TO_FUNCNR(p_fpga_info->pciident));\n+\n+\tfprintf(pfh, \"%s: DeviceName: %s\\n\", p_adapter_id_str,\n+\t\t(p_dev_name ? p_dev_name : \"NA\"));\n+\tfprintf(pfh, \"%s: PCI Details:\\n\", p_adapter_id_str);\n+\tfprintf(pfh, \"%s: %s: %08X: %04X:%04X %04X:%04X\\n\", p_adapter_id_str,\n+\t\ta_pci_ident_str, p_fpga_info->pciident, p_hw_info->pci_vendor_id,\n+\t\tp_hw_info->pci_device_id, p_hw_info->pci_sub_vendor_id,\n+\t\tp_hw_info->pci_sub_device_id);\n+\tfprintf(pfh, \"%s: FPGA Details:\\n\", p_adapter_id_str);\n+\tfprintf(pfh, \"%s: %03d-%04d-%02d-%02d [%016\" PRIX64 \"] (%08X)\\n\",\n+\t\tp_adapter_id_str, p_fpga_info->n_fpga_type_id, p_fpga_info->n_fpga_prod_id,\n+\t\tp_fpga_info->n_fpga_ver_id, p_fpga_info->n_fpga_rev_id,\n+\t\tp_fpga_info->n_fpga_ident, p_fpga_info->n_fpga_build_time);\n+\tfprintf(pfh, \"%s: FpgaDebugMode=0x%x\\n\", p_adapter_id_str,\n+\t\tp_fpga_info->n_fpga_debug_mode);\n+\tfprintf(pfh,\n+\t\t\"%s: Nims=%d PhyPorts=%d PhyQuads=%d RxPorts=%d TxPorts=%d\\n\",\n+\t\tp_adapter_id_str, p_fpga_info->n_nims, p_fpga_info->n_phy_ports,\n+\t\tp_fpga_info->n_phy_quads, p_fpga_info->n_rx_ports, p_fpga_info->n_tx_ports);\n+\tfprintf(pfh, \"%s: Hw=0x%02X_rev%d: %s\\n\", p_adapter_id_str,\n+\t\tp_hw_info->hw_platform_id, p_fpga_info->nthw_hw_info.hw_id,\n+\t\tp_fpga_info->nthw_hw_info.hw_plat_id_str);\n+\n+\tnt4ga_stat_dump(p_adapter_info, pfh);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * SPI for sensors initialization\n+ */\n+static nthw_spi_v3_t *new_sensors_s_spi(struct nt_fpga_s *p_fpga)\n+{\n+\tnthw_spi_v3_t *sensors_s_spi = nthw_spi_v3_new();\n+\n+\tif (sensors_s_spi == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: SPI allocation error\\n\", __func__);\n+\t\treturn NULL;\n+\t}\n+\n+\tif (nthw_spi_v3_init(sensors_s_spi, p_fpga, 0)) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: SPI initialization error\\n\", __func__);\n+\t\tnthw_spi_v3_delete(sensors_s_spi);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn sensors_s_spi;\n+}\n+\n+/*\n+ * SPI for sensors reading\n+ */\n+nthw_spis_t *new_sensors_t_spi(struct nt_fpga_s *p_fpga)\n+{\n+\tnthw_spis_t *sensors_t_spi = nthw_spis_new();\n+\t/* init SPI for sensor initialization process */\n+\tif (sensors_t_spi == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: SPI allocation error\\n\", __func__);\n+\t\treturn NULL;\n+\t}\n+\n+\tif (nthw_spis_init(sensors_t_spi, p_fpga, 0)) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: SPI initialization error\\n\", __func__);\n+\t\tnthw_spis_delete(sensors_t_spi);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn sensors_t_spi;\n+}\n+\n+static void adapter_sensor_setup(hw_info_t *p_hw_info, struct adapter_info_s *adapter)\n+{\n+\tstruct nt_fpga_s *p_fpga = adapter->fpga_info.mp_fpga;\n+\tstruct nt_sensor_group *sensors_list_ptr = NULL;\n+\tnthw_spi_v3_t *sensors_s_spi = new_sensors_s_spi(p_fpga);\n+\n+\tadapter->adapter_sensors_cnt = 0;\n+\n+\t/* FPGA */\n+\tadapter->adapter_sensors = fpga_temperature_sensor_init(p_hw_info->n_nthw_adapter_id,\n+\t\t\t\t\t\t\t\tNT_SENSOR_FPGA_TEMP, p_fpga);\n+\tsensors_list_ptr = adapter->adapter_sensors;\n+\tadapter->adapter_sensors_cnt++;\n+\n+\t/* AVR */\n+\tif (sensors_s_spi) {\n+\t\tif (nt_avr_sensor_mon_ctrl(sensors_s_spi,\n+\t\t\t\t\t   SENSOR_MON_CTRL_REM_ALL_SENSORS) != 0) {\n+\t\t\t/* stop sensor monitoring */\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"Failed to stop AVR sensors monitoring\\n\");\n+\t\t} else {\n+\t\t\tNT_LOG(DBG, ETHDEV, \"AVR sensors init started\\n\");\n+\n+\t\t\tsensors_list_ptr->next = avr_sensor_init(sensors_s_spi,\n+\t\t\t\t\t\t\t\t p_hw_info->n_nthw_adapter_id,\n+\t\t\t\t\t\t\t\t \"FAN0\",\n+\t\t\t\t\t\t\t\t NT_SENSOR_SOURCE_ADAPTER,\n+\t\t\t\t\t\t\t\t NT_SENSOR_TYPE_FAN,\n+\t\t\t\t\t\t\t\t NT_SENSOR_NT200E3_FAN_SPEED,\n+\t\t\t\t\t\t\t\t SENSOR_MON_FAN, 0,\n+\t\t\t\t\t\t\t\t SENSOR_MON_BIG_ENDIAN,\n+\t\t\t\t\t\t\t\t SENSOR_MON_UNSIGNED,\n+\t\t\t\t\t\t\t\t &fan, 0xFFFF);\n+\t\t\tsensors_list_ptr = sensors_list_ptr->next;\n+\t\t\tadapter->adapter_sensors_cnt++;\n+\n+\t\t\tsensors_list_ptr->next = avr_sensor_init(sensors_s_spi,\n+\t\t\t\t\t\t\t\t p_hw_info->n_nthw_adapter_id,\n+\t\t\t\t\t\t\t\t \"PSU0\",\n+\t\t\t\t\t\t\t\t NT_SENSOR_SOURCE_ADAPTER,\n+\t\t\t\t\t\t\t\t NT_SENSOR_TYPE_TEMPERATURE,\n+\t\t\t\t\t\t\t\t NT_SENSOR_NT200E3_PSU0_TEMP,\n+\t\t\t\t\t\t\t\t SENSOR_MON_PSU_EXAR_7724_0, 0x15,\n+\t\t\t\t\t\t\t\t SENSOR_MON_LITTLE_ENDIAN,\n+\t\t\t\t\t\t\t\t SENSOR_MON_UNSIGNED,\n+\t\t\t\t\t\t\t\t &exar7724_tj, 0xFFFF);\n+\t\t\tsensors_list_ptr = sensors_list_ptr->next;\n+\t\t\tadapter->adapter_sensors_cnt++;\n+\n+\t\t\tsensors_list_ptr->next = avr_sensor_init(sensors_s_spi,\n+\t\t\t\t\t\t\t\t p_hw_info->n_nthw_adapter_id,\n+\t\t\t\t\t\t\t\t \"PSU1\",\n+\t\t\t\t\t\t\t\t NT_SENSOR_SOURCE_ADAPTER,\n+\t\t\t\t\t\t\t\t NT_SENSOR_TYPE_TEMPERATURE,\n+\t\t\t\t\t\t\t\t NT_SENSOR_NT200A02_PSU1_TEMP,\n+\t\t\t\t\t\t\t\t SENSOR_MON_MP2886A, 0x8d,\n+\t\t\t\t\t\t\t\t SENSOR_MON_BIG_ENDIAN,\n+\t\t\t\t\t\t\t\t SENSOR_MON_UNSIGNED,\n+\t\t\t\t\t\t\t\t &mp2886a_tj, 0xFFFF);\n+\t\t\tsensors_list_ptr = sensors_list_ptr->next;\n+\t\t\tadapter->adapter_sensors_cnt++;\n+\n+\t\t\tsensors_list_ptr->next = avr_sensor_init(sensors_s_spi,\n+\t\t\t\t\t\t\t\t p_hw_info->n_nthw_adapter_id,\n+\t\t\t\t\t\t\t\t \"PCB\",\n+\t\t\t\t\t\t\t\t NT_SENSOR_SOURCE_ADAPTER,\n+\t\t\t\t\t\t\t\t NT_SENSOR_TYPE_TEMPERATURE,\n+\t\t\t\t\t\t\t\t NT_SENSOR_NT200E3_PCB_TEMP,\n+\t\t\t\t\t\t\t\t SENSOR_MON_DS1775, 0,\n+\t\t\t\t\t\t\t\t SENSOR_MON_LITTLE_ENDIAN,\n+\t\t\t\t\t\t\t\t SENSOR_MON_SIGNED,\n+\t\t\t\t\t\t\t\t &ds1775_t, 0xFFFF);\n+\t\t\tsensors_list_ptr = sensors_list_ptr->next;\n+\t\t\tadapter->adapter_sensors_cnt++;\n+\n+\t\t\tNT_LOG(DBG, ETHDEV, \"AVR sensors init finished\\n\");\n+\n+\t\t\tif (nt_avr_sensor_mon_ctrl(sensors_s_spi,\n+\t\t\t\t\t\t   SENSOR_MON_CTRL_RUN) != 0) {\n+\t\t\t\t/* start sensor monitoring */\n+\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t       \"Failed to start AVR sensors monitoring\\n\");\n+\t\t\t} else {\n+\t\t\t\tNT_LOG(DBG, ETHDEV,\n+\t\t\t\t       \"AVR sensors monitoring starteed\\n\");\n+\t\t\t}\n+\t\t}\n+\n+\t\tnthw_spi_v3_delete(sensors_s_spi);\n+\t}\n+}\n+\n+int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info)\n+{\n+\tchar *const p_dev_name = malloc(24);\n+\tchar *const p_adapter_id_str = malloc(24);\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\thw_info_t *p_hw_info = &p_adapter_info->hw_info;\n+\n+\t/*\n+\t * IMPORTANT: Most variables cannot be determined before fpga model is instantiated\n+\t * (nthw_fpga_init())\n+\t */\n+\tint n_phy_ports = -1;\n+\tint n_nim_ports = -1;\n+\tint res = -1;\n+\tnt_fpga_t *p_fpga = NULL;\n+\n+\t(void)n_nim_ports; /* currently UNUSED - prevent warning */\n+\n+\tp_hw_info->n_nthw_adapter_id =\n+\t\tnthw_platform_get_nthw_adapter_id(p_hw_info->pci_device_id);\n+\n+\tfpga_info->n_nthw_adapter_id = p_hw_info->n_nthw_adapter_id;\n+\tp_hw_info->hw_product_type = p_hw_info->pci_device_id &\n+\t\t\t\t   0x000f; /* ref: DN-0060 section 9 */\n+\t/* ref: DN-0060 section 9 */\n+\tp_hw_info->hw_platform_id = (p_hw_info->pci_device_id >> 4) & 0x00ff;\n+\t/* ref: DN-0060 section 9 */\n+\tp_hw_info->hw_reserved1 = (p_hw_info->pci_device_id >> 12) & 0x000f;\n+\n+\t/* mp_dev_name */\n+\tp_adapter_info->p_dev_name = p_dev_name;\n+\tif (p_dev_name) {\n+\t\tsnprintf(p_dev_name, 24, \"\" PCIIDENT_PRINT_STR \"\",\n+\t\t\t PCIIDENT_TO_DOMAIN(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_BUSNR(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_DEVNR(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_FUNCNR(p_adapter_info->fpga_info.pciident));\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: (0x%08X)\\n\", p_dev_name,\n+\t\t       p_adapter_info->fpga_info.pciident);\n+\t}\n+\n+\t/* mp_adapter_id_str */\n+\tp_adapter_info->mp_adapter_id_str = p_adapter_id_str;\n+\n+\tp_adapter_info->fpga_info.mp_adapter_id_str = p_adapter_id_str;\n+\n+\tif (p_adapter_id_str) {\n+\t\tsnprintf(p_adapter_id_str, 24, \"PCI:\" PCIIDENT_PRINT_STR \"\",\n+\t\t\t PCIIDENT_TO_DOMAIN(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_BUSNR(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_DEVNR(p_adapter_info->fpga_info.pciident),\n+\t\t\t PCIIDENT_TO_FUNCNR(p_adapter_info->fpga_info.pciident));\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: %s\\n\", p_adapter_id_str, p_dev_name);\n+\t}\n+\n+\t{\n+\t\tint i;\n+\n+\t\tfor (i = 0; i < (int)ARRAY_SIZE(p_adapter_info->mp_port_id_str);\n+\t\t\t\ti++) {\n+\t\t\tchar *p = malloc(32);\n+\n+\t\t\tif (p) {\n+\t\t\t\tsnprintf(p, 32, \"%s:intf_%d\",\n+\t\t\t\t\t (p_adapter_id_str ? p_adapter_id_str : \"NA\"),\n+\t\t\t\t\t i);\n+\t\t\t\tNT_LOG(DBG, ETHDEV, \"%s\\n\", p);\n+\t\t\t}\n+\t\t\tp_adapter_info->mp_port_id_str[i] = p;\n+\t\t}\n+\t}\n+\n+\tres = nthw_fpga_init(&p_adapter_info->fpga_info);\n+\tif (res) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: %s: FPGA=%04d res=x%08X [%s:%u]\\n\",\n+\t\t       p_adapter_id_str, p_dev_name, fpga_info->n_fpga_prod_id, res,\n+\t\t       __func__, __LINE__);\n+\t\treturn res;\n+\t}\n+\n+\tassert(fpga_info);\n+\tp_fpga = fpga_info->mp_fpga;\n+\tassert(p_fpga);\n+\tn_phy_ports = fpga_info->n_phy_ports;\n+\tassert(n_phy_ports >= 1);\n+\tn_nim_ports = fpga_info->n_nims;\n+\tassert(n_nim_ports >= 1);\n+\n+\t/*\n+\t * HIF/PCI TA/TG\n+\t */\n+\t{\n+\t\tres = nt4ga_pci_ta_tg_init(p_adapter_info);\n+\t\tif (res == 0) {\n+\t\t\tnt4ga_pci_ta_tg_measure_throughput_main(p_adapter_info,\n+\t\t\t\t\t\t\t\t0, 0,\n+\t\t\t\t\t\t\t\tTG_PKT_SIZE,\n+\t\t\t\t\t\t\t\tTG_NUM_PACKETS,\n+\t\t\t\t\t\t\t\tTG_DELAY);\n+\t\t} else {\n+\t\t\tNT_LOG(WRN, ETHDEV,\n+\t\t\t       \"%s: PCI TA/TG is not available - skipping\\n\",\n+\t\t\t       p_adapter_id_str);\n+\t\t}\n+\t}\n+\n+\tadapter_sensor_setup(p_hw_info, p_adapter_info);\n+\n+\t{\n+\t\tint i;\n+\n+\t\tassert(fpga_info->n_fpga_prod_id > 0);\n+\t\tfor (i = 0; i < NUM_ADAPTER_PORTS_MAX; i++) {\n+\t\t\t/* Disable all ports. Must be enabled later */\n+\t\t\tp_adapter_info->nt4ga_link.port_action[i].port_disable =\n+\t\t\t\ttrue;\n+\t\t}\n+\t\tswitch (fpga_info->n_fpga_prod_id) {\n+\t\t/* NT200A02: 2x100G */\n+\t\tcase 9563: /* NT200A02 */\n+\t\t\tres = nt4ga_link_100g_ports_init(p_adapter_info, p_fpga);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"%s: Unsupported FPGA product: %04d\\n\", __func__,\n+\t\t\t       fpga_info->n_fpga_prod_id);\n+\t\t\tres = -1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (res) {\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"%s: %s: %s: %u: FPGA=%04d res=x%08X\\n\",\n+\t\t\t       p_adapter_id_str, p_dev_name, __func__, __LINE__,\n+\t\t\t       fpga_info->n_fpga_prod_id, res);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * HostBuffer Systems\n+\t */\n+\tp_adapter_info->n_rx_host_buffers = 0;\n+\tp_adapter_info->n_tx_host_buffers = 0;\n+\n+\tp_adapter_info->fpga_info.mp_nthw_epp = NULL;\n+\tif (nthw_epp_present(p_adapter_info->fpga_info.mp_fpga, 0)) {\n+\t\tp_adapter_info->fpga_info.mp_nthw_epp = nthw_epp_new();\n+\t\tif (p_adapter_info->fpga_info.mp_nthw_epp == NULL) {\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: Cannot create EPP\\n\",\n+\t\t\t       p_adapter_id_str);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tres = nthw_epp_init(p_adapter_info->fpga_info.mp_nthw_epp,\n+\t\t\t\t    p_adapter_info->fpga_info.mp_fpga, 0);\n+\t\tif (res != 0) {\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: Cannot initialize EPP\\n\",\n+\t\t\t       p_adapter_id_str);\n+\t\t\treturn res;\n+\t\t}\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: Initialized EPP\\n\",\n+\t\t       p_adapter_id_str);\n+\n+\t\tres = nthw_epp_setup(p_adapter_info->fpga_info.mp_nthw_epp);\n+\t\tif (res != 0) {\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: Cannot setup EPP\\n\",\n+\t\t\t       p_adapter_id_str);\n+\t\t\treturn res;\n+\t\t}\n+\t}\n+\n+\t/* Nt4ga Stat init/setup */\n+\tres = nt4ga_stat_init(p_adapter_info);\n+\tif (res != 0) {\n+\t\tNT_LOG(ERR, ETHDEV,\n+\t\t       \"%s: Cannot initialize the statistics module\\n\",\n+\t\t       p_adapter_id_str);\n+\t\treturn res;\n+\t}\n+\tres = nt4ga_stat_setup(p_adapter_info);\n+\tif (res != 0) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: Cannot setup the statistics module\\n\",\n+\t\t       p_adapter_id_str);\n+\t\treturn res;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int nt4ga_adapter_deinit(struct adapter_info_s *p_adapter_info)\n+{\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\tint i;\n+\tint res;\n+\tstruct nt_sensor_group *cur_adapter_sensor = NULL;\n+\tstruct nt_sensor_group *next_adapter_sensor = NULL;\n+\tstruct nim_sensor_group *cur_nim_sensor = NULL;\n+\tstruct nim_sensor_group *next_nim_sensor = NULL;\n+\n+\tstop_monitor_tasks(-1);\n+\n+\tnt4ga_stat_stop(p_adapter_info);\n+\n+\tnthw_fpga_shutdown(&p_adapter_info->fpga_info);\n+\n+\t/* Rac rab reset flip flop */\n+\tres = nthw_rac_rab_reset(fpga_info->mp_nthw_rac);\n+\n+\t/* Free adapter port ident strings */\n+\tfor (i = 0; i < fpga_info->n_phy_ports; i++) {\n+\t\tif (p_adapter_info->mp_port_id_str[i]) {\n+\t\t\tfree(p_adapter_info->mp_port_id_str[i]);\n+\t\t\tp_adapter_info->mp_port_id_str[i] = NULL;\n+\t\t}\n+\t}\n+\n+\t/* Free adapter ident string */\n+\tif (p_adapter_info->mp_adapter_id_str) {\n+\t\tfree(p_adapter_info->mp_adapter_id_str);\n+\t\tp_adapter_info->mp_adapter_id_str = NULL;\n+\t}\n+\n+\t/* Free devname ident string */\n+\tif (p_adapter_info->p_dev_name) {\n+\t\tfree(p_adapter_info->p_dev_name);\n+\t\tp_adapter_info->p_dev_name = NULL;\n+\t}\n+\n+\t/* Free adapter sensors */\n+\tif (p_adapter_info->adapter_sensors != NULL) {\n+\t\tdo {\n+\t\t\tcur_adapter_sensor = p_adapter_info->adapter_sensors;\n+\t\t\tnext_adapter_sensor =\n+\t\t\t\tp_adapter_info->adapter_sensors->next;\n+\t\t\tp_adapter_info->adapter_sensors = next_adapter_sensor;\n+\n+\t\t\tsensor_deinit(cur_adapter_sensor);\n+\t\t} while (next_adapter_sensor != NULL);\n+\t}\n+\n+\t/* Free NIM sensors */\n+\tfor (i = 0; i < fpga_info->n_phy_ports; i++) {\n+\t\tif (p_adapter_info->nim_sensors[i] != NULL) {\n+\t\t\tdo {\n+\t\t\t\tcur_nim_sensor = p_adapter_info->nim_sensors[i];\n+\t\t\t\tnext_nim_sensor =\n+\t\t\t\t\tp_adapter_info->nim_sensors[i]->next;\n+\t\t\t\tp_adapter_info->nim_sensors[i] = next_nim_sensor;\n+\t\t\t\tfree(cur_nim_sensor->sensor);\n+\t\t\t\tfree(cur_nim_sensor);\n+\t\t\t} while (next_nim_sensor != NULL);\n+\t\t}\n+\t}\n+\n+\treturn res;\n+}\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_adapter.h b/drivers/net/ntnic/adapter/nt4ga_adapter.h\nnew file mode 100644\nindex 0000000000..6ae78a3743\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_adapter.h\n@@ -0,0 +1,108 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NT4GA_ADAPTER_H_\n+#define _NT4GA_ADAPTER_H_\n+\n+#include \"common_adapter_defs.h\"\n+\n+struct adapter_info_s;\n+\n+/*\n+ * DN-0060 section 9\n+ */\n+typedef struct hw_info_s {\n+\t/* pciids */\n+\tuint16_t pci_vendor_id;\n+\tuint16_t pci_device_id;\n+\tuint16_t pci_sub_vendor_id;\n+\tuint16_t pci_sub_device_id;\n+\tuint16_t pci_class_id;\n+\n+\t/* Derived from pciid */\n+\tnthw_adapter_id_t n_nthw_adapter_id;\n+\tint hw_platform_id;\n+\tint hw_product_type;\n+\tint hw_reserved1;\n+} hw_info_t;\n+\n+/*\n+ * Services provided by the adapter module\n+ */\n+#include \"nt4ga_pci_ta_tg.h\"\n+#include \"nt4ga_filter.h\"\n+#include \"nt4ga_stat.h\"\n+#include \"nt4ga_link.h\"\n+\n+#include \"sensors.h\"\n+#include \"i2c_nim.h\"\n+#include \"sensor_types.h\"\n+\n+typedef struct adapter_info_s {\n+\tstruct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg;\n+\tstruct nt4ga_stat_s nt4ga_stat;\n+\tstruct nt4ga_filter_s nt4ga_filter;\n+\tstruct nt4ga_link_s nt4ga_link;\n+\n+\tstruct hw_info_s hw_info;\n+\tstruct fpga_info_s fpga_info;\n+\n+\tuint16_t adapter_sensors_cnt;\n+\tuint16_t nim_sensors_cnt[NUM_ADAPTER_PORTS_MAX];\n+\tstruct nt_sensor_group *adapter_sensors;\n+\tstruct nim_sensor_group *nim_sensors[NUM_ADAPTER_PORTS_MAX];\n+\n+\tchar *mp_port_id_str[NUM_ADAPTER_PORTS_MAX];\n+\tchar *mp_adapter_id_str;\n+\tchar *p_dev_name;\n+\tvolatile bool *pb_shutdown;\n+\n+\tint adapter_no;\n+\tint n_rx_host_buffers;\n+\tint n_tx_host_buffers;\n+} adapter_info_t;\n+\n+/*\n+ * Monitor task operations.  This structure defines the management hooks for\n+ * Napatech network devices.  The following hooks can be defined; unless noted\n+ * otherwise, they are optional and can be filled with a null pointer.\n+ *\n+ * int (*mto_open)(int adapter, int port);\n+ *     The function to call when a network device transitions to the up state,\n+ *     e.g., `ip link set <interface> up`.\n+ *\n+ * int (*mto_stop)(int adapter, int port);\n+ *     The function to call when a network device transitions to the down state,\n+ *     e.g., `ip link set <interface> down`.\n+ */\n+struct monitor_task_ops {\n+\tint (*mto_open)(int adapter, int port);\n+\tint (*mto_stop)(int adapter, int port);\n+};\n+\n+#include <pthread.h>\n+#include <signal.h>\n+\n+/* The file nt4ga_adapter.c defines the next four variables. */\n+extern pthread_t monitor_tasks[NUM_ADAPTER_MAX];\n+extern volatile int monitor_task_is_running[NUM_ADAPTER_MAX];\n+\n+/*\n+ * Function that sets up signal handler(s) that stop the monitoring tasks.\n+ */\n+int set_up_signal_handlers_to_stop_monitoring_tasks(void);\n+\n+int nt4ga_adapter_init(struct adapter_info_s *p_adapter_info);\n+int nt4ga_adapter_deinit(struct adapter_info_s *p_adapter_info);\n+\n+int nt4ga_adapter_status(struct adapter_info_s *p_adapter_info);\n+int nt4ga_adapter_transmit_packet(struct adapter_info_s *p_adapter_info,\n+\t\t\t\t  int n_intf_no, uint8_t *p_pkt, int n_pkt_len);\n+\n+int nt4ga_adapter_show_info(struct adapter_info_s *p_adapter_info, FILE *pfh);\n+\n+/* SPI for sensors reading */\n+nthw_spis_t *new_sensors_t_spi(struct nt_fpga_s *p_fpga);\n+\n+#endif /* _NT4GA_ADAPTER_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_filter.h b/drivers/net/ntnic/adapter/nt4ga_filter.h\nnew file mode 100644\nindex 0000000000..ad7e7d8c71\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_filter.h\n@@ -0,0 +1,15 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NT4GA_FILTER_H_\n+#define NT4GA_FILTER_H_\n+\n+typedef struct nt4ga_filter_s {\n+\tint n_intf_cnt;\n+\tint n_queues_per_intf_cnt;\n+\n+\tstruct flow_nic_dev *mp_flow_device;\n+} nt4ga_filter_t;\n+\n+#endif /* NT4GA_FILTER_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_link.c b/drivers/net/ntnic/adapter/nt4ga_link.c\nnew file mode 100644\nindex 0000000000..7fbdb72897\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_link.c\n@@ -0,0 +1,178 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <unistd.h>\n+#include <stdlib.h>\n+#include <stdio.h>\n+#include <stdint.h>\n+#include <inttypes.h>\n+\n+#include \"ntlog.h\"\n+#include \"nthw_drv.h\"\n+#include \"nt4ga_adapter.h\"\n+\n+#include \"nt4ga_link.h\"\n+#include \"nt_util.h\"\n+\n+/*\n+ * port: speed capabilitoes\n+ * This is actually an adapter capability mapped onto every port\n+ */\n+uint32_t nt4ga_port_get_link_speed_capabilities(struct adapter_info_s *p _unused,\n+\t\tint port _unused)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tconst uint32_t nt_link_speed_capa = p_link->speed_capa;\n+\treturn nt_link_speed_capa;\n+}\n+\n+/*\n+ * port: nim present\n+ */\n+bool nt4ga_port_get_nim_present(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tconst bool nim_present = p_link->link_state[port].nim_present;\n+\treturn nim_present;\n+}\n+\n+/*\n+ * port: link mode\n+ */\n+void nt4ga_port_set_adm_state(struct adapter_info_s *p, int port, bool adm_state)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\n+\tp_link->port_action[port].port_disable = !adm_state;\n+}\n+\n+bool nt4ga_port_get_adm_state(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tconst bool adm_state = !p_link->port_action[port].port_disable;\n+\treturn adm_state;\n+}\n+\n+/*\n+ * port: link status\n+ */\n+void nt4ga_port_set_link_status(struct adapter_info_s *p, int port,\n+\t\t\t\tbool link_status)\n+{\n+\t/* Setting link state/status is (currently) the same as controlling the port adm state */\n+\tnt4ga_port_set_adm_state(p, port, link_status);\n+}\n+\n+bool nt4ga_port_get_link_status(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tbool status = p_link->link_state[port].link_up;\n+\treturn status;\n+}\n+\n+/*\n+ * port: link speed\n+ */\n+void nt4ga_port_set_link_speed(struct adapter_info_s *p, int port,\n+\t\t\t       nt_link_speed_t speed)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\n+\tp_link->port_action[port].port_speed = speed;\n+\tp_link->link_info[port].link_speed = speed;\n+}\n+\n+nt_link_speed_t nt4ga_port_get_link_speed(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tnt_link_speed_t speed = p_link->link_info[port].link_speed;\n+\treturn speed;\n+}\n+\n+/*\n+ * port: link autoneg\n+ * Currently not fully supported by link code\n+ */\n+void nt4ga_port_set_link_autoneg(struct adapter_info_s *p _unused,\n+\t\t\t\t int port _unused, bool autoneg _unused)\n+{\n+\tnt4ga_link_t *const p_link _unused = &p->nt4ga_link;\n+}\n+\n+bool nt4ga_port_get_link_autoneg(struct adapter_info_s *p _unused,\n+\t\t\t\t int port _unused)\n+{\n+\tnt4ga_link_t *const p_link _unused = &p->nt4ga_link;\n+\treturn true;\n+}\n+\n+/*\n+ * port: link duplex\n+ * Currently not fully supported by link code\n+ */\n+void nt4ga_port_set_link_duplex(struct adapter_info_s *p, int port,\n+\t\t\t\tnt_link_duplex_t duplex)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\n+\tp_link->port_action[port].port_duplex = duplex;\n+}\n+\n+nt_link_duplex_t nt4ga_port_get_link_duplex(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tnt_link_duplex_t duplex = p_link->link_info[port].link_duplex;\n+\treturn duplex;\n+}\n+\n+/*\n+ * port: loopback mode\n+ */\n+void nt4ga_port_set_loopback_mode(struct adapter_info_s *p, int port,\n+\t\t\t\t  uint32_t mode)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\n+\tp_link->port_action[port].port_lpbk_mode = mode;\n+}\n+\n+uint32_t nt4ga_port_get_loopback_mode(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\n+\treturn p_link->port_action[port].port_lpbk_mode;\n+}\n+\n+/*\n+ * port: nim capabilities\n+ */\n+nim_i2c_ctx_t nt4ga_port_get_nim_capabilities(struct adapter_info_s *p, int port)\n+{\n+\tnt4ga_link_t *const p_link = &p->nt4ga_link;\n+\tnim_i2c_ctx_t nim_ctx = p_link->u.var100g.nim_ctx[port];\n+\treturn nim_ctx;\n+}\n+\n+/*\n+ * port: tx power\n+ */\n+int nt4ga_port_tx_power(struct adapter_info_s *p, int port, bool disable)\n+{\n+\tnt4ga_link_t *link_info = &p->nt4ga_link;\n+\n+\tif (link_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28_SR4 ||\n+\t\t\tlink_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28 ||\n+\t\t\tlink_info->u.nim_ctx[port].port_type == NT_PORT_TYPE_QSFP28_LR4) {\n+\t\tnim_i2c_ctx_t *nim_ctx = &link_info->u.var100g.nim_ctx[port];\n+\n+\t\tif (!nim_ctx->specific_u.qsfp.rx_only) {\n+\t\t\tif (nim_qsfp_plus_nim_set_tx_laser_disable(nim_ctx, disable,\n+\t\t\t\t\t\t\t       -1) != 0)\n+\t\t\t\treturn 1;\n+\t\t}\n+\t} else {\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_link.h b/drivers/net/ntnic/adapter/nt4ga_link.h\nnew file mode 100644\nindex 0000000000..2be9f49075\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_link.h\n@@ -0,0 +1,179 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NT4GA_LINK_H_\n+#define NT4GA_LINK_H_\n+\n+#include \"common_adapter_defs.h\"\n+#include \"nthw_drv.h\"\n+#include \"i2c_nim.h\"\n+#include \"nthw_fpga_rst_nt200a0x.h\"\n+\n+/*\n+ * Link state.\\n\n+ * Just after start of ntservice the link state might be unknown since the\n+ * monitoring routine is busy reading NIM state and NIM data. This might also\n+ * be the case after a NIM is plugged into an interface.\n+ * The error state indicates a HW reading error.\n+ */\n+enum nt_link_state_e {\n+\tNT_LINK_STATE_UNKNOWN = 0, /* The link state has not been read yet */\n+\tNT_LINK_STATE_DOWN = 1, /* The link state is DOWN */\n+\tNT_LINK_STATE_UP = 2, /* The link state is UP */\n+\tNT_LINK_STATE_ERROR = 3 /* The link state could not be read */\n+};\n+\n+typedef enum nt_link_state_e nt_link_state_t, *nt_link_state_p;\n+\n+/*\n+ * Link duplex mode\n+ */\n+enum nt_link_duplex_e {\n+\tNT_LINK_DUPLEX_UNKNOWN = 0,\n+\tNT_LINK_DUPLEX_HALF = 0x01, /* Half duplex */\n+\tNT_LINK_DUPLEX_FULL = 0x02, /* Full duplex */\n+};\n+\n+typedef enum nt_link_duplex_e nt_link_duplex_t;\n+\n+/*\n+ * Link loopback mode\n+ */\n+enum nt_link_loopback_e {\n+\tNT_LINK_LOOPBACK_OFF = 0,\n+\tNT_LINK_LOOPBACK_HOST = 0x01, /* Host loopback mode */\n+\tNT_LINK_LOOPBACK_LINE = 0x02, /* Line loopback mode */\n+};\n+\n+/*\n+ * Link MDI mode\n+ */\n+enum nt_link_mdi_e {\n+\tNT_LINK_MDI_NA = 0,\n+\tNT_LINK_MDI_AUTO = 0x01, /* MDI auto */\n+\tNT_LINK_MDI_MDI = 0x02, /* MDI mode */\n+\tNT_LINK_MDI_MDIX = 0x04, /* MDIX mode */\n+};\n+\n+typedef enum nt_link_mdi_e nt_link_mdi_t;\n+\n+/*\n+ * Link Auto/Manual mode\n+ */\n+enum nt_link_auto_neg_e {\n+\tNT_LINK_AUTONEG_NA = 0,\n+\tNT_LINK_AUTONEG_MANUAL = 0x01,\n+\tNT_LINK_AUTONEG_OFF = NT_LINK_AUTONEG_MANUAL, /* Auto negotiation OFF */\n+\tNT_LINK_AUTONEG_AUTO = 0x02,\n+\tNT_LINK_AUTONEG_ON = NT_LINK_AUTONEG_AUTO, /* Auto negotiation ON */\n+};\n+\n+typedef enum nt_link_auto_neg_e nt_link_auto_neg_t;\n+\n+/*\n+ * Callback functions to setup mac, pcs and phy\n+ */\n+typedef struct link_state_s {\n+\tbool link_disabled;\n+\tbool nim_present;\n+\tbool lh_nim_absent;\n+\tbool link_up;\n+\tenum nt_link_state_e link_state;\n+\tenum nt_link_state_e link_state_latched;\n+} link_state_t;\n+\n+typedef struct link_info_s {\n+\tenum nt_link_speed_e link_speed;\n+\tenum nt_link_duplex_e link_duplex;\n+\tenum nt_link_auto_neg_e link_auto_neg;\n+} link_info_t;\n+\n+typedef struct port_action_s {\n+\tbool port_disable;\n+\tenum nt_link_speed_e port_speed;\n+\tenum nt_link_duplex_e port_duplex;\n+\tuint32_t port_lpbk_mode;\n+} port_action_t;\n+\n+typedef struct adapter_100g_s {\n+\tnim_i2c_ctx_t\n+\tnim_ctx[NUM_ADAPTER_PORTS_MAX]; /* Should be the first field */\n+\tnthw_mac_pcs_t mac_pcs100g[NUM_ADAPTER_PORTS_MAX];\n+\tnthw_gpio_phy_t gpio_phy[NUM_ADAPTER_PORTS_MAX];\n+} adapter_100g_t;\n+\n+typedef union adapter_var_s {\n+\tnim_i2c_ctx_t nim_ctx\n+\t[NUM_ADAPTER_PORTS_MAX]; /* First field in all the adaptors type */\n+\tadapter_100g_t var100g;\n+} adapter_var_u;\n+\n+typedef struct nt4ga_link_s {\n+\tlink_state_t link_state[NUM_ADAPTER_PORTS_MAX];\n+\tlink_info_t link_info[NUM_ADAPTER_PORTS_MAX];\n+\tport_action_t port_action[NUM_ADAPTER_PORTS_MAX];\n+\tuint32_t speed_capa;\n+\t/* */\n+\tbool variables_initialized;\n+\tadapter_var_u u;\n+} nt4ga_link_t;\n+\n+bool nt4ga_port_get_nim_present(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port:s link mode\n+ */\n+void nt4ga_port_set_adm_state(struct adapter_info_s *p, int port,\n+\t\t\t      bool adm_state);\n+bool nt4ga_port_get_adm_state(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port:s link status\n+ */\n+void nt4ga_port_set_link_status(struct adapter_info_s *p, int port, bool status);\n+bool nt4ga_port_get_link_status(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port: link autoneg\n+ */\n+void nt4ga_port_set_link_autoneg(struct adapter_info_s *p, int port,\n+\t\t\t\t bool autoneg);\n+bool nt4ga_port_get_link_autoneg(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port: link speed\n+ */\n+void nt4ga_port_set_link_speed(struct adapter_info_s *p, int port,\n+\t\t\t       nt_link_speed_t speed);\n+nt_link_speed_t nt4ga_port_get_link_speed(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port: link duplex\n+ */\n+void nt4ga_port_set_link_duplex(struct adapter_info_s *p, int port,\n+\t\t\t\tnt_link_duplex_t duplex);\n+nt_link_duplex_t nt4ga_port_get_link_duplex(struct adapter_info_s *p, int port);\n+\n+/*\n+ * port: loopback mode\n+ */\n+void nt4ga_port_set_loopback_mode(struct adapter_info_s *p, int port,\n+\t\t\t\t  uint32_t mode);\n+uint32_t nt4ga_port_get_loopback_mode(struct adapter_info_s *p, int port);\n+\n+uint32_t nt4ga_port_get_link_speed_capabilities(struct adapter_info_s *p,\n+\t\tint port);\n+\n+/*\n+ * port: nim capabilities\n+ */\n+nim_i2c_ctx_t nt4ga_port_get_nim_capabilities(struct adapter_info_s *p,\n+\t\tint port);\n+\n+/*\n+ * port: tx power\n+ */\n+int nt4ga_port_tx_power(struct adapter_info_s *p, int port, bool disable);\n+\n+#endif /* NT4GA_LINK_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_link_100g.c b/drivers/net/ntnic/adapter/nt4ga_link_100g.c\nnew file mode 100644\nindex 0000000000..8465b6a341\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_link_100g.c\n@@ -0,0 +1,825 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+#include \"i2c_nim.h\"\n+#include \"nt4ga_adapter.h\"\n+#include \"nt4ga_link_100g.h\"\n+\n+#include <string.h> /* memcmp, memset */\n+\n+/*\n+ * Prototypes\n+ */\n+static int swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs,\n+\t\t\t\tint port, bool swap);\n+static int reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs);\n+\n+/*\n+ * Structs and types definitions\n+ */\n+enum link_up_state {\n+\tRESET, /* A valid signal is detected by NO local faults. */\n+\tEXPECT_NO_LF, /* After that we check NO latched local fault bit before */\n+\t/* de-asserting Remote fault indication. */\n+\tWAIT_STABLE_LINK, /* Now we expect the link is up. */\n+\tMONITOR_LINK /* After link-up we monitor link state. */\n+};\n+\n+typedef struct _monitoring_state {\n+\t/* Fields below are set by monitoring thread */\n+\tenum link_up_state m_link_up_state;\n+\tenum nt_link_state_e link_state;\n+\tenum nt_link_state_e latch_link_state;\n+\tint m_time_out;\n+} monitoring_state_t, *monitoring_state_p;\n+\n+/*\n+ * Global variables\n+ */\n+\n+/*\n+ * External state, to be set by the network driver.\n+ */\n+\n+/*\n+ * Utility functions\n+ */\n+\n+static void set_loopback(struct adapter_info_s *p_adapter_info,\n+\t\t\t  nthw_mac_pcs_t *mac_pcs, int intf_no, uint32_t mode,\n+\t\t\t  uint32_t last_mode)\n+{\n+\tbool swap_polerity = true;\n+\n+\tswitch (mode) {\n+\tcase 1:\n+\t\tNT_LOG(INF, ETHDEV, \"%s: Applying host loopback\\n\",\n+\t\t       p_adapter_info->mp_port_id_str[intf_no]);\n+\t\tnthw_mac_pcs_set_fec(mac_pcs, true);\n+\t\tnthw_mac_pcs_set_host_loopback(mac_pcs, true);\n+\t\tswap_polerity = false;\n+\t\tbreak;\n+\tcase 2:\n+\t\tNT_LOG(INF, ETHDEV, \"%s: Applying line loopback\\n\",\n+\t\t       p_adapter_info->mp_port_id_str[intf_no]);\n+\t\tnthw_mac_pcs_set_line_loopback(mac_pcs, true);\n+\t\tbreak;\n+\tdefault:\n+\t\tswitch (last_mode) {\n+\t\tcase 1:\n+\t\t\tNT_LOG(INF, ETHDEV, \"%s: Removing host loopback\\n\",\n+\t\t\t       p_adapter_info->mp_port_id_str[intf_no]);\n+\t\t\tnthw_mac_pcs_set_host_loopback(mac_pcs, false);\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tNT_LOG(INF, ETHDEV, \"%s: Removing line loopback\\n\",\n+\t\t\t       p_adapter_info->mp_port_id_str[intf_no]);\n+\t\t\tnthw_mac_pcs_set_line_loopback(mac_pcs, false);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t/* Do nothing */\n+\t\t\tbreak;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n+\tif ((p_adapter_info->fpga_info.nthw_hw_info.hw_id == 2 &&\n+\t\t\tp_adapter_info->hw_info.n_nthw_adapter_id == NT_HW_ADAPTER_ID_NT200A01) ||\n+\t\t\tp_adapter_info->hw_info.n_nthw_adapter_id == NT_HW_ADAPTER_ID_NT200A02) {\n+\t\t(void)swap_tx_rx_polarity(p_adapter_info, mac_pcs, intf_no,\n+\t\t\t\t\t   swap_polerity);\n+\t}\n+\n+\t/* After changing the loopback the system must be properly reset */\n+\treset_rx(p_adapter_info, mac_pcs);\n+\n+\tNT_OS_WAIT_USEC(10000); /* 10ms - arbitrary choice */\n+\n+\tif (!nthw_mac_pcs_is_rx_path_rst(mac_pcs)) {\n+\t\tnthw_mac_pcs_reset_bip_counters(mac_pcs);\n+\t\tif (!nthw_mac_pcs_get_fec_bypass(mac_pcs))\n+\t\t\tnthw_mac_pcs_reset_fec_counters(mac_pcs);\n+\t}\n+}\n+\n+/*\n+ * Function to retrieve the current state of a link (for one port)\n+ */\n+static int link_state_build(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs,\n+\t\t\t     nthw_gpio_phy_t *gpio_phy, int port,\n+\t\t\t     link_state_t *state, bool is_port_disabled)\n+{\n+\tuint32_t abs;\n+\tuint32_t phy_link_state;\n+\tuint32_t lh_abs;\n+\tuint32_t ll_phy_link_state;\n+\tuint32_t link_down_cnt;\n+\tuint32_t nim_interr;\n+\tuint32_t lh_local_fault;\n+\tuint32_t lh_remote_fault;\n+\tuint32_t lh_internal_local_fault;\n+\tuint32_t lh_received_local_fault;\n+\n+\tmemset(state, 0, sizeof(*state));\n+\tstate->link_disabled = is_port_disabled;\n+\tnthw_mac_pcs_get_link_summary(mac_pcs, &abs, &phy_link_state, &lh_abs,\n+\t\t\t\t  &ll_phy_link_state, &link_down_cnt,\n+\t\t\t\t  &nim_interr, &lh_local_fault,\n+\t\t\t\t  &lh_remote_fault, &lh_internal_local_fault,\n+\t\t\t\t  &lh_received_local_fault);\n+\n+\tassert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);\n+\tstate->nim_present =\n+\t\tnthw_gpio_phy_is_module_present(gpio_phy, (uint8_t)port);\n+\tstate->lh_nim_absent = !state->nim_present;\n+\tstate->link_up = phy_link_state ? true : false;\n+\n+\t{\n+\t\tstatic char lsbuf[NUM_ADAPTER_MAX][NUM_ADAPTER_PORTS_MAX][256];\n+\t\tchar buf[255];\n+\t\tconst int adapter_no = drv->adapter_no;\n+\n+\t\tsnprintf(buf, sizeof(buf),\n+\t\t\t \"%s: Port = %d: abs = %u, phy_link_state = %u, lh_abs = %u, \"\n+\t\t\t \"ll_phy_link_state = %u, link_down_cnt = %u, nim_interr = %u, \"\n+\t\t\t \"lh_local_fault = %u, lh_remote_fault = %u, lh_internal_local_fault = %u, \"\n+\t\t\t \"lh_received_local_fault = %u\",\n+\t\t\tdrv->mp_adapter_id_str, mac_pcs->mn_instance, abs,\n+\t\t\tphy_link_state, lh_abs, ll_phy_link_state,\n+\t\t\tlink_down_cnt, nim_interr, lh_local_fault,\n+\t\t\tlh_remote_fault, lh_internal_local_fault,\n+\t\t\tlh_received_local_fault);\n+\t\tif (strcmp(lsbuf[adapter_no][port], buf) != 0) {\n+\t\t\trte_strscpy(lsbuf[adapter_no][port], buf,\n+\t\t\t\tsizeof(lsbuf[adapter_no][port]) - 1U);\n+\t\t\tlsbuf[adapter_no][port]\n+\t\t\t[sizeof(lsbuf[adapter_no][port]) - 1U] = '\\0';\n+\t\t\tNT_LOG(DBG, ETHDEV, \"%s\\n\", lsbuf[adapter_no][port]);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Check whether a NIM module is present\n+ */\n+static bool nim_is_present(nthw_gpio_phy_t *gpio_phy, uint8_t if_no)\n+{\n+\tassert(if_no < NUM_ADAPTER_PORTS_MAX);\n+\n+\treturn nthw_gpio_phy_is_module_present(gpio_phy, if_no);\n+}\n+\n+/*\n+ * Enable RX\n+ */\n+static int enable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\t(void)drv; /* unused */\n+\tnthw_mac_pcs_set_rx_enable(mac_pcs, true);\n+\treturn 0;\n+}\n+\n+/*\n+ * Enable TX\n+ */\n+static int enable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\t(void)drv; /* unused */\n+\tnthw_mac_pcs_set_tx_enable(mac_pcs, true);\n+\tnthw_mac_pcs_set_tx_sel_host(mac_pcs, true);\n+\treturn 0;\n+}\n+\n+/*\n+ * Disable RX\n+ */\n+static int disable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\t(void)drv; /* unused */\n+\tnthw_mac_pcs_set_rx_enable(mac_pcs, false);\n+\treturn 0;\n+}\n+\n+/*\n+ * Disable TX\n+ */\n+static int disable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\t(void)drv; /* unused */\n+\tnthw_mac_pcs_set_tx_enable(mac_pcs, false);\n+\tnthw_mac_pcs_set_tx_sel_host(mac_pcs, false);\n+\treturn 0;\n+}\n+\n+/*\n+ * Reset RX\n+ */\n+static int reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\t(void)drv;\n+\n+\tnthw_mac_pcs_rx_path_rst(mac_pcs, true);\n+\tNT_OS_WAIT_USEC(10000); /* 10ms */\n+\tnthw_mac_pcs_rx_path_rst(mac_pcs, false);\n+\tNT_OS_WAIT_USEC(10000); /* 10ms */\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Reset TX\n+ */\n+\n+/*\n+ * Swap tx/rx polarity\n+ */\n+static int swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs,\n+\t\t\t\tint port, bool swap)\n+{\n+\tconst bool tx_polarity_swap[2][4] = { { true, true, false, false },\n+\t\t{ false, true, false, false }\n+\t};\n+\tconst bool rx_polarity_swap[2][4] = { { false, true, true, true },\n+\t\t{ false, true, true, false }\n+\t};\n+\tuint8_t lane;\n+\n+\t(void)drv;\n+\tfor (lane = 0U; lane < 4U; lane++) {\n+\t\tif (swap) {\n+\t\t\tnthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane,\n+\t\t\t\t\t\t\t  tx_polarity_swap[port][lane]);\n+\t\t\tnthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane,\n+\t\t\t\t\t\t\t  rx_polarity_swap[port][lane]);\n+\t\t} else {\n+\t\t\tnthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane, false);\n+\t\t\tnthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane, false);\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Check link once NIM is installed and link can be expected.\n+ */\n+static int check_link_state(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)\n+{\n+\tbool rst_required;\n+\tbool ber;\n+\tbool fec_all_locked;\n+\n+\trst_required = nthw_mac_pcs_reset_required(mac_pcs);\n+\n+\tber = nthw_mac_pcs_get_hi_ber(mac_pcs);\n+\n+\tfec_all_locked = nthw_mac_pcs_get_fec_stat_all_am_locked(mac_pcs);\n+\n+\tif (rst_required || ber || !fec_all_locked)\n+\t\treset_rx(drv, mac_pcs);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Initialize NIM, Code based on nt200e3_2_ptp.cpp: MyPort::createNim()\n+ */\n+static int create_nim(adapter_info_t *drv, nt_fpga_t *fpga, int port,\n+\t\t       bool enable)\n+{\n+\tint res = 0;\n+\tconst uint8_t valid_nim_id = 17U;\n+\tnthw_gpio_phy_t *gpio_phy;\n+\tnim_i2c_ctx_t *nim_ctx;\n+\tsfp_nim_state_t nim;\n+\tnt4ga_link_t *link_info = &drv->nt4ga_link;\n+\tnthw_mac_pcs_t *mac_pcs = &link_info->u.var100g.mac_pcs100g[port];\n+\n+\t(void)fpga; /* unused */\n+\tassert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);\n+\tassert(link_info->variables_initialized);\n+\n+\tgpio_phy = &link_info->u.var100g.gpio_phy[port];\n+\tnim_ctx = &link_info->u.var100g.nim_ctx[port];\n+\n+\t/*\n+\t * Check NIM is present before doing GPIO PHY reset.\n+\t */\n+\tif (!nim_is_present(gpio_phy, (uint8_t)port)) {\n+\t\tNT_LOG(INF, ETHDEV, \"%s: NIM module is absent\\n\",\n+\t\t       drv->mp_port_id_str[port]);\n+\t\treturn 0;\n+\t}\n+\n+\tif (!enable) {\n+\t\tdisable_rx(drv, mac_pcs);\n+\t\tdisable_tx(drv, mac_pcs);\n+\t\treset_rx(drv, mac_pcs);\n+\t}\n+\n+\t/*\n+\t * Perform PHY reset.\n+\t */\n+\tNT_LOG(DBG, ETHDEV, \"%s: Performing NIM reset\\n\",\n+\t       drv->mp_port_id_str[port]);\n+\tnthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, true);\n+\tNT_OS_WAIT_USEC(100000); /* pause 0.1s */\n+\tnthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, false);\n+\n+\t/*\n+\t * Wait a little after a module has been inserted before trying to access I2C\n+\t * data, otherwise the module will not respond correctly.\n+\t */\n+\tNT_OS_WAIT_USEC(1000000); /* pause 1.0s */\n+\n+\tif (!nim_is_present(gpio_phy, (uint8_t)port)) {\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: NIM module is no longer absent!\\n\",\n+\t\t       drv->mp_port_id_str[port]);\n+\t\treturn -1;\n+\t}\n+\n+\tres = construct_and_preinit_nim(nim_ctx, NULL, port,\n+\t\t\t\t\t((struct adapter_info_s *)drv)->nim_sensors,\n+\t\t\t\t\t&((struct adapter_info_s *)drv)->nim_sensors_cnt[port]);\n+\tif (res)\n+\t\treturn res;\n+\n+\tres = nim_state_build(nim_ctx, &nim);\n+\tif (res)\n+\t\treturn res;\n+\n+\tNT_LOG(DBG, NTHW,\n+\t       \"%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'\\n\",\n+\t       drv->mp_port_id_str[port], nim_ctx->nim_id,\n+\t       nim_id_to_text(nim_ctx->nim_id), nim.br, nim_ctx->vendor_name,\n+\t       nim_ctx->prod_no, nim_ctx->serial_no);\n+\n+\t/*\n+\t * Does the driver support the NIM module type?\n+\t */\n+\tif (nim_ctx->nim_id != valid_nim_id) {\n+\t\tNT_LOG(ERR, NTHW,\n+\t\t       \"%s: The driver does not support the NIM module type %s\\n\",\n+\t\t       drv->mp_port_id_str[port], nim_id_to_text(nim_ctx->nim_id));\n+\t\tNT_LOG(DBG, NTHW,\n+\t\t       \"%s: The driver supports the NIM module type %s\\n\",\n+\t\t       drv->mp_port_id_str[port], nim_id_to_text(valid_nim_id));\n+\t\treturn -1;\n+\t}\n+\n+\tif (enable) {\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: De-asserting low power\\n\",\n+\t\t       drv->mp_port_id_str[port]);\n+\t\tnthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, false);\n+\t} else {\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: Asserting low power\\n\",\n+\t\t       drv->mp_port_id_str[port]);\n+\t\tnthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, true);\n+\t}\n+\n+\treturn res;\n+}\n+\n+/*\n+ * Initialize one 100 Gbps port.\n+ * The function shall not assume anything about the state of the adapter\n+ * and/or port.\n+ */\n+static int port_init(adapter_info_t *drv, nt_fpga_t *fpga, int port)\n+{\n+\tint adapter_id;\n+\tint hw_id;\n+\tint res;\n+\tnt4ga_link_t *link_info = &drv->nt4ga_link;\n+\n+\tnthw_mac_pcs_t *mac_pcs;\n+\n+\tassert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);\n+\tassert(link_info->variables_initialized);\n+\n+\tif (fpga && fpga->p_fpga_info) {\n+\t\tadapter_id = fpga->p_fpga_info->n_nthw_adapter_id;\n+\t\thw_id = fpga->p_fpga_info->nthw_hw_info.hw_id;\n+\t} else {\n+\t\tadapter_id = -1;\n+\t\thw_id = -1;\n+\t}\n+\n+\tmac_pcs = &link_info->u.var100g.mac_pcs100g[port];\n+\n+\t/*\n+\t * Phase 1. Pre-state machine (`port init` functions)\n+\t * 1.1) Nt4gaAdapter::portInit()\n+\t */\n+\n+\t/* No adapter set-up here, only state variables */\n+\n+\t/* 1.2) MyPort::init() */\n+\tlink_info->link_info[port].link_speed = NT_LINK_SPEED_100G;\n+\tlink_info->link_info[port].link_duplex = NT_LINK_DUPLEX_FULL;\n+\tlink_info->link_info[port].link_auto_neg = NT_LINK_AUTONEG_OFF;\n+\tlink_info->speed_capa |= NT_LINK_SPEED_100G;\n+\tnthw_mac_pcs_set_led_mode(mac_pcs, NTHW_MAC_PCS_LED_AUTO);\n+\tnthw_mac_pcs_set_receiver_equalization_mode(mac_pcs,\n+\t\t\t\t\t       nthw_mac_pcs_receiver_mode_lpm);\n+\n+\t/*\n+\t * NT200A01 build 2 HW and NT200A02 that require GTY polarity swap\n+\t * if (adapter is `NT200A01 build 2 HW or NT200A02`)\n+\t */\n+\tif (adapter_id == NT_HW_ADAPTER_ID_NT200A02 ||\n+\t\t\t(adapter_id == NT_HW_ADAPTER_ID_NT200A01 && hw_id == 2))\n+\t\t(void)swap_tx_rx_polarity(drv, mac_pcs, port, true);\n+\n+\tnthw_mac_pcs_set_ts_eop(mac_pcs, true); /* end-of-frame timestamping */\n+\n+\t/* Work in ABSOLUTE timing mode, don't set IFG mode. */\n+\n+\t/* Phase 2. Pre-state machine (`setup` functions) */\n+\n+\t/* 2.1) nt200a0x.cpp:Myport::setup() */\n+\tNT_LOG(DBG, ETHDEV, \"%s: Setting up port %d\\n\", drv->mp_port_id_str[port],\n+\t       port);\n+\n+\tNT_LOG(DBG, ETHDEV, \"%s: Port %d: PHY TX enable\\n\",\n+\t       drv->mp_port_id_str[port], port);\n+\tenable_tx(drv, mac_pcs);\n+\treset_rx(drv, mac_pcs);\n+\n+\t/* 2.2) Nt4gaPort::setup() */\n+\tif (nthw_gmf_init(NULL, fpga, port) == 0) {\n+\t\tnthw_gmf_t gmf;\n+\n+\t\tif (nthw_gmf_init(&gmf, fpga, port) == 0)\n+\t\t\tnthw_gmf_set_enable(&gmf, true);\n+\t}\n+\n+\t/* Phase 3. Link state machine steps */\n+\n+\t/* 3.1) Create NIM, ::createNim() */\n+\tres = create_nim(drv, fpga, port, true);\n+\n+\tif (res) {\n+\t\tNT_LOG(WRN, ETHDEV, \"%s: NIM initialization failed\\n\",\n+\t\t       drv->mp_port_id_str[port]);\n+\t\treturn res;\n+\t}\n+\n+\tNT_LOG(DBG, ETHDEV, \"%s: NIM initialized\\n\", drv->mp_port_id_str[port]);\n+\n+\t/* 3.2) MyPort::nimReady() */\n+\n+\t/* 3.3) MyPort::nimReady100Gb() */\n+\n+\t/* Setting FEC resets the lane counter in one half of the GMF */\n+\tnthw_mac_pcs_set_fec(mac_pcs, true);\n+\tNT_LOG(DBG, ETHDEV, \"%s: Port %d: HOST FEC enabled\\n\",\n+\t       drv->mp_port_id_str[port], port);\n+\n+\tif (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && hw_id == 1) {\n+\t\tconst uint8_t tuning_s_r4[2][4][3] = { { { 8, 15, 8 },\n+\t\t\t\t{ 8, 15, 9 },\n+\t\t\t\t{ 7, 15, 9 },\n+\t\t\t\t{ 6, 15, 8 }\n+\t\t\t},\n+\t\t\t{\t{ 6, 15, 8 },\n+\t\t\t\t{ 3, 15, 12 },\n+\t\t\t\t{ 7, 15, 9 },\n+\t\t\t\t{ 7, 15, 8 }\n+\t\t\t}\n+\t\t};\n+\n+\t\tuint8_t lane = 0;\n+\n+\t\tfor (lane = 0; lane < 4; lane++) {\n+\t\t\tuint8_t pre, diff, post;\n+\n+\t\t\t/* Use short-range tuning values */\n+\t\t\tpre = tuning_s_r4[port][lane][0];\n+\t\t\tdiff = tuning_s_r4[port][lane][1];\n+\t\t\tpost = tuning_s_r4[port][lane][2];\n+\n+\t\t\tnthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, diff,\n+\t\t\t\t\t\t  post);\n+\t\t}\n+\t} else if ((adapter_id == NT_HW_ADAPTER_ID_NT200A02) ||\n+\t\t\t((adapter_id == NT_HW_ADAPTER_ID_NT200A01) &&\n+\t\t\t (hw_id == 2))) {\n+\t\tconst uint8_t pre = 5;\n+\t\tconst uint8_t diff = 25;\n+\t\tconst uint8_t post = 12;\n+\n+\t\tuint8_t lane = 0;\n+\n+\t\tfor (lane = 0; lane < 4; lane++) {\n+\t\t\tnthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, diff,\n+\t\t\t\t\t\t  post);\n+\t\t}\n+\t} else {\n+\t\tNT_LOG(ERR, ETHDEV,\n+\t\t       \"%s: Unhandled AdapterId/HwId: %02x_hwid%d\\n\", __func__,\n+\t\t       adapter_id, hw_id);\n+\t\tassert(0);\n+\t}\n+\treset_rx(drv, mac_pcs);\n+\n+\t/*\n+\t * 3.4) MyPort::setLinkState()\n+\t *\n+\t * Compensation = 1640 - dly\n+\t * CMAC-core dly 188 ns\n+\t * FEC no correction 87 ns\n+\t * FEC active correction 211\n+\t */\n+\tif (nthw_mac_pcs_get_fec_valid(mac_pcs))\n+\t\tnthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 211));\n+\n+\telse\n+\t\tnthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 87));\n+\n+\t/* 3.5) uint32_t MyPort::macConfig(nt_link_state_t link_state) */\n+\tenable_rx(drv, mac_pcs);\n+\n+\tnthw_mac_pcs_set_host_loopback(mac_pcs, false);\n+\n+\treturn res;\n+}\n+\n+/*\n+ * State machine shared between kernel and userland\n+ */\n+static int common_ptp_nim_state_machine(void *data)\n+{\n+\tadapter_info_t *drv = (adapter_info_t *)data;\n+\tfpga_info_t *fpga_info = &drv->fpga_info;\n+\tnt4ga_link_t *link_info = &drv->nt4ga_link;\n+\tnt_fpga_t *fpga = fpga_info->mp_fpga;\n+\tconst int adapter_no = drv->adapter_no;\n+\tconst int nb_ports = fpga_info->n_phy_ports;\n+\tuint32_t last_lpbk_mode[NUM_ADAPTER_PORTS_MAX];\n+\n+\tnim_i2c_ctx_t *nim_ctx;\n+\tlink_state_t *link_state;\n+\tnthw_mac_pcs_t *mac_pcs;\n+\tnthw_gpio_phy_t *gpio_phy;\n+\n+\tif (!fpga) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: fpga is NULL\\n\", drv->mp_adapter_id_str);\n+\t\tgoto NT4GA_LINK_100G_MON_EXIT;\n+\t}\n+\n+\tassert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);\n+\tnim_ctx = link_info->u.var100g.nim_ctx;\n+\tlink_state = link_info->link_state;\n+\tmac_pcs = link_info->u.var100g.mac_pcs100g;\n+\tgpio_phy = link_info->u.var100g.gpio_phy;\n+\n+\tmonitor_task_is_running[adapter_no] = 1;\n+\tmemset(last_lpbk_mode, 0, sizeof(last_lpbk_mode));\n+\n+\tif (monitor_task_is_running[adapter_no]) {\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: link state machine running...\\n\",\n+\t\t       drv->mp_adapter_id_str);\n+\t}\n+\n+\twhile (monitor_task_is_running[adapter_no]) {\n+\t\tint i;\n+\t\tstatic bool reported_link[NUM_ADAPTER_PORTS_MAX] = { false };\n+\n+\t\t/* Read sensors */\n+\t\tif (drv->adapter_sensors != NULL) {\n+\t\t\tnthw_spis_t *t_spi =\n+\t\t\t\tnew_sensors_t_spi(drv->fpga_info.mp_fpga);\n+\t\t\tif (t_spi) {\n+\t\t\t\tfor (struct nt_sensor_group *ptr =\n+\t\t\t\t\t\t\tdrv->adapter_sensors;\n+\t\t\t\t\t\tptr != NULL; ptr = ptr->next)\n+\t\t\t\t\tptr->read(ptr, t_spi);\n+\t\t\t\tnthw_spis_delete(t_spi);\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (i = 0; i < nb_ports; i++) {\n+\t\t\tlink_state_t new_link_state;\n+\t\t\tconst bool is_port_disabled =\n+\t\t\t\tlink_info->port_action[i].port_disable;\n+\t\t\tconst bool was_port_disabled =\n+\t\t\t\tlink_state[i].link_disabled;\n+\t\t\tconst bool disable_port = is_port_disabled &&\n+\t\t\t\t\t\t  !was_port_disabled;\n+\t\t\tconst bool enable_port = !is_port_disabled &&\n+\t\t\t\t\t\t was_port_disabled;\n+\n+\t\t\tif (!monitor_task_is_running[adapter_no])   /* stop quickly */\n+\t\t\t\tbreak;\n+\n+\t\t\t/* Reading NIM sensors */\n+\t\t\tif (drv->nim_sensors[i] != NULL) {\n+\t\t\t\tnthw_spis_t *t_spi = new_sensors_t_spi(drv->fpga_info.mp_fpga);\n+\t\t\t\tif (t_spi) {\n+\t\t\t\t\tfor (struct nim_sensor_group *ptr =\n+\t\t\t\t\t\t\t\tdrv->nim_sensors[i];\n+\t\t\t\t\t\t\tptr != NULL; ptr = ptr->next)\n+\t\t\t\t\t\tptr->read(ptr, t_spi);\n+\t\t\t\t\tnthw_spis_delete(t_spi);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t/* Has the administrative port state changed? */\n+\t\t\tassert(!(disable_port && enable_port));\n+\t\t\tif (disable_port) {\n+\t\t\t\tmemset(&link_state[i], 0,\n+\t\t\t\t       sizeof(link_state[i]));\n+\t\t\t\tlink_state[i].link_disabled = true;\n+\t\t\t\treported_link[i] = false;\n+\t\t\t\t/* Turn off laser and LED, etc. */\n+\t\t\t\t(void)create_nim(drv, fpga, i, false);\n+\t\t\t\tNT_LOG(DBG, ETHDEV, \"%s: Port %i is disabled\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i], i);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (enable_port) {\n+\t\t\t\tlink_state[i].link_disabled = false;\n+\t\t\t\tNT_LOG(DBG, ETHDEV, \"%s: Port %i is enabled\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i], i);\n+\t\t\t}\n+\n+\t\t\tif (is_port_disabled)\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (link_info->port_action[i].port_lpbk_mode !=\n+\t\t\t\t\tlast_lpbk_mode[i]) {\n+\t\t\t\t/* Loopback mode has changed. Do something */\n+\t\t\t\tif (!nim_is_present(&gpio_phy[i],\n+\t\t\t\t\t\t     (uint8_t)i)) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * If there is no Nim present, we need to initialize the\n+\t\t\t\t\t * port anyway\n+\t\t\t\t\t */\n+\t\t\t\t\tport_init(drv, fpga, i);\n+\t\t\t\t}\n+\t\t\t\tNT_LOG(INF, ETHDEV,\n+\t\t\t\t       \"%s: Loopback mode changed=%u\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i],\n+\t\t\t\t       link_info->port_action[i].port_lpbk_mode);\n+\t\t\t\tset_loopback(drv, &mac_pcs[i], i,\n+\t\t\t\t\t     link_info->port_action[i].port_lpbk_mode,\n+\t\t\t\t\t     last_lpbk_mode[i]);\n+\t\t\t\tif (link_info->port_action[i].port_lpbk_mode ==\n+\t\t\t\t\t\t1)\n+\t\t\t\t\tlink_state[i].link_up = true;\n+\t\t\t\tlast_lpbk_mode[i] =\n+\t\t\t\t\tlink_info->port_action[i].port_lpbk_mode;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t(void)link_state_build(drv, &mac_pcs[i], &gpio_phy[i],\n+\t\t\t\t\t\ti, &new_link_state,\n+\t\t\t\t\t\tis_port_disabled);\n+\t\t\tif (!new_link_state.nim_present) {\n+\t\t\t\tif (link_state[i].nim_present) {\n+\t\t\t\t\tNT_LOG(INF, ETHDEV,\n+\t\t\t\t\t       \"%s: NIM module removed\\n\",\n+\t\t\t\t\t       drv->mp_port_id_str[i]);\n+\t\t\t\t}\n+\t\t\t\tlink_state[i] = new_link_state;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\t/* NIM module is present */\n+\t\t\tif (new_link_state.lh_nim_absent ||\n+\t\t\t\t\t!link_state[i].nim_present) {\n+\t\t\t\tsfp_nim_state_t new_state;\n+\n+\t\t\t\tNT_LOG(DBG, ETHDEV, \"%s: NIM module inserted\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i]);\n+\n+\t\t\t\tif (port_init(drv, fpga, i)) {\n+\t\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t\t       \"%s: Failed to initialize NIM module\\n\",\n+\t\t\t\t\t       drv->mp_port_id_str[i]);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tif (nim_state_build(&nim_ctx[i], &new_state)) {\n+\t\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t\t       \"%s: Cannot read basic NIM data\\n\",\n+\t\t\t\t\t       drv->mp_port_id_str[i]);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\tassert(new_state.br); /* Cannot be zero if NIM is present */\n+\t\t\t\tNT_LOG(DBG, ETHDEV,\n+\t\t\t\t       \"%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i], nim_ctx->nim_id,\n+\t\t\t\t       nim_id_to_text(nim_ctx->nim_id),\n+\t\t\t\t       (unsigned int)new_state.br,\n+\t\t\t\t       nim_ctx->vendor_name, nim_ctx->prod_no,\n+\t\t\t\t       nim_ctx->serial_no);\n+\n+\t\t\t\t(void)link_state_build(drv, &mac_pcs[i],\n+\t\t\t\t\t\t\t&gpio_phy[i], i,\n+\t\t\t\t\t\t\t&link_state[i],\n+\t\t\t\t\t\t\tis_port_disabled);\n+\n+\t\t\t\tNT_LOG(DBG, ETHDEV,\n+\t\t\t\t       \"%s: NIM module initialized\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i]);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (reported_link[i] != new_link_state.link_up) {\n+\t\t\t\tNT_LOG(INF, ETHDEV, \"%s: link is %s\\n\",\n+\t\t\t\t       drv->mp_port_id_str[i],\n+\t\t\t\t       (new_link_state.link_up ? \"up\" :\n+\t\t\t\t\t\"down\"));\n+\t\t\t\tlink_state[i].link_up = new_link_state.link_up;\n+\t\t\t\treported_link[i] = new_link_state.link_up;\n+\t\t\t}\n+\t\t\tcheck_link_state(drv, &mac_pcs[i]);\n+\t\t} /* end-for */\n+\t\tif (monitor_task_is_running[adapter_no])\n+\t\t\tNT_OS_WAIT_USEC(5 * 100000U); /* 5 x 0.1s = 0.5s */\n+\t}\n+\n+NT4GA_LINK_100G_MON_EXIT:\n+\n+\tNT_LOG(DBG, ETHDEV,\n+\t       \"%s: Stopped NT4GA 100 Gbps link monitoring thread.\\n\",\n+\t       drv->mp_adapter_id_str);\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Userland NIM state machine\n+ */\n+static void *nt4ga_link_100g_mon(void *data)\n+{\n+\t(void)common_ptp_nim_state_machine(data);\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ * Initialize all ports\n+ * The driver calls this function during initialization (of the driver).\n+ */\n+int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info,\n+\t\t\t       nt_fpga_t *fpga)\n+{\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\tconst int adapter_no = p_adapter_info->adapter_no;\n+\tconst int nb_ports = fpga_info->n_phy_ports;\n+\tint res = 0;\n+\n+\tNT_LOG(DBG, ETHDEV, \"%s: Initializing ports\\n\",\n+\t       p_adapter_info->mp_adapter_id_str);\n+\n+\t/*\n+\t * Initialize global variables\n+\t */\n+\tassert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);\n+\n+\tif (res == 0 && !p_adapter_info->nt4ga_link.variables_initialized) {\n+\t\tnthw_mac_pcs_t *mac_pcs =\n+\t\t\tp_adapter_info->nt4ga_link.u.var100g.mac_pcs100g;\n+\t\tnim_i2c_ctx_t *nim_ctx =\n+\t\t\tp_adapter_info->nt4ga_link.u.var100g.nim_ctx;\n+\t\tnthw_gpio_phy_t *gpio_phy =\n+\t\t\tp_adapter_info->nt4ga_link.u.var100g.gpio_phy;\n+\t\tint i;\n+\n+\t\tfor (i = 0; i < nb_ports; i++) {\n+\t\t\tconst uint8_t instance =\n+\t\t\t\t(uint8_t)(2U + i); /* 2 + adapter port number */\n+\t\t\tres = nthw_mac_pcs_init(&mac_pcs[i], fpga,\n+\t\t\t\t\t      i /* int nInstance */);\n+\t\t\tif (res != 0)\n+\t\t\t\tbreak;\n+\t\t\tres = nthw_iic_init(&nim_ctx[i].hwiic, fpga, instance,\n+\t\t\t\t\t   8 /* timing */);\n+\t\t\tif (res != 0)\n+\t\t\t\tbreak;\n+\t\t\tnim_ctx[i].instance = instance;\n+\t\t\tnim_ctx[i].devaddr = 0x50; /* 0xA0 / 2 */\n+\t\t\tnim_ctx[i].regaddr = 0U;\n+\t\t\tres = nthw_gpio_phy_init(&gpio_phy[i], fpga,\n+\t\t\t\t\t       0 /* Only one instance */);\n+\t\t\tif (res != 0)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (res == 0)\n+\t\t\tp_adapter_info->nt4ga_link.variables_initialized = true;\n+\t}\n+\n+\t/* Create state-machine thread */\n+\tif (res == 0) {\n+\t\tif (!monitor_task_is_running[adapter_no]) {\n+\t\t\tres = pthread_create(&monitor_tasks[adapter_no], NULL,\n+\t\t\t\t\t     nt4ga_link_100g_mon, p_adapter_info);\n+\t\t}\n+\t}\n+\treturn res;\n+}\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_link_100g.h b/drivers/net/ntnic/adapter/nt4ga_link_100g.h\nnew file mode 100644\nindex 0000000000..803b3454b7\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_link_100g.h\n@@ -0,0 +1,12 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NT4GA_LINK_100G_H_\n+#define NT4GA_LINK_100G_H_\n+\n+#include \"nthw_drv.h\"\n+\n+int nt4ga_link_100g_ports_init(adapter_info_t *p_adapter_info, nt_fpga_t *p_fpga);\n+\n+#endif /* NT4GA_LINK_100G_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c\nnew file mode 100644\nindex 0000000000..07884e9219\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c\n@@ -0,0 +1,598 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntlog.h\"\n+#include \"nt_util.h\"\n+#include \"nthw_drv.h\"\n+#include \"nt4ga_adapter.h\"\n+#include \"nt4ga_pci_ta_tg.h\"\n+#include \"nthw_pci_ta.h\"\n+#include \"nthw_pci_rd_tg.h\"\n+#include \"nthw_pci_wr_tg.h\"\n+\n+int nt4ga_pci_ta_tg_init(struct adapter_info_s *p_adapter_info)\n+{\n+\tconst char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\tnt_fpga_t *p_fpga = fpga_info->mp_fpga;\n+\tnt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg;\n+\tint res;\n+\tint n_err_cnt = 0;\n+\n+\tif (p) {\n+\t\tmemset(p, 0, sizeof(nt4ga_pci_ta_tg_t));\n+\t} else {\n+\t\tNT_LOG(ERR, NTHW, \"%s: %s: null ptr\\n\", p_adapter_id_str, __func__);\n+\t\treturn -1;\n+\t}\n+\n+\tassert(p_fpga);\n+\n+\tp->mp_nthw_pci_rd_tg = nthw_pci_rd_tg_new();\n+\tassert(p->mp_nthw_pci_rd_tg);\n+\tres = nthw_pci_rd_tg_init(p->mp_nthw_pci_rd_tg, p_fpga, 0);\n+\tif (res) {\n+\t\tn_err_cnt++;\n+\t\tNT_LOG(WRN, NTHW, \"%s: module PCI_RD_TG not found\\n\",\n+\t\t       p_adapter_id_str);\n+\t}\n+\n+\tp->mp_nthw_pci_wr_tg = nthw_pci_wr_tg_new();\n+\tassert(p->mp_nthw_pci_wr_tg);\n+\tres = nthw_pci_wr_tg_init(p->mp_nthw_pci_wr_tg, p_fpga, 0);\n+\tif (res) {\n+\t\tn_err_cnt++;\n+\t\tNT_LOG(WRN, NTHW, \"%s: module PCI_WR_TG not found\\n\",\n+\t\t       p_adapter_id_str);\n+\t}\n+\n+\tp->mp_nthw_pci_ta = nthw_pci_ta_new();\n+\tassert(p->mp_nthw_pci_ta);\n+\tres = nthw_pci_ta_init(p->mp_nthw_pci_ta, p_fpga, 0);\n+\tif (res) {\n+\t\tn_err_cnt++;\n+\t\tNT_LOG(WRN, NTHW, \"%s: module PCI_TA not found\\n\",\n+\t\t       p_adapter_id_str);\n+\t}\n+\n+\treturn n_err_cnt;\n+}\n+\n+static int nt4ga_pci_ta_tg_ta_write_control_enable(nt4ga_pci_ta_tg_t *p,\n+\t\tuint32_t enable)\n+{\n+\tnthw_pci_ta_set_control_enable(p->mp_nthw_pci_ta, enable);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_ta_read_length_error(nt4ga_pci_ta_tg_t *p, uint32_t *p_data)\n+{\n+\tnthw_pci_ta_get_length_error(p->mp_nthw_pci_ta, p_data);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_ta_read_packet_bad(nt4ga_pci_ta_tg_t *p, uint32_t *p_data)\n+{\n+\tnthw_pci_ta_get_packet_bad(p->mp_nthw_pci_ta, p_data);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_ta_read_packet_good(nt4ga_pci_ta_tg_t *p, uint32_t *p_data)\n+{\n+\tnthw_pci_ta_get_packet_good(p->mp_nthw_pci_ta, p_data);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_ta_read_payload_error(nt4ga_pci_ta_tg_t *p,\n+\t\tuint32_t *p_data)\n+{\n+\tnthw_pci_ta_get_payload_error(p->mp_nthw_pci_ta, p_data);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_rd_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova,\n+\t\t\t\t    int slot_addr, uint32_t req_size, bool wait,\n+\t\t\t\t    bool wrap)\n+{\n+\tconst uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * req_size));\n+\n+\tnthw_pci_rd_tg_set_ram_addr(p->mp_nthw_pci_rd_tg, slot_addr);\n+\tnthw_pci_rd_tg_set_phys_addr(p->mp_nthw_pci_rd_tg, n_phys_addr);\n+\tnthw_pci_rd_tg_set_ram_data(p->mp_nthw_pci_rd_tg, req_size, wait, wrap);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_rd_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t num_iterations)\n+{\n+\tnthw_pci_rd_tg_set_run(p->mp_nthw_pci_rd_tg, num_iterations);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_rd_tg_wait_ready(nt4ga_pci_ta_tg_t *p)\n+{\n+\tint poll = 0;\n+\tuint32_t data = 0;\n+\n+\twhile (data == 0) {\n+\t\t/* NOTE: Deliberately start with a sleep - ensures that the FPGA pipe is empty */\n+\t\tNT_OS_WAIT_USEC(1000);\n+\t\tdata = nthw_pci_rd_tg_get_ctrl_rdy(p->mp_nthw_pci_rd_tg);\n+\t\tpoll++;\n+\t\tif (poll >= 1000) {\n+\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t       \"%s: FAILED waiting PCI RD TG ready: poll=%d\\n\",\n+\t\t\t       __func__, poll);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_wr_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova,\n+\t\t\t\t    int slot_addr, uint32_t req_size, bool wait,\n+\t\t\t\t    bool wrap, bool inc)\n+{\n+\tconst uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * req_size));\n+\n+\tnthw_pci_wr_tg_set_ram_addr(p->mp_nthw_pci_wr_tg, slot_addr);\n+\tnthw_pci_wr_tg_set_phys_addr(p->mp_nthw_pci_wr_tg, n_phys_addr);\n+\tnthw_pci_wr_tg_set_ram_data(p->mp_nthw_pci_wr_tg, req_size, wait, wrap, inc);\n+\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_wr_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t num_iterations)\n+{\n+\tnthw_pci_wr_tg_set_run(p->mp_nthw_pci_wr_tg, num_iterations);\n+\treturn 0;\n+}\n+\n+static int nt4ga_pci_ta_tg_wr_tg_wait_ready(nt4ga_pci_ta_tg_t *p)\n+{\n+\tint poll = 0;\n+\tuint32_t data = 0;\n+\n+\twhile (data == 0) {\n+\t\t/* NOTE: Deliberately start with a sleep - ensures that the FPGA pipe is empty */\n+\t\tNT_OS_WAIT_USEC(1000);\n+\t\tdata = nthw_pci_wr_tg_get_ctrl_rdy(p->mp_nthw_pci_wr_tg);\n+\t\tpoll++;\n+\t\tif (poll >= 1000) {\n+\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t       \"%s: FAILED waiting PCI WR TG ready: poll=%d\\n\",\n+\t\t\t       __func__, poll);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int nt4ga_pci_ta_tg_measure_throughput_run(struct adapter_info_s *p_adapter_info,\n+\t\t\t\t      struct nthw_hif_end_point_counters *pri,\n+\t\t\t\t      struct nthw_hif_end_point_counters *sla)\n+{\n+\tnt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg;\n+\n+\tconst int delay = pri->n_tg_delay;\n+\tconst int pkt_size = pri->n_tg_pkt_size;\n+\tconst int num_pkts = pri->n_tg_num_pkts;\n+\tconst int n_direction = pri->n_tg_direction;\n+\tconst uint8_t n_numa_node = (uint8_t)pri->n_numa_node;\n+\tconst int dma_buf_size = (4 * 1024 * 1024);\n+\n+\tconst size_t align_size = ALIGN_SIZE(dma_buf_size);\n+\tuint32_t *mem_addr;\n+\tuint64_t iova;\n+\n+\tint bo_error = 0;\n+\n+\tnthw_hif *p_master_instance = p_adapter_info->fpga_info.mp_nthw_hif;\n+\tnthw_hif *p_slave_instance = NULL;\n+\n+\tnthw_pcie3 *p_pci_master = p_adapter_info->fpga_info.mp_nthw_pcie3;\n+\tnthw_pcie3 *p_pci_slave = NULL;\n+\n+\tassert(p_master_instance || p_pci_master);\n+\n+\tstruct nt_dma_s *p_dma;\n+\t/* FPGA needs a Page alignment (4K on Intel) */\n+\tp_dma = nt_dma_alloc(align_size, 0x1000, n_numa_node);\n+\tif (p_dma == NULL) {\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: vfio_dma_alloc failed\\n\", __func__);\n+\t\treturn 0;\n+\t}\n+\tmem_addr = (uint32_t *)p_dma->addr;\n+\tiova = p_dma->iova;\n+\n+\tNT_LOG(DBG, NTHW,\n+\t       \"%s: Running HIF bandwidth measurements on NUMA node %d\\n\",\n+\t       __func__, n_numa_node);\n+\n+\tbo_error = 0;\n+\t{\n+\t\tint wrap;\n+\n+\t\t/* Stop any existing running test */\n+\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);\n+\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);\n+\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\t\tbo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);\n+\n+\t\t/* Prepare the HIF Traffic generator */\n+\t\tbo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 1);\n+\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\t\t/*\n+\t\t * Ensure that the hostbuffer memory contain data that can be read -\n+\t\t * For this we will ask the FPGA to write data to it. The last wrap packet\n+\t\t * does not generate any data it only wraps (unlike the PCIe2 TG)\n+\t\t */\n+\t\t{\n+\t\t\tint pkt;\n+\n+\t\t\tfor (pkt = 0; pkt < num_pkts; pkt++) {\n+\t\t\t\tif (pkt >= (num_pkts - 1))\n+\t\t\t\t\twrap = 1;\n+\n+\t\t\t\telse\n+\t\t\t\t\twrap = 0;\n+\t\t\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_setup(p, iova,\n+\t\t\t\t\t\t\t\t\tpkt, pkt_size,\n+\t\t\t\t\t\t\t\t\t0, wrap, 1);\n+\t\t\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_setup(p, iova,\n+\t\t\t\t\t\t\t\t\tpkt, pkt_size,\n+\t\t\t\t\t\t\t\t\t0, wrap);\n+\t\t\t}\n+\t\t}\n+\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1);\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\t\t/* Start WR TG Write once */\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1);\n+\t\t/* Wait until WR TG ready */\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\t\t/* Verify that we have a packet */\n+\t\t{\n+\t\t\tint pkt;\n+\n+\t\t\tfor (pkt = 0; pkt < num_pkts; pkt++) {\n+\t\t\t\tuint32_t value = 0;\n+\t\t\t\tint poll;\n+\n+\t\t\t\tfor (poll = 8; poll < pkt_size;\n+\t\t\t\t\t\tpoll += 4, value++) {\n+\t\t\t\t\tif (*(uint32_t *)((uint8_t *)mem_addr +\n+\t\t\t\t\t\t\t  (pkt * pkt_size) +\n+\t\t\t\t\t\t\t  poll) != value) {\n+\t\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t\t       \"HIF TG: Prepare failed. Data write failed: #%d.%d:  %016X:%08X\\n\",\n+\t\t\t\t\t\t       pkt, poll,\n+\t\t\t\t\t\t       *(uint32_t *)((uint8_t *)\n+\t\t\t\t\t\t\t\t     mem_addr +\n+\t\t\t\t\t\t\t\t     (pkt *\n+\t\t\t\t\t\t\t\t      pkt_size) +\n+\t\t\t\t\t\t\t\t     poll),\n+\t\t\t\t\t\t       value);\n+\n+\t\t\t\t\t\t/*\n+\t\t\t\t\t\t * Break out of the verification loop on first\n+\t\t\t\t\t\t * Compare error\n+\t\t\t\t\t\t */\n+\t\t\t\t\t\tbo_error |= 1;\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tswitch (n_direction) {\n+\t\tcase 1: /* Read only test */\n+\t\t\tnt4ga_pci_ta_tg_wr_tg_run(p, 0xffff);\n+\t\t\tbreak;\n+\t\tcase 2: /* Write only test */\n+\t\t\tnt4ga_pci_ta_tg_rd_tg_run(p, 0xffff);\n+\t\t\tbreak;\n+\t\tcase 3: /* Combined read/write test */\n+\t\t\tnt4ga_pci_ta_tg_wr_tg_run(p, 0xffff);\n+\t\t\tnt4ga_pci_ta_tg_rd_tg_run(p, 0xffff);\n+\t\t\tbreak;\n+\t\tdefault: /* stop tests */\n+\t\t\tnt4ga_pci_ta_tg_wr_tg_run(p, 0);\n+\t\t\tnt4ga_pci_ta_tg_rd_tg_run(p, 0);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tdo {\n+\t\t\t/* prep */\n+\t\t\tif (p_pci_master) {\n+\t\t\t\tnthw_pcie3_end_point_counters_sample_pre(p_pci_master,\n+\t\t\t\t\t\t\t\t    pri);\n+\t\t\t}\n+\t\t\tif (p_pci_slave) {\n+\t\t\t\tnthw_pcie3_end_point_counters_sample_pre(p_pci_slave,\n+\t\t\t\t\t\t\t\t    sla);\n+\t\t\t}\n+\n+\t\t\t/* start measure */\n+\t\t\tif (p_master_instance)\n+\t\t\t\tnthw_hif_stat_req_enable(p_master_instance);\n+\t\t\tif (p_pci_master)\n+\t\t\t\tnthw_pcie3_stat_req_enable(p_pci_master);\n+\n+\t\t\tif (p_slave_instance)\n+\t\t\t\tnthw_hif_stat_req_enable(p_slave_instance);\n+\t\t\tif (p_pci_slave)\n+\t\t\t\tnthw_pcie3_stat_req_enable(p_pci_slave);\n+\n+\t\t\t/* Wait */\n+\t\t\tNT_OS_WAIT_USEC(delay);\n+\n+\t\t\t/* Stop measure */\n+\t\t\tif (p_master_instance)\n+\t\t\t\tnthw_hif_stat_req_disable(p_master_instance);\n+\t\t\tif (p_pci_master)\n+\t\t\t\tnthw_pcie3_stat_req_disable(p_pci_master);\n+\n+\t\t\tif (p_slave_instance)\n+\t\t\t\tnthw_hif_stat_req_disable(p_slave_instance);\n+\t\t\tif (p_pci_slave)\n+\t\t\t\tnthw_pcie3_stat_req_disable(p_pci_slave);\n+\n+\t\t\t/* Post process master */\n+\t\t\tif (p_master_instance) {\n+\t\t\t\tnthw_hif_end_point_counters_sample(p_master_instance,\n+\t\t\t\t\t\t\t       pri);\n+\t\t\t}\n+\n+\t\t\tif (p_pci_master) {\n+\t\t\t\tnthw_pcie3_end_point_counters_sample_post(p_pci_master,\n+\t\t\t\t\t\t\t\t     pri);\n+\t\t\t}\n+\n+\t\t\t/* Post process slave */\n+\t\t\tif (p_slave_instance) {\n+\t\t\t\tnthw_hif_end_point_counters_sample(p_slave_instance,\n+\t\t\t\t\t\t\t       sla);\n+\t\t\t}\n+\n+\t\t\tif (p_pci_slave) {\n+\t\t\t\tnthw_pcie3_end_point_counters_sample_post(p_pci_slave,\n+\t\t\t\t\t\t\t\t     sla);\n+\t\t\t}\n+\n+\t\t\t{\n+\t\t\t\t/* Check for TA transmit errors */\n+\t\t\t\tuint32_t dw_good_pkts, dw_bad_pkts, dw_bad_length,\n+\t\t\t\t\t dw_bad_payload;\n+\t\t\t\tnt4ga_pci_ta_tg_ta_read_packet_good(p,\n+\t\t\t\t\t\t\t\t &dw_good_pkts);\n+\t\t\t\tnt4ga_pci_ta_tg_ta_read_packet_bad(p, &dw_bad_pkts);\n+\t\t\t\tnt4ga_pci_ta_tg_ta_read_length_error(p,\n+\t\t\t\t\t\t\t\t  &dw_bad_length);\n+\t\t\t\tnt4ga_pci_ta_tg_ta_read_payload_error(p, &dw_bad_payload);\n+\n+\t\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t\t       \"%s: NUMA node %u: HIF: TA: Good pkts, Bad pkts, Bad length, Bad payload\\n\",\n+\t\t\t\t       __func__, n_numa_node);\n+\t\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t\t       \"%s: NUMA node %u: HIF: TA: 0x%08x 0x%08x 0x%08x 0x%08x\\n\",\n+\t\t\t\t       __func__, n_numa_node, dw_good_pkts,\n+\t\t\t\t       dw_bad_pkts, dw_bad_length, dw_bad_payload);\n+\n+\t\t\t\tif (dw_bad_pkts | dw_bad_length | dw_bad_payload) {\n+\t\t\t\t\tbo_error |= 1;\n+\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t       \"%s: NUMA node %u: HIF: TA: error detected\\n\",\n+\t\t\t\t\t       __func__, n_numa_node);\n+\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t       \"%s: NUMA node %u: HIF: TA: Good packets received: %u\\n\",\n+\t\t\t\t\t       __func__, n_numa_node, dw_good_pkts);\n+\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t       \"%s: NUMA node %u: HIF: TA: Bad packets received : %u\\n\",\n+\t\t\t\t\t       __func__, n_numa_node, dw_bad_pkts);\n+\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t       \"%s: NUMA node %u: HIF: TA: Bad length received  : %u\\n\",\n+\t\t\t\t\t       __func__, n_numa_node,\n+\t\t\t\t\t       dw_bad_length);\n+\t\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t\t       \"%s: NUMA node %u: HIF: TA: Bad payload received : %u\\n\",\n+\t\t\t\t\t       __func__, n_numa_node,\n+\t\t\t\t\t       dw_bad_payload);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (bo_error != 0)\n+\t\t\t\tbreak;\n+\n+\t\t\tbreak; /* for now only loop once */\n+\n+\t\t\t/*\n+\t\t\t * Only do \"signalstop\" looping if a specific numa node and direction is to\n+\t\t\t * be tested.\n+\t\t\t */\n+\t\t} while ((bo_error == 0) && (n_numa_node != UINT8_MAX) &&\n+\t\t\t\t(n_direction != -1));\n+\n+\t\t/* Stop the test */\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);\n+\t\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);\n+\t\tbo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);\n+\n+\t\tbo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);\n+\n+\t\t/* PCIe3 sanity checks */\n+\t\t{\n+#if defined(DEBUG)\n+\t\t\tint do_loop = 1;\n+#else\n+\t\t\tint do_loop = 0;\n+#endif\n+\n+\t\t\twhile (do_loop) {\n+\t\t\t\tdo_loop = 0;\n+\n+\t\t\t\tif (p_master_instance) {\n+\t\t\t\t\tnthw_hif_stat_req_enable(p_master_instance);\n+\t\t\t\t\tNT_OS_WAIT_USEC(100);\n+\t\t\t\t\tnthw_hif_stat_req_disable(p_master_instance);\n+\t\t\t\t}\n+\n+\t\t\t\tif (do_loop == 0)\n+\t\t\t\t\tbreak;\n+\n+\t\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t\t       \"%s: WARNING this is wrong - wait again\\n\",\n+\t\t\t\t       __func__);\n+\t\t\t\tNT_OS_WAIT_USEC(200 * 1000);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Stop the test */\n+\n+\tbo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);\n+\tbo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);\n+\n+\tbo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);\n+\tbo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);\n+\n+\tbo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);\n+\n+\tnt_dma_free(p_dma);\n+\n+\treturn bo_error;\n+}\n+\n+int nt4ga_pci_ta_tg_measure_throughput_main(struct adapter_info_s *p_adapter_info,\n+\t\t\t\t       const uint8_t numa_node,\n+\t\t\t\t       const int direction, const int n_pkt_size,\n+\t\t\t\t       const int n_batch_count, const int n_delay)\n+{\n+\t/* All numa nodes is indicated by UINT8_MAX */\n+\tconst uint8_t numa_begin = (numa_node == UINT8_MAX ? 0 : numa_node);\n+\tconst uint8_t numa_end = numa_begin;\n+\n+\t/* sanity check direction param */\n+\tconst int dir_begin = (direction <= 0 ? 1 : direction);\n+\tconst int dir_end = (direction <= 0 ? 3 : direction);\n+\n+\tint bo_error = 0;\n+\tstruct nthw_hif_end_points eps;\n+\n+\tif (n_delay == 0)\n+\t\treturn -1;\n+\n+\tNT_LOG(DBG, NTHW, \"HIF adapter throughput:\\n\");\n+\n+\t/* Only do \"signalstop\"-looping if a specific numa node is to be tested. */\n+\t{\n+\t\tuint8_t numa;\n+\n+\t\tfor (numa = numa_begin; numa <= numa_end; numa++) {\n+\t\t\t{\n+\t\t\t\tint by_loop;\n+\n+\t\t\t\tfor (by_loop = dir_begin; by_loop <= dir_end;\n+\t\t\t\t\t\tby_loop++) {\n+\t\t\t\t\tstruct nthw_hif_end_point_counters *pri =\n+\t\t\t\t\t\t\t&eps.pri;\n+\t\t\t\t\tstruct nthw_hif_end_point_counters *sla =\n+\t\t\t\t\t\t\t&eps.sla;\n+\n+\t\t\t\t\tpri->n_numa_node = numa;\n+\t\t\t\t\tpri->n_tg_direction = by_loop;\n+\t\t\t\t\tpri->n_tg_pkt_size = (n_pkt_size > 0 ?\n+\t\t\t\t\t\t\t   n_pkt_size :\n+\t\t\t\t\t\t\t   TG_PKT_SIZE);\n+\t\t\t\t\tpri->n_tg_num_pkts =\n+\t\t\t\t\t\t(n_batch_count > 0 ?\n+\t\t\t\t\t\t n_batch_count :\n+\t\t\t\t\t\t TG_NUM_PACKETS);\n+\t\t\t\t\tpri->n_tg_delay = (n_delay > 0 ? n_delay :\n+\t\t\t\t\t\t\t TG_DELAY);\n+\t\t\t\t\tpri->cur_rx = 0;\n+\t\t\t\t\tpri->cur_tx = 0;\n+\t\t\t\t\tpri->n_ref_clk_cnt = -1;\n+\t\t\t\t\tpri->bo_error = 0;\n+\n+\t\t\t\t\tsla->n_numa_node = numa;\n+\t\t\t\t\tsla->n_tg_direction = by_loop;\n+\t\t\t\t\tsla->n_tg_pkt_size = (n_pkt_size > 0 ?\n+\t\t\t\t\t\t\t   n_pkt_size :\n+\t\t\t\t\t\t\t   TG_PKT_SIZE);\n+\t\t\t\t\tsla->n_tg_num_pkts =\n+\t\t\t\t\t\t(n_batch_count > 0 ?\n+\t\t\t\t\t\t n_batch_count :\n+\t\t\t\t\t\t TG_NUM_PACKETS);\n+\t\t\t\t\tsla->n_tg_delay = (n_delay > 0 ? n_delay :\n+\t\t\t\t\t\t\t TG_DELAY);\n+\t\t\t\t\tsla->cur_rx = 0;\n+\t\t\t\t\tsla->cur_tx = 0;\n+\t\t\t\t\tpri->n_ref_clk_cnt = -1;\n+\t\t\t\t\tsla->bo_error = 0;\n+\n+\t\t\t\t\tbo_error +=\n+\t\t\t\t\tnt4ga_pci_ta_tg_measure_throughput_run(p_adapter_info,\n+\t\t\t\t\t\t\t\t\t       pri, sla);\n+#if defined(DEBUG) && (1)\n+\t\t\t\t\t{\n+\t\t\t\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t\t\t\t       \"%s: @ %d: %d %d %d %d: %016lX %016lX : %6ld Mbps %6ld Mbps\\n\",\n+\t\t\t\t\t\t       __func__, pri->n_numa_node,\n+\t\t\t\t\t\t       pri->n_tg_direction,\n+\t\t\t\t\t\t       pri->n_tg_num_pkts,\n+\t\t\t\t\t\t       pri->n_tg_pkt_size,\n+\t\t\t\t\t\t       pri->n_tg_delay,\n+\t\t\t\t\t\t       pri->cur_rx, pri->cur_tx,\n+\t\t\t\t\t\t       (pri->cur_rx * 8UL /\n+\t\t\t\t\t\t\t1000000UL),\n+\t\t\t\t\t\t       (pri->cur_tx * 8UL /\n+\t\t\t\t\t\t\t1000000UL));\n+\t\t\t\t\t}\n+\t\t\t\t\t{\n+\t\t\t\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t\t\t\t       \"%s: @ %d: %d %d %d %d: %016lX %016lX : %6ld Mbps %6ld Mbps\\n\",\n+\t\t\t\t\t\t       __func__, sla->n_numa_node,\n+\t\t\t\t\t\t       sla->n_tg_direction,\n+\t\t\t\t\t\t       sla->n_tg_num_pkts,\n+\t\t\t\t\t\t       sla->n_tg_pkt_size,\n+\t\t\t\t\t\t       sla->n_tg_delay,\n+\t\t\t\t\t\t       sla->cur_rx, sla->cur_tx,\n+\t\t\t\t\t\t       (sla->cur_rx * 8UL /\n+\t\t\t\t\t\t\t1000000UL),\n+\t\t\t\t\t\t       (sla->cur_tx * 8UL /\n+\t\t\t\t\t\t\t1000000UL));\n+\t\t\t\t\t}\n+#endif\n+\n+\t\t\t\t\tif (pri->bo_error != 0 || sla->bo_error != 0)\n+\t\t\t\t\t\tbo_error++;\n+\t\t\t\t\tif (bo_error)\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (bo_error != 0) {\n+\t\tNT_LOG(ERR, NTHW, \"%s: error during bandwidth measurement\\n\",\n+\t\t       __func__);\n+\t}\n+\n+\tNT_LOG(DBG, NTHW, \"HIF adapter throughput: done\\n\");\n+\n+\tNT_LOG(DBG, NTHW, \"%s: [%s:%u] done\\n\", __func__, __FILE__, __LINE__);\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h\nnew file mode 100644\nindex 0000000000..8b46491f77\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.h\n@@ -0,0 +1,41 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NT4GA_PCI_TA_TG_H_\n+#define _NT4GA_PCI_TA_TG_H_\n+\n+#include <stdint.h>\n+\n+#define TA_TG_DBG_SHOW_SUMMARY (1)\n+\n+#define TG_NUM_PACKETS (8)\n+#define TG_PKT_SIZE (2048 * 1)\n+#define TG_AREA_SIZE (TG_NUM_PACKETS * TG_PKT_SIZE)\n+\n+#define TG_DELAY (200000) /* usec */\n+\n+/* Struct predefinitions */\n+struct adapter_info_s;\n+struct nthw_hif_end_point_counters;\n+\n+struct nt4ga_pci_ta_tg_s {\n+\tstruct nthw_pci_rd_tg *mp_nthw_pci_rd_tg;\n+\tstruct nthw_pci_wr_tg *mp_nthw_pci_wr_tg;\n+\tstruct nthw_pci_ta *mp_nthw_pci_ta;\n+};\n+\n+typedef struct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg_t;\n+typedef struct nt4ga_pci_ta_tg_s nt4ga_pci_ta_tg;\n+\n+int nt4ga_pci_ta_tg_init(struct adapter_info_s *p_adapter_info);\n+\n+int nt4ga_pci_ta_tg_measure_throughput_run(struct adapter_info_s *p_adapter_info,\n+\t\t\t\t      struct nthw_hif_end_point_counters *pri,\n+\t\t\t\t      struct nthw_hif_end_point_counters *sla);\n+int nt4ga_pci_ta_tg_measure_throughput_main(struct adapter_info_s *p_adapter_info,\n+\t\t\t\t       const uint8_t numa_node,\n+\t\t\t\t       const int direction, const int n_pkt_size,\n+\t\t\t\t       const int n_batch_count, const int n_delay);\n+\n+#endif /* _NT4GA_PCI_TA_TG_H_ */\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_stat.c b/drivers/net/ntnic/adapter/nt4ga_stat.c\nnew file mode 100644\nindex 0000000000..b61c73ea12\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_stat.c\n@@ -0,0 +1,705 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntlog.h\"\n+#include \"nt_util.h\"\n+#include \"nthw_drv.h\"\n+#include \"nthw_fpga.h\"\n+#include \"nt4ga_adapter.h\"\n+\n+#define NO_FLAGS 0\n+\n+/* Inline timestamp format s pcap 32:32 bits. Convert to nsecs */\n+static inline uint64_t timestamp2ns(uint64_t ts)\n+{\n+\treturn ((ts >> 32) * 1000000000) + (ts & 0xffffffff);\n+}\n+\n+static int nt4ga_stat_collect_cap_v1_stats(nt4ga_stat_t *p_nt4ga_stat,\n+\t\t\t\t   uint32_t *p_stat_dma_virtual);\n+static int nt4ga_stat_collect_virt_v1_stats(nt4ga_stat_t *p_nt4ga_stat,\n+\t\t\t\t    uint32_t *p_stat_dma_virtual);\n+\n+int nt4ga_stat_collect(struct adapter_info_s *p_adapter_info _unused,\n+\t\t      nt4ga_stat_t *p_nt4ga_stat)\n+{\n+\tnthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat;\n+\n+\tif (p_nthw_stat->mb_is_vswitch) {\n+\t\t/*\n+\t\t * Set all bits in the DMA block timestamp since 9530-42-05 and other Vswitch FPGA\n+\t\t * images may only clear all bits in this memory location. TBV\n+\t\t * Consequently, last_timestamp must be constructed via a system call.\n+\t\t */\n+\t\t*p_nthw_stat->mp_timestamp = 0xFFFFFFFF;\n+\t\tp_nt4ga_stat->last_timestamp = NT_OS_GET_TIME_NS();\n+\t\tnt4ga_stat_collect_virt_v1_stats(p_nt4ga_stat,\n+\t\t\t\t\t\tp_nt4ga_stat->p_stat_dma_virtual);\n+\t} else {\n+\t\tp_nt4ga_stat->last_timestamp =\n+\t\t\ttimestamp2ns(*p_nthw_stat->mp_timestamp);\n+\t\tnt4ga_stat_collect_cap_v1_stats(p_nt4ga_stat,\n+\t\t\t\t\t       p_nt4ga_stat->p_stat_dma_virtual);\n+\t}\n+\treturn 0;\n+}\n+\n+int nt4ga_stat_init(struct adapter_info_s *p_adapter_info)\n+{\n+\tconst char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\tnt_fpga_t *p_fpga = fpga_info->mp_fpga;\n+\tnt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat;\n+\n+\tif (p_nt4ga_stat) {\n+\t\tmemset(p_nt4ga_stat, 0, sizeof(nt4ga_stat_t));\n+\t} else {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: ERROR (%s:%d)\", p_adapter_id_str,\n+\t\t       __func__, __LINE__);\n+\t\treturn -1;\n+\t}\n+\n+\t{\n+\t\tnthw_stat_t *p_nthw_stat = nthw_stat_new();\n+\t\tnthw_rmc_t *p_nthw_rmc = nthw_rmc_new();\n+\n+\t\tif (!p_nthw_stat) {\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: ERROR (%s:%d)\", p_adapter_id_str,\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (!p_nthw_rmc) {\n+\t\t\tnthw_stat_delete(p_nthw_stat);\n+\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: ERROR (%s:%d)\", p_adapter_id_str,\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tp_nt4ga_stat->mp_nthw_stat = p_nthw_stat;\n+\t\tnthw_stat_init(p_nthw_stat, p_fpga, 0);\n+\n+\t\tp_nt4ga_stat->mp_nthw_rmc = p_nthw_rmc;\n+\t\tnthw_rmc_init(p_nthw_rmc, p_fpga, 0);\n+\n+\t\tp_nt4ga_stat->mn_rx_host_buffers = p_nthw_stat->m_nb_rx_host_buffers;\n+\t\tp_nt4ga_stat->mn_tx_host_buffers = p_nthw_stat->m_nb_tx_host_buffers;\n+\n+\t\tp_nt4ga_stat->mn_rx_ports = p_nthw_stat->m_nb_rx_ports;\n+\t\tp_nt4ga_stat->mn_tx_ports = p_nthw_stat->m_nb_tx_ports;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int nt4ga_stat_setup(struct adapter_info_s *p_adapter_info)\n+{\n+\tconst int n_physical_adapter_no _unused = p_adapter_info->adapter_no;\n+\tnt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat;\n+\tnthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat;\n+\tnthw_rmc_t *p_nthw_rmc = p_nt4ga_stat->mp_nthw_rmc;\n+\n+\tif (p_nthw_rmc)\n+\t\tnthw_rmc_block(p_nthw_rmc);\n+\n+\t/* Allocate and map memory for fpga statistics */\n+\t{\n+\t\tuint32_t n_stat_size =\n+\t\t\t(uint32_t)(p_nthw_stat->m_nb_counters * sizeof(uint32_t) +\n+\t\t\t\t   sizeof(p_nthw_stat->mp_timestamp));\n+\t\tstruct nt_dma_s *p_dma;\n+\t\tint numa_node = p_adapter_info->fpga_info.numa_node;\n+\n+\t\t/* FPGA needs a 16K alignment on Statistics */\n+\t\tp_dma = nt_dma_alloc(n_stat_size, 0x4000, numa_node);\n+\n+\t\tif (!p_dma) {\n+\t\t\tNT_LOG(ERR, ETHDEV, \"%s: pDma alloc failed\\n\",\n+\t\t\t       __func__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: %x @%d %p %\" PRIX64 \" %\" PRIX64 \"\\n\", __func__,\n+\t\t       n_stat_size, numa_node, p_dma->addr, p_dma->iova);\n+\n+\t\tNT_LOG(DBG, ETHDEV,\n+\t\t       \"DMA: Physical adapter %02ld, PA = 0x%016\" PRIX64\n+\t\t       \" DMA = 0x%016\" PRIX64 \" size = 0x%\" PRIX64 \"\\n\",\n+\t\t       n_physical_adapter_no, p_dma->iova, p_dma->addr, n_stat_size);\n+\n+\t\tp_nt4ga_stat->p_stat_dma_virtual = (uint32_t *)p_dma->addr;\n+\t\tp_nt4ga_stat->n_stat_size = n_stat_size;\n+\t\tp_nt4ga_stat->p_stat_dma = p_dma;\n+\n+\t\tmemset(p_nt4ga_stat->p_stat_dma_virtual, 0xaa, n_stat_size);\n+\t\tnthw_stat_set_dma_address(p_nthw_stat, p_dma->iova,\n+\t\t\t\t       p_nt4ga_stat->p_stat_dma_virtual);\n+\t}\n+\n+\tif (p_nthw_rmc)\n+\t\tnthw_rmc_unblock(p_nthw_rmc, false);\n+\n+\tp_nt4ga_stat->mp_stat_structs_color = calloc(p_nthw_stat->m_nb_color_counters,\n+\t\t\t\t\t\tsizeof(struct color_counters));\n+\tif (!p_nt4ga_stat->mp_stat_structs_color) {\n+\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\", __func__,\n+\t\t       __LINE__);\n+\t\treturn -1;\n+\t}\n+\n+\tp_nt4ga_stat->mp_stat_structs_hb =\n+\t\tcalloc(p_nt4ga_stat->mn_rx_host_buffers + p_nt4ga_stat->mn_tx_host_buffers,\n+\t\t       sizeof(struct host_buffer_counters));\n+\tif (!p_nt4ga_stat->mp_stat_structs_hb) {\n+\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\", __func__,\n+\t\t       __LINE__);\n+\t\treturn -1;\n+\t}\n+\n+\t/*\n+\t * Separate memory allocation for VSWITCH and Inline to appropriate port counter structures.\n+\t */\n+\tif (p_nthw_stat->mb_is_vswitch) {\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx =\n+\t\t\tcalloc(p_nthw_stat->m_nb_rx_host_buffers,\n+\t\t\t       sizeof(struct port_counters_vswitch_v1));\n+\t\tif (!p_nt4ga_stat->virt.mp_stat_structs_port_rx) {\n+\t\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\",\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx =\n+\t\t\tcalloc(p_nthw_stat->m_nb_tx_host_buffers,\n+\t\t\t       sizeof(struct port_counters_vswitch_v1));\n+\t\tif (!p_nt4ga_stat->virt.mp_stat_structs_port_tx) {\n+\t\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\",\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tp_nt4ga_stat->flm_stat_ver = 0;\n+\t\tp_nt4ga_stat->mp_stat_structs_flm = NULL;\n+\t} else { /* Inline */\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx =\n+\t\t\tcalloc(NUM_ADAPTER_PORTS_MAX,\n+\t\t\t       sizeof(struct port_counters_v2));\n+\t\tif (!p_nt4ga_stat->cap.mp_stat_structs_port_rx) {\n+\t\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\",\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx =\n+\t\t\tcalloc(NUM_ADAPTER_PORTS_MAX,\n+\t\t\t       sizeof(struct port_counters_v2));\n+\t\tif (!p_nt4ga_stat->cap.mp_stat_structs_port_tx) {\n+\t\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\",\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tp_nt4ga_stat->flm_stat_ver = 0;\n+\n+\t\tp_nt4ga_stat->mp_stat_structs_flm =\n+\t\t\tcalloc(1, sizeof(struct flm_counters_v1));\n+\t\tif (!p_nt4ga_stat->mp_stat_structs_flm) {\n+\t\t\tNT_LOG(ERR, GENERAL, \"Cannot allocate mem (%s:%d).\\n\",\n+\t\t\t       __func__, __LINE__);\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\n+\tmemset(p_nt4ga_stat->a_stat_structs_color_base, 0,\n+\t       sizeof(struct color_counters) * NT_MAX_COLOR_FLOW_STATS);\n+\tp_nt4ga_stat->last_timestamp = 0;\n+\n+\tnthw_stat_trigger(p_nthw_stat);\n+\n+\treturn 0;\n+}\n+\n+int nt4ga_stat_stop(struct adapter_info_s *p_adapter_info)\n+{\n+\tnt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat;\n+\n+\tif (p_nt4ga_stat->virt.mp_stat_structs_port_rx) {\n+\t\tfree(p_nt4ga_stat->virt.mp_stat_structs_port_rx);\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx = NULL;\n+\t}\n+\tif (p_nt4ga_stat->cap.mp_stat_structs_port_rx) {\n+\t\tfree(p_nt4ga_stat->cap.mp_stat_structs_port_rx);\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx = NULL;\n+\t}\n+\n+\tif (p_nt4ga_stat->virt.mp_stat_structs_port_tx) {\n+\t\tfree(p_nt4ga_stat->virt.mp_stat_structs_port_tx);\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx = NULL;\n+\t}\n+\tif (p_nt4ga_stat->cap.mp_stat_structs_port_tx) {\n+\t\tfree(p_nt4ga_stat->cap.mp_stat_structs_port_tx);\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx = NULL;\n+\t}\n+\n+\tif (p_nt4ga_stat->mp_stat_structs_color) {\n+\t\tfree(p_nt4ga_stat->mp_stat_structs_color);\n+\t\tp_nt4ga_stat->mp_stat_structs_color = NULL;\n+\t}\n+\n+\tif (p_nt4ga_stat->mp_stat_structs_hb) {\n+\t\tfree(p_nt4ga_stat->mp_stat_structs_hb);\n+\t\tp_nt4ga_stat->mp_stat_structs_hb = NULL;\n+\t}\n+\n+\tif (p_nt4ga_stat->mp_stat_structs_flm) {\n+\t\tfree(p_nt4ga_stat->mp_stat_structs_flm);\n+\t\tp_nt4ga_stat->mp_stat_structs_flm = NULL;\n+\t}\n+\n+\tif (p_nt4ga_stat->p_stat_dma) {\n+\t\tnt_dma_free(p_nt4ga_stat->p_stat_dma);\n+\t\tp_nt4ga_stat->p_stat_dma = NULL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int nt4ga_stat_dump(struct adapter_info_s *p_adapter_info, FILE *pfh)\n+{\n+\tconst char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;\n+\tfpga_info_t *fpga_info = &p_adapter_info->fpga_info;\n+\tnt4ga_stat_t *p_nt4ga_stat = &p_adapter_info->nt4ga_stat;\n+\tint i;\n+\n+\tfor (i = 0; i < fpga_info->n_phy_ports; i++) {\n+\t\tfprintf(pfh,\n+\t\t\t\"%s: Intf %02d: Rx: %016\" PRIX64 \" %016\" PRIX64\n+\t\t\t\" %016\" PRIX64 \" Tx: %016\" PRIX64 \" %016\" PRIX64\n+\t\t\t\" %016\" PRIX64 \"\\n\",\n+\t\t\tp_adapter_id_str, i, p_nt4ga_stat->a_port_rx_packets_total[i],\n+\t\t\tp_nt4ga_stat->a_port_rx_octets_total[i],\n+\t\t\tp_nt4ga_stat->a_port_rx_drops_total[i],\n+\t\t\tp_nt4ga_stat->a_port_tx_packets_total[i],\n+\t\t\tp_nt4ga_stat->a_port_tx_octets_total[i],\n+\t\t\tp_nt4ga_stat->a_port_tx_drops_total[i]);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Called with stat mutex locked */\n+static int nt4ga_stat_collect_virt_v1_stats(nt4ga_stat_t *p_nt4ga_stat,\n+\t\t\t\t    uint32_t *p_stat_dma_virtual)\n+{\n+\tnthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat;\n+\tconst int n_rx_ports = p_nt4ga_stat->mn_rx_ports;\n+\tconst int n_tx_ports = p_nt4ga_stat->mn_tx_ports;\n+\tint c, h, p;\n+\n+\tif (!p_nthw_stat || !p_nt4ga_stat)\n+\t\treturn -1;\n+\n+\tif (p_nthw_stat->mn_stat_layout_version != 6) {\n+\t\tNT_LOG(ERR, ETHDEV, \"HW STA module version not supported\");\n+\t\treturn -1;\n+\t}\n+\n+\t/* RX ports */\n+\tfor (c = 0; c < p_nthw_stat->m_nb_color_counters / 2; c++) {\n+\t\tconst unsigned int tcp_flags_bits = 6U;\n+\t\tconst uint32_t val_mask_dma = 0xffffffffULL >> tcp_flags_bits;\n+\n+\t\tp_nt4ga_stat->mp_stat_structs_color[c].color_packets +=\n+\t\t\tp_stat_dma_virtual[c * 2] & val_mask_dma;\n+\t\tp_nt4ga_stat->mp_stat_structs_color[c].tcp_flags |=\n+\t\t\t(uint8_t)(p_stat_dma_virtual[c * 2] >>\n+\t\t\t\t  (32 - tcp_flags_bits));\n+\t\tp_nt4ga_stat->mp_stat_structs_color[c].color_bytes +=\n+\t\t\tp_stat_dma_virtual[c * 2 + 1];\n+\t}\n+\n+\t/* Move to Host buffer counters */\n+\tp_stat_dma_virtual += p_nthw_stat->m_nb_color_counters;\n+\n+\t/* Host buffer counters */\n+\tfor (h = 0; h < p_nthw_stat->m_nb_rx_host_buffers; h++) {\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].flush_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].drop_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 1];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].fwd_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 2];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 3];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].flush_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 4];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].drop_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 5];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].fwd_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 6];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 7];\n+\t}\n+\n+\t/* Move to Rx Port counters */\n+\tp_stat_dma_virtual += p_nthw_stat->m_nb_rx_hb_counters;\n+\n+\t/* RX ports */\n+\tfor (p = 0; p < n_rx_ports; p++) {\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx[p].octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx[p].pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx[p].drop_events +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx[p].qos_drop_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 3];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_rx[p].qos_drop_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 4];\n+\n+\t\t/* Rx totals */\n+\t\tp_nt4ga_stat->a_port_rx_octets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters];\n+\t\tp_nt4ga_stat->a_port_rx_packets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1];\n+\t\tp_nt4ga_stat->a_port_rx_drops_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2];\n+\t}\n+\n+\t/* Move to Tx Port counters */\n+\tp_stat_dma_virtual += n_rx_ports * p_nthw_stat->m_nb_rx_port_counters;\n+\n+\t/* TX ports */\n+\tfor (p = 0; p < n_tx_ports; p++) {\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx[p].octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx[p].pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx[p].drop_events +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx[p].qos_drop_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 3];\n+\t\tp_nt4ga_stat->virt.mp_stat_structs_port_tx[p].qos_drop_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 4];\n+\n+\t\t/* Tx totals */\n+\t\tp_nt4ga_stat->a_port_tx_octets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters];\n+\t\tp_nt4ga_stat->a_port_tx_packets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1];\n+\t\tp_nt4ga_stat->a_port_tx_drops_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2];\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* Called with stat mutex locked */\n+static int nt4ga_stat_collect_cap_v1_stats(nt4ga_stat_t *p_nt4ga_stat,\n+\t\t\t\t\t   uint32_t *p_stat_dma_virtual)\n+{\n+\tnthw_stat_t *p_nthw_stat = p_nt4ga_stat->mp_nthw_stat;\n+\n+\tconst int n_rx_ports = p_nt4ga_stat->mn_rx_ports;\n+\tconst int n_tx_ports = p_nt4ga_stat->mn_tx_ports;\n+\tint c, h, p;\n+\n+\tif (!p_nthw_stat || !p_nt4ga_stat)\n+\t\treturn -1;\n+\n+\tif (p_nthw_stat->mn_stat_layout_version != 6) {\n+\t\tNT_LOG(ERR, ETHDEV, \"HW STA module version not supported\");\n+\t\treturn -1;\n+\t}\n+\n+\t/* RX ports */\n+\tfor (c = 0; c < p_nthw_stat->m_nb_color_counters / 2; c++) {\n+\t\tp_nt4ga_stat->mp_stat_structs_color[c].color_packets +=\n+\t\t\tp_stat_dma_virtual[c * 2];\n+\t\tp_nt4ga_stat->mp_stat_structs_color[c].color_bytes +=\n+\t\t\tp_stat_dma_virtual[c * 2 + 1];\n+\t}\n+\n+\t/* Move to Host buffer counters */\n+\tp_stat_dma_virtual += p_nthw_stat->m_nb_color_counters;\n+\n+\tfor (h = 0; h < p_nthw_stat->m_nb_rx_host_buffers; h++) {\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].flush_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].drop_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 1];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].fwd_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 2];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_packets +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 3];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].flush_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 4];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].drop_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 5];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].fwd_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 6];\n+\t\tp_nt4ga_stat->mp_stat_structs_hb[h].dbs_drop_bytes +=\n+\t\t\tp_stat_dma_virtual[h * 8 + 7];\n+\t}\n+\n+\t/* Move to Rx Port counters */\n+\tp_stat_dma_virtual += p_nthw_stat->m_nb_rx_hb_counters;\n+\n+\t/* RX ports */\n+\tfor (p = 0; p < n_rx_ports; p++) {\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 0];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].broadcast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 1];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].multicast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 2];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].unicast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 3];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_alignment +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 4];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_code_violation +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 5];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_crc +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 6];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].undersize_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 7];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].oversize_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 8];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].fragments +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 9];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].jabbers_not_truncated +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 10];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].jabbers_truncated +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 11];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_64_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 12];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_65_to_127_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 13];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_128_to_255_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 14];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_256_to_511_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 15];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_512_to_1023_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 16];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p]\n+\t\t.pkts_1024_to_1518_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 17];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p]\n+\t\t.pkts_1519_to_2047_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 18];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p]\n+\t\t.pkts_2048_to_4095_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 19];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p]\n+\t\t.pkts_4096_to_8191_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 20];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_8192_to_max_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 21];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].mac_drop_events +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_lr +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 23];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].duplicate +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 24];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_ip_chksum_error +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 25];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_udp_chksum_error +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 26];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_tcp_chksum_error +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 27];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_giant_undersize +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 28];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_baby_giant +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 29];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_not_isl_vlan_mpls +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 30];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 31];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_vlan +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 32];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 33];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_mpls +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 34];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_mpls +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 35];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_vlan_mpls +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 36];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan_mpls +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 37];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_no_filter +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 38];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_dedup_drop +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 39];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_filter_drop +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 40];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_overflow +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 41];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts_dbs_drop +=\n+\t\t\tp_nthw_stat->m_dbs_present ?\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters +\n+\t\t\t\t\t  42] :\n+\t\t\t0;\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_no_filter +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 43];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_dedup_drop +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 44];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_filter_drop +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 45];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_overflow +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 46];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].octets_dbs_drop +=\n+\t\t\tp_nthw_stat->m_dbs_present ?\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters +\n+\t\t\t\t\t  47] :\n+\t\t\t0;\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_first_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 48];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_first_not_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 49];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_mid_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 50];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_mid_not_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 51];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_last_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 52];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].ipft_last_not_hit +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 53];\n+\n+\t\t/* Rx totals */\n+\t\tuint64_t new_drop_events_sum =\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 38] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 39] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 40] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 41] +\n+\t\t\t(p_nthw_stat->m_dbs_present ?\n+\t\t\t p_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters +\n+\t\t\t\t\t   42] :\n+\t\t\t 0);\n+\n+\t\tuint64_t new_packets_sum =\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 7] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 8] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 9] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 10] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 11] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 12] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 13] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 14] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 15] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 16] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 17] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 18] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 19] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 20] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 21];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].drop_events +=\n+\t\t\tnew_drop_events_sum;\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_rx[p].pkts += new_packets_sum;\n+\n+\t\tp_nt4ga_stat->a_port_rx_octets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 0];\n+\t\tp_nt4ga_stat->a_port_rx_packets_total[p] += new_packets_sum;\n+\t\tp_nt4ga_stat->a_port_rx_drops_total[p] += new_drop_events_sum;\n+\t}\n+\n+\t/* Move to Tx Port counters */\n+\tp_stat_dma_virtual += n_rx_ports * p_nthw_stat->m_nb_rx_port_counters;\n+\n+\tfor (p = 0; p < n_tx_ports; p++) {\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 0];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].broadcast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 1];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].multicast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 2];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].unicast_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 3];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_alignment +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 4];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_code_violation +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 5];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_crc +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 6];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].undersize_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 7];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].oversize_pkts +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 8];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].fragments +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 9];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].jabbers_not_truncated +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 10];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].jabbers_truncated +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 11];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_64_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 12];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_65_to_127_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 13];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_128_to_255_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 14];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_256_to_511_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 15];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_512_to_1023_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 16];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p]\n+\t\t.pkts_1024_to_1518_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 17];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p]\n+\t\t.pkts_1519_to_2047_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 18];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p]\n+\t\t.pkts_2048_to_4095_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 19];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p]\n+\t\t.pkts_4096_to_8191_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 20];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_8192_to_max_octets +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 21];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].mac_drop_events +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 22];\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts_lr +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 23];\n+\n+\t\t/* Tx totals */\n+\t\tuint64_t new_drop_events_sum =\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_rx_port_counters + 22];\n+\n+\t\tuint64_t new_packets_sum =\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 7] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 8] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 9] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 10] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 11] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 12] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 13] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 14] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 15] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 16] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 17] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 18] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 19] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 20] +\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 21];\n+\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].drop_events +=\n+\t\t\tnew_drop_events_sum;\n+\t\tp_nt4ga_stat->cap.mp_stat_structs_port_tx[p].pkts += new_packets_sum;\n+\n+\t\tp_nt4ga_stat->a_port_tx_octets_total[p] +=\n+\t\t\tp_stat_dma_virtual[p * p_nthw_stat->m_nb_tx_port_counters + 0];\n+\t\tp_nt4ga_stat->a_port_tx_packets_total[p] += new_packets_sum;\n+\t\tp_nt4ga_stat->a_port_tx_drops_total[p] += new_drop_events_sum;\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ntnic/adapter/nt4ga_stat.h b/drivers/net/ntnic/adapter/nt4ga_stat.h\nnew file mode 100644\nindex 0000000000..4a1067200c\n--- /dev/null\n+++ b/drivers/net/ntnic/adapter/nt4ga_stat.h\n@@ -0,0 +1,202 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NT4GA_STAT_H_\n+#define NT4GA_STAT_H_\n+\n+#include \"nt_util.h\"\n+#include \"common_adapter_defs.h\"\n+\n+#define NT_MAX_COLOR_FLOW_STATS 0x400\n+\n+struct color_counters {\n+\tuint64_t color_packets;\n+\tuint64_t color_bytes;\n+\tuint8_t tcp_flags;\n+};\n+\n+struct host_buffer_counters {\n+\tuint64_t flush_packets;\n+\tuint64_t drop_packets;\n+\tuint64_t fwd_packets;\n+\tuint64_t dbs_drop_packets;\n+\tuint64_t flush_bytes;\n+\tuint64_t drop_bytes;\n+\tuint64_t fwd_bytes;\n+\tuint64_t dbs_drop_bytes;\n+};\n+\n+struct port_counters_v2 {\n+\t/* Rx/Tx common port counters */\n+\tuint64_t drop_events;\n+\tuint64_t pkts;\n+\t/* FPGA counters */\n+\tuint64_t octets;\n+\tuint64_t broadcast_pkts;\n+\tuint64_t multicast_pkts;\n+\tuint64_t unicast_pkts;\n+\tuint64_t pkts_alignment;\n+\tuint64_t pkts_code_violation;\n+\tuint64_t pkts_crc;\n+\tuint64_t undersize_pkts;\n+\tuint64_t oversize_pkts;\n+\tuint64_t fragments;\n+\tuint64_t jabbers_not_truncated;\n+\tuint64_t jabbers_truncated;\n+\tuint64_t pkts_64_octets;\n+\tuint64_t pkts_65_to_127_octets;\n+\tuint64_t pkts_128_to_255_octets;\n+\tuint64_t pkts_256_to_511_octets;\n+\tuint64_t pkts_512_to_1023_octets;\n+\tuint64_t pkts_1024_to_1518_octets;\n+\tuint64_t pkts_1519_to_2047_octets;\n+\tuint64_t pkts_2048_to_4095_octets;\n+\tuint64_t pkts_4096_to_8191_octets;\n+\tuint64_t pkts_8192_to_max_octets;\n+\tuint64_t mac_drop_events;\n+\tuint64_t pkts_lr;\n+\t/* Rx only port counters */\n+\tuint64_t duplicate;\n+\tuint64_t pkts_ip_chksum_error;\n+\tuint64_t pkts_udp_chksum_error;\n+\tuint64_t pkts_tcp_chksum_error;\n+\tuint64_t pkts_giant_undersize;\n+\tuint64_t pkts_baby_giant;\n+\tuint64_t pkts_not_isl_vlan_mpls;\n+\tuint64_t pkts_isl;\n+\tuint64_t pkts_vlan;\n+\tuint64_t pkts_isl_vlan;\n+\tuint64_t pkts_mpls;\n+\tuint64_t pkts_isl_mpls;\n+\tuint64_t pkts_vlan_mpls;\n+\tuint64_t pkts_isl_vlan_mpls;\n+\tuint64_t pkts_no_filter;\n+\tuint64_t pkts_dedup_drop;\n+\tuint64_t pkts_filter_drop;\n+\tuint64_t pkts_overflow;\n+\tuint64_t pkts_dbs_drop;\n+\tuint64_t octets_no_filter;\n+\tuint64_t octets_dedup_drop;\n+\tuint64_t octets_filter_drop;\n+\tuint64_t octets_overflow;\n+\tuint64_t octets_dbs_drop;\n+\tuint64_t ipft_first_hit;\n+\tuint64_t ipft_first_not_hit;\n+\tuint64_t ipft_mid_hit;\n+\tuint64_t ipft_mid_not_hit;\n+\tuint64_t ipft_last_hit;\n+\tuint64_t ipft_last_not_hit;\n+};\n+\n+struct port_counters_vswitch_v1 {\n+\t/* Rx/Tx common port counters */\n+\tuint64_t octets;\n+\tuint64_t pkts;\n+\tuint64_t drop_events;\n+\tuint64_t qos_drop_octets;\n+\tuint64_t qos_drop_pkts;\n+};\n+\n+struct flm_counters_v1 {\n+\t/* FLM 0.17 */\n+\tuint64_t current;\n+\tuint64_t learn_done;\n+\tuint64_t learn_ignore;\n+\tuint64_t learn_fail;\n+\tuint64_t unlearn_done;\n+\tuint64_t unlearn_ignore;\n+\tuint64_t auto_unlearn_done;\n+\tuint64_t auto_unlearn_ignore;\n+\tuint64_t auto_unlearn_fail;\n+\tuint64_t timeout_unlearn_done;\n+\tuint64_t rel_done;\n+\tuint64_t rel_ignore;\n+\t/* FLM 0.20 */\n+\tuint64_t prb_done;\n+\tuint64_t prb_ignore;\n+\tuint64_t sta_done;\n+\tuint64_t inf_done;\n+\tuint64_t inf_skip;\n+\tuint64_t pck_hit;\n+\tuint64_t pck_miss;\n+\tuint64_t pck_unh;\n+\tuint64_t pck_dis;\n+\tuint64_t csh_hit;\n+\tuint64_t csh_miss;\n+\tuint64_t csh_unh;\n+\tuint64_t cuc_start;\n+\tuint64_t cuc_move;\n+};\n+\n+struct nt4ga_stat_s {\n+\tnthw_stat_t *mp_nthw_stat;\n+\tnthw_rmc_t *mp_nthw_rmc;\n+\tstruct nt_dma_s *p_stat_dma;\n+\tuint32_t *p_stat_dma_virtual;\n+\tuint32_t n_stat_size;\n+\n+\tuint64_t last_timestamp;\n+\n+\tint mn_rx_host_buffers;\n+\tint mn_tx_host_buffers;\n+\n+\tint mn_rx_ports;\n+\tint mn_tx_ports;\n+\n+\tstruct color_counters *mp_stat_structs_color;\n+\t/* For calculating increments between stats polls */\n+\tstruct color_counters a_stat_structs_color_base[NT_MAX_COLOR_FLOW_STATS];\n+\n+\tunion {\n+\t\t/*Port counters for VSWITCH/inline */\n+\t\tstruct {\n+\t\t\tstruct port_counters_vswitch_v1 *mp_stat_structs_port_rx;\n+\t\t\tstruct port_counters_vswitch_v1 *mp_stat_structs_port_tx;\n+\t\t} virt;\n+\t\tstruct {\n+\t\t\tstruct port_counters_v2 *mp_stat_structs_port_rx;\n+\t\t\tstruct port_counters_v2 *mp_stat_structs_port_tx;\n+\t\t} cap;\n+\t};\n+\n+\tstruct host_buffer_counters *mp_stat_structs_hb;\n+\n+\tint flm_stat_ver;\n+\tstruct flm_counters_v1 *mp_stat_structs_flm;\n+\n+\t/* Rx/Tx totals: */\n+\tuint64_t n_totals_reset_timestamp; /* timestamp for last totals reset */\n+\n+\tuint64_t a_port_rx_octets_total[NUM_ADAPTER_PORTS_MAX];\n+\t/* Base is for calculating increments between statistics reads */\n+\tuint64_t a_port_rx_octets_base[NUM_ADAPTER_PORTS_MAX];\n+\n+\tuint64_t a_port_rx_packets_total[NUM_ADAPTER_PORTS_MAX];\n+\tuint64_t a_port_rx_packets_base[NUM_ADAPTER_PORTS_MAX];\n+\n+\tuint64_t a_port_rx_drops_total[NUM_ADAPTER_PORTS_MAX];\n+\tuint64_t a_port_rx_drops_base[NUM_ADAPTER_PORTS_MAX];\n+\n+\tuint64_t a_port_tx_octets_total[NUM_ADAPTER_PORTS_MAX];\n+\tuint64_t a_port_tx_octets_base[NUM_ADAPTER_PORTS_MAX];\n+\n+\tuint64_t a_port_tx_packets_base[NUM_ADAPTER_PORTS_MAX];\n+\tuint64_t a_port_tx_packets_total[NUM_ADAPTER_PORTS_MAX];\n+\n+\tuint64_t a_port_tx_drops_base[NUM_ADAPTER_PORTS_MAX];\n+\tuint64_t a_port_tx_drops_total[NUM_ADAPTER_PORTS_MAX];\n+};\n+\n+typedef struct nt4ga_stat_s nt4ga_stat_t;\n+\n+int nt4ga_stat_init(struct adapter_info_s *p_adapter_info);\n+int nt4ga_stat_setup(struct adapter_info_s *p_adapter_info);\n+int nt4ga_stat_stop(struct adapter_info_s *p_adapter_info);\n+\n+int nt4ga_stat_dump(struct adapter_info_s *p_adapter_info, FILE *pfh);\n+\n+int nt4ga_stat_collect(struct adapter_info_s *p_adapter_info,\n+\t\t      nt4ga_stat_t *p_nt4ga_stat);\n+\n+#endif /* NT4GA_STAT_H_ */\ndiff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build\nindex 46913c0c74..ae43254f9f 100644\n--- a/drivers/net/ntnic/meson.build\n+++ b/drivers/net/ntnic/meson.build\n@@ -4,22 +4,39 @@\n # includes\n includes = [\n     include_directories('.'),\n+    include_directories('adapter'),\n     include_directories('include'),\n+    include_directories('nim'),\n     include_directories('ntlog/include'),\n     include_directories('ntutil/include'),\n     include_directories('nthw'),\n     include_directories('nthw/core'),\n     include_directories('nthw/supported'),\n+    include_directories('sensors'),\n+    include_directories('sensors/avr_sensors'),\n+    include_directories('sensors/board_sensors'),\n+    include_directories('sensors/nim_sensors'),\n+    include_directories('sensors/ntavr'),\n ]\n \n # all sources\n sources = files(\n+    'adapter/nt4ga_adapter.c',\n+    'adapter/nt4ga_link.c',\n+    'adapter/nt4ga_link_100g.c',\n+    'adapter/nt4ga_pci_ta_tg.c',\n+    'adapter/nt4ga_stat.c',\n+    'nim/i2c_nim.c',\n+    'nim/nt_link_speed.c',\n+    'nim/qsfp_sensors.c',\n+    'nim/sfp_sensors.c',\n     'nthw/core/nthw_clock_profiles.c',\n     'nthw/core/nthw_fpga.c',\n     'nthw/core/nthw_fpga_nt200a0x.c',\n     'nthw/core/nthw_fpga_rst.c',\n     'nthw/core/nthw_fpga_rst9563.c',\n     'nthw/core/nthw_fpga_rst_nt200a0x.c',\n+    'nthw/core/nthw_gmf.c',\n     'nthw/core/nthw_gpio_phy.c',\n     'nthw/core/nthw_hif.c',\n     'nthw/core/nthw_iic.c',\n@@ -29,6 +46,7 @@ sources = files(\n     'nthw/core/nthw_pci_ta.c',\n     'nthw/core/nthw_pci_wr_tg.c',\n     'nthw/core/nthw_pcie3.c',\n+    'nthw/core/nthw_rmc.c',\n     'nthw/core/nthw_sdc.c',\n     'nthw/core/nthw_si5340.c',\n     'nthw/core/nthw_spi_v3.c',\n@@ -44,6 +62,12 @@ sources = files(\n     'nthw/supported/nthw_fpga_9563_055_024_0000.c',\n     'ntlog/ntlog.c',\n     'ntutil/nt_util.c',\n+    'sensors/avr_sensors/avr_sensors.c',\n+    'sensors/board_sensors/board_sensors.c',\n+    'sensors/board_sensors/tempmon.c',\n+    'sensors/nim_sensors/nim_sensors.c',\n+    'sensors/ntavr/ntavr.c',\n+    'sensors/sensors.c',\n )\n \n if is_variable('default_cflags')\ndiff --git a/drivers/net/ntnic/nim/i2c_nim.c b/drivers/net/ntnic/nim/i2c_nim.c\nnew file mode 100644\nindex 0000000000..55740e6de6\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/i2c_nim.c\n@@ -0,0 +1,1974 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"nthw_drv.h\"\n+#include \"i2c_nim.h\"\n+#include \"ntlog.h\"\n+#include \"nt_util.h\"\n+\n+#include \"nim_sensors.h\"\n+#include \"sfp_p_registers.h\"\n+#include \"qsfp_registers.h\"\n+#include \"sfp_sensors.h\"\n+#include \"qsfp_sensors.h\"\n+\n+#include <assert.h>\n+#include <string.h> /* memcmp, memset */\n+\n+/*\n+ * Nim functions\n+ */\n+#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */\n+#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */\n+#define QSFP_CONTROL_STATUS_LIN_ADDR 86\n+#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F\n+#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */\n+#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */\n+#define NIM_READ false\n+#define NIM_WRITE true\n+#define NIM_PAGE_SEL_REGISTER 127\n+#define nim_i2c_0xa0 0xA0 /* Basic I2C address */\n+#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */\n+#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */\n+#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */\n+#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */\n+#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */\n+#define QSFP_DMI_OPTION_LIN_ADDR 220\n+\n+#define QSFP_EXTENDED_IDENTIFIER 129\n+#define QSFP_POWER_CLASS_BITS_1_4 0xC0\n+#define QSFP_POWER_CLASS_BITS_5_7 0x03\n+\n+static bool sfp_is_supported_tri_speed_pn(char *prod_no)\n+{\n+\tstatic const char *const pn_trispeed_list[] = {\n+\t\t\"FCMJ-8521-3\", \"FCLF-8521-3\", \"FCLF8521P2BTL\", \"EOLT-C12-02A\",\n+\t\t\"AMXP-24RJS\",  \"ABCU-5710RZ\", \"ABCU-5740RZ\",   \"FCLF8522P2BTL\",\n+\t};\n+\n+\t/* Determine if copper SFP is supported 3-speed type */\n+\tfor (size_t i = 0; i < ARRAY_SIZE(pn_trispeed_list); i++)\n+\t\tif (strcmp(pn_trispeed_list[i], prod_no) == 0)\n+\t\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+static bool page_addressing(nt_nim_identifier_t id)\n+{\n+\tswitch (id) {\n+\tcase NT_NIM_SFP_SFP_PLUS:\n+\t\treturn false;\n+\tcase NT_NIM_XFP:\n+\t\treturn true;\n+\tcase NT_NIM_QSFP:\n+\tcase NT_NIM_QSFP_PLUS:\n+\tcase NT_NIM_QSFP28:\n+\t\treturn true;\n+\tdefault:\n+\t\tNT_LOG(DBG, ETHDEV, \"%s: Unknown NIM identifier %d\\n\", __func__,\n+\t\t       id);\n+\t\treturn false;\n+\t}\n+}\n+\n+nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx)\n+{\n+\treturn (nt_nim_identifier_t)ctx->nim_id;\n+}\n+\n+static int nim_read_write_i2c_data(nim_i2c_ctx_p ctx, bool do_write,\n+\t\t\t\tuint16_t lin_addr, uint8_t i2c_addr,\n+\t\t\t\tuint8_t reg_addr, uint8_t seq_cnt, uint8_t *p_data)\n+{\n+\t/* Divide I2C_Addr by 2 because nthw_iic_read/writeData multiplies by 2 */\n+\tconst uint8_t i2c_devaddr = i2c_addr / 2U;\n+\t(void)lin_addr; /* Unused */\n+\n+\tif (do_write)\n+\t\treturn nthw_iic_write_data(&ctx->hwiic, i2c_devaddr, reg_addr,\n+\t\t\t\t\t seq_cnt, p_data);\n+\telse\n+\t\treturn nthw_iic_read_data(&ctx->hwiic, i2c_devaddr, reg_addr,\n+\t\t\t\t\tseq_cnt, p_data);\n+}\n+\n+/*\n+ * ------------------------------------------------------------------------------\n+ * Selects a new page for page addressing. This is only relevant if the NIM\n+ * supports this. Since page switching can take substantial time the current page\n+ * select is read and subsequently only changed if necessary.\n+ * Important:\n+ * XFP Standard 8077, Ver 4.5, Page 61 states that:\n+ * If the host attempts to write a table select value which is not supported in\n+ * a particular module, the table select byte will revert to 01h.\n+ * This can lead to some surprising result that some pages seems to be duplicated.\n+ * ------------------------------------------------------------------------------\n+ */\n+\n+static int nim_setup_page(nim_i2c_ctx_p ctx, uint8_t page_sel)\n+{\n+\tuint8_t curr_page_sel;\n+\n+\t/* Read the current page select value */\n+\tif (nim_read_write_i2c_data(ctx, NIM_READ, NIM_PAGE_SEL_REGISTER,\n+\t\t\t\t nim_i2c_0xa0, NIM_PAGE_SEL_REGISTER,\n+\t\t\t\t sizeof(curr_page_sel), &curr_page_sel) != 0)\n+\t\treturn -1;\n+\n+\t/* Only write new page select value if necessary */\n+\tif (page_sel != curr_page_sel) {\n+\t\tif (nim_read_write_i2c_data(ctx, NIM_WRITE, NIM_PAGE_SEL_REGISTER,\n+\t\t\t\t\t nim_i2c_0xa0, NIM_PAGE_SEL_REGISTER,\n+\t\t\t\t\t sizeof(page_sel), &page_sel) != 0)\n+\t\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int nim_nim_read_write_data_lin(nim_i2c_ctx_p ctx, bool m_page_addressing,\n+\t\t\t\t   uint16_t lin_addr, uint16_t length,\n+\t\t\t\t   uint8_t *p_data, bool do_write)\n+{\n+\tuint16_t i;\n+\tuint8_t reg_addr; /* The actual register address in I2C device */\n+\tuint8_t i2c_addr;\n+\tint block_size = 128; /* Equal to size of MSA pages */\n+\tint seq_cnt;\n+\tint max_seq_cnt = 1;\n+\tint multi_byte = 1; /* One byte per I2C register is default */\n+\tconst int m_port_no = ctx->instance - 2;\n+\n+\tif (lin_addr >= SFP_PHY_LIN_ADDR) {\n+\t\t/*\n+\t\t * This represents an address space at I2C address 0xAC for SFP modules\n+\t\t * containing a PHY. (eg 1G Copper SFP). Each register is 16bit and is\n+\t\t * accessed MSByte first and this reading latches the LSByte that is\n+\t\t * subsequently read from the same address.\n+\t\t */\n+\t\tmulti_byte = 2;\n+\t\tmax_seq_cnt = 2;\n+\n+\t\t/* Test for correct multibyte access */\n+\t\tif ((length % multi_byte) != 0) {\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"Port %d: %s: Uneven length (%d) for address range [0x%X..0x%X].\",\n+\t\t\t       m_port_no, __func__, length, SFP_PHY_LIN_ADDR,\n+\t\t\t       SFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG - 1);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (lin_addr + (length / 2) >\n+\t\t\t\tSFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG) {\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"Port %d: %s: Access above address range [0x%X..0x%X].\",\n+\t\t\t       m_port_no, __func__, SFP_PHY_LIN_ADDR,\n+\t\t\t       SFP_PHY_LIN_ADDR + SFP_PHY_LIN_RNG - 1);\n+\t\t\treturn -1;\n+\t\t}\n+\t} else if (lin_addr + length > 128) {\n+\t\t/*\n+\t\t * Page addressing could be relevant since the last byte is outside the\n+\t\t * basic range so check if it is enabled\n+\t\t */\n+\t\tif (m_page_addressing) {\n+\t\t\t/* Crossing into the PHY address range is not allowed */\n+\t\t\tif (lin_addr + length > SFP_PHY_LIN_ADDR) {\n+\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t       \"Port %d: %s: Access above paged address range [0..0x%X].\",\n+\t\t\t\t       m_port_no, __func__, SFP_PHY_LIN_ADDR);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t} else {\n+\t\t\t/* Access outside 0xA2 address range not allowed */\n+\t\t\tif (lin_addr + length > 512) {\n+\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t       \"Port %d: %s: Access above address range [0..511].\",\n+\t\t\t\t       m_port_no, __func__);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t/* No missing else here - all devices supports access to address [0..127] */\n+\n+\tfor (i = 0; i < length;) {\n+\t\tbool use_page_select = false;\n+\n+\t\t/*\n+\t\t * Find out how much can be read from the current block in case of\n+\t\t * single byte access\n+\t\t */\n+\t\tif (multi_byte == 1)\n+\t\t\tmax_seq_cnt = block_size - (lin_addr % block_size);\n+\n+\t\tif (m_page_addressing) {\n+\t\t\tif (lin_addr >= 128) { /* Only page setup above this address */\n+\t\t\t\tuse_page_select = true;\n+\n+\t\t\t\t/* Map to [128..255] of 0xA0 device */\n+\t\t\t\treg_addr = (uint8_t)(block_size +\n+\t\t\t\t\t\t    (lin_addr % block_size));\n+\t\t\t} else {\n+\t\t\t\treg_addr = (uint8_t)lin_addr;\n+\t\t\t}\n+\t\t\ti2c_addr = nim_i2c_0xa0; /* Base I2C address */\n+\t\t} else {\n+\t\t\tif (lin_addr >= SFP_PHY_LIN_ADDR) {\n+\t\t\t\t/* Map to address [0..31] of 0xAC device */\n+\t\t\t\treg_addr = (uint8_t)(lin_addr - SFP_PHY_LIN_ADDR);\n+\t\t\t\ti2c_addr = nim_i2c_0xac;\n+\t\t\t} else if (lin_addr >= 256) {\n+\t\t\t\t/* Map to address [0..255] of 0xA2 device */\n+\t\t\t\treg_addr = (uint8_t)(lin_addr - 256);\n+\t\t\t\ti2c_addr = nim_i2c_0xa2;\n+\t\t\t} else {\n+\t\t\t\treg_addr = (uint8_t)lin_addr;\n+\t\t\t\ti2c_addr = nim_i2c_0xa0; /* Base I2C address */\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* Now actually do the reading/writing */\n+\t\tseq_cnt = length - i; /* Number of remaining bytes */\n+\n+\t\tif (seq_cnt > max_seq_cnt)\n+\t\t\tseq_cnt = max_seq_cnt;\n+\n+\t\t/*\n+\t\t * Read a number of bytes without explicitly specifying a new address.\n+\t\t * This can speed up I2C access since automatic incrementation of the\n+\t\t * I2C device internal address counter can be used. It also allows\n+\t\t * a HW implementation, that can deal with block access.\n+\t\t * Furthermore it also allows for access to data that must be accessed\n+\t\t * as 16bit words reading two bytes at each address eg PHYs.\n+\t\t */\n+\t\tif (use_page_select) {\n+\t\t\tif (nim_setup_page(ctx,\n+\t\t\t\t\t   (uint8_t)((lin_addr / 128) - 1)) != 0) {\n+\t\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t\t       \"%s: Cannot set up page for linear address %u\\n\",\n+\t\t\t\t       __func__, lin_addr);\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t\tif (nim_read_write_i2c_data(ctx, do_write, lin_addr, i2c_addr,\n+\t\t\t\t\t    reg_addr, (uint8_t)seq_cnt,\n+\t\t\t\t\t    p_data) != 0) {\n+\t\t\tNT_LOG(ERR, ETHDEV,\n+\t\t\t       \"%s: Call to NIM_ReadWriteI2cData failed\\n\",\n+\t\t\t       __func__);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tp_data += seq_cnt;\n+\t\ti = (uint16_t)(i + seq_cnt);\n+\t\tlin_addr = (uint16_t)(lin_addr + (seq_cnt / multi_byte));\n+\t}\n+\treturn 0;\n+}\n+\n+int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length,\n+\t\tvoid *data)\n+{\n+\treturn nim_nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id),\n+\t\t\t\t       lin_addr, length, data, NIM_READ);\n+}\n+\n+static int write_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length,\n+\t\t\tvoid *data)\n+{\n+\treturn nim_nim_read_write_data_lin(ctx, page_addressing(ctx->nim_id),\n+\t\t\t\t       lin_addr, length, data, NIM_WRITE);\n+}\n+\n+/* Read and return a single byte */\n+static uint8_t read_byte(nim_i2c_ctx_p ctx, uint16_t addr)\n+{\n+\tuint8_t data;\n+\n+\tread_data_lin(ctx, addr, sizeof(data), &data);\n+\treturn data;\n+}\n+\n+static int nim_read_id(nim_i2c_ctx_t *ctx)\n+{\n+\t/* We are only reading the first byte so we don't care about pages here. */\n+\tconst bool use_page_addressing = false;\n+\n+\tif (nim_nim_read_write_data_lin(ctx, use_page_addressing,\n+\t\t\t\t    NIM_IDENTIFIER_ADDR, sizeof(ctx->nim_id),\n+\t\t\t\t    &ctx->nim_id, NIM_READ) != 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static int i2c_nim_common_construct(nim_i2c_ctx_p ctx)\n+{\n+\tctx->nim_id = 0;\n+\tint res = nim_read_id(ctx);\n+\n+\tif (res) {\n+\t\tNT_LOG(ERR, PMD, \"Can't read NIM id.\");\n+\t\treturn res;\n+\t}\n+\tmemset(ctx->vendor_name, 0, sizeof(ctx->vendor_name));\n+\tmemset(ctx->prod_no, 0, sizeof(ctx->prod_no));\n+\tmemset(ctx->serial_no, 0, sizeof(ctx->serial_no));\n+\tmemset(ctx->date, 0, sizeof(ctx->date));\n+\tmemset(ctx->rev, 0, sizeof(ctx->rev));\n+\n+\tctx->content_valid = false;\n+\tmemset(ctx->len_info, 0, sizeof(ctx->len_info));\n+\tctx->pwr_level_req = 0;\n+\tctx->pwr_level_cur = 0;\n+\tctx->avg_pwr = false;\n+\tctx->tx_disable = false;\n+\tctx->lane_idx = -1;\n+\tctx->lane_count = 1;\n+\tctx->options = 0;\n+\treturn 0;\n+}\n+\n+static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr,\n+\t\t\t\t uint8_t max_len, char *p_data);\n+\n+#define XSFP_READ_VENDOR_INFO(x)                                             \\\n+\tstatic void x##sfp_read_vendor_info(nim_i2c_ctx_t *ctx)              \\\n+\t{                                                                    \\\n+\t\tnim_read_vendor_info(ctx, Q##SFP_VENDOR_NAME_LIN_ADDR,      \\\n+\t\t\t\t      sizeof(ctx->vendor_name),               \\\n+\t\t\t\t      ctx->vendor_name);                      \\\n+\t\tnim_read_vendor_info(ctx, Q##SFP_VENDOR_PN_LIN_ADDR,        \\\n+\t\t\t\t      sizeof(ctx->prod_no), ctx->prod_no);     \\\n+\t\tnim_read_vendor_info(ctx, Q##SFP_VENDOR_SN_LIN_ADDR,        \\\n+\t\t\t\t      sizeof(ctx->serial_no), ctx->serial_no); \\\n+\t\tnim_read_vendor_info(ctx, Q##SFP_VENDOR_DATE_LIN_ADDR,      \\\n+\t\t\t\t      sizeof(ctx->date), ctx->date);         \\\n+\t\tnim_read_vendor_info(ctx, Q##SFP_VENDOR_REV_LIN_ADDR,       \\\n+\t\t\t\t      (uint8_t)(sizeof(ctx->rev) - 2),       \\\n+\t\t\t\t      ctx->rev); /*OBS Only two bytes*/      \\\n+\t}\n+\n+XSFP_READ_VENDOR_INFO()\n+XSFP_READ_VENDOR_INFO(q)\n+\n+static int sfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)\n+{\n+\tint res;\n+\n+\tassert(ctx && state);\n+\tassert(ctx->nim_id != NT_NIM_UNKNOWN && \"Nim is not initialized\");\n+\n+\t(void)memset(state, 0, sizeof(*state));\n+\n+\tres = nthw_iic_read_data(&ctx->hwiic, ctx->devaddr, SFP_BIT_RATE_ADDR,\n+\t\t\t       sizeof(state->br), &state->br);\n+\treturn res;\n+}\n+\n+static int qsfp_nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)\n+{\n+\tint res = 0; /* unused due to no readings from HW */\n+\n+\tassert(ctx && state);\n+\tassert(ctx->nim_id != NT_NIM_UNKNOWN && \"Nim is not initialized\");\n+\n+\t(void)memset(state, 0, sizeof(*state));\n+\n+\tswitch (ctx->nim_id) {\n+\tcase 12U:\n+\t\tstate->br = 10U; /* QSFP: 4 x 1G = 4G */\n+\t\tbreak;\n+\tcase 13U:\n+\t\tstate->br = 103U; /* QSFP+: 4 x 10G = 40G */\n+\t\tbreak;\n+\tcase 17U:\n+\t\tstate->br = 255U; /* QSFP28: 4 x 25G = 100G */\n+\t\tbreak;\n+\tdefault:\n+\t\tNT_LOG(INF, PMD,\n+\t\t       \"%s:%d nim_id = %u is not an QSFP/QSFP+/QSFP28 module\\n\",\n+\t\t       __func__, __LINE__, ctx->nim_id);\n+\t\tres = -1;\n+\t}\n+\n+\treturn res;\n+}\n+\n+int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state)\n+{\n+\tif (translate_nimid(ctx) == NT_NIM_SFP_SFP_PLUS)\n+\t\treturn sfp_nim_state_build(ctx, state);\n+\telse\n+\t\treturn qsfp_nim_state_build(ctx, state);\n+}\n+\n+const char *nim_id_to_text(uint8_t nim_id)\n+{\n+\tswitch (nim_id) {\n+\tcase 0x0:\n+\t\treturn \"UNKNOWN\";\n+\tcase 0x1:\n+\t\treturn \"GBIC\";\n+\tcase 0x2:\n+\t\treturn \"FIXED\";\n+\tcase 0x3:\n+\t\treturn \"SFP/SFP+\";\n+\tcase 0x04:\n+\t\treturn \"300 pin XBI\";\n+\tcase 0x05:\n+\t\treturn \"XEN-PAK\";\n+\tcase 0x06:\n+\t\treturn \"XFP\";\n+\tcase 0x07:\n+\t\treturn \"XFF\";\n+\tcase 0x08:\n+\t\treturn \"XFP-E\";\n+\tcase 0x09:\n+\t\treturn \"XPAK\";\n+\tcase 0x0A:\n+\t\treturn \"X2\";\n+\tcase 0x0B:\n+\t\treturn \"DWDM\";\n+\tcase 0x0C:\n+\t\treturn \"QSFP\";\n+\tcase 0x0D:\n+\t\treturn \"QSFP+\";\n+\tcase 0x11:\n+\t\treturn \"QSFP28\";\n+\tcase 0x12:\n+\t\treturn \"CFP4\";\n+\tdefault:\n+\t\treturn \"ILLEGAL!\";\n+\t}\n+}\n+\n+/*\n+ * Read and check the validity of the NIM basic data.\n+ * This will also preload the cache\n+ */\n+static void check_content_valid(nim_i2c_ctx_p ctx, uint16_t start_addr)\n+{\n+\tuint32_t sum = 0;\n+\tuint8_t buf[96];\n+\n+\tread_data_lin(ctx, start_addr, sizeof(buf), &buf[0]);\n+\n+\tfor (int i = 0; i < 63; i++)\n+\t\tsum += buf[i];\n+\n+\tif ((sum & 0xFF) != buf[63]) {\n+\t\tctx->content_valid = false;\n+\t} else {\n+\t\tsum = 0;\n+\n+\t\tfor (int i = 64; i < 95; i++)\n+\t\t\tsum += buf[i];\n+\n+\t\tctx->content_valid = ((sum & 0xFF) == buf[95]);\n+\t}\n+\tif (ctx->content_valid)\n+\t\tNT_LOG(DBG, NTHW, \"NIM content validation passed\");\n+\telse\n+\t\tNT_LOG(WRN, NTHW, \"NIM content validation failed\");\n+}\n+\n+/*\n+ * Set/reset Soft Rate__select bits (RS0 & RS1)\n+ */\n+static void nim_sfp_set_rate_sel_high(nim_i2c_ctx_p ctx, bool rx_rate_high,\n+\t\t\t\t  bool tx_rate_high)\n+{\n+\tconst bool m_page_addressing = page_addressing(ctx->nim_id);\n+\tuint8_t data;\n+\n+\tnim_nim_read_write_data_lin(ctx, m_page_addressing,\n+\t\t\t\tSFP_CONTROL_STATUS_LIN_ADDR, sizeof(data),\n+\t\t\t\t&data, NIM_READ);\n+\n+\tif (rx_rate_high)\n+\t\tdata |= SFP_SOFT_RATE0_BIT;\n+\telse\n+\t\tdata &= (uint8_t)~(SFP_SOFT_RATE0_BIT);\n+\n+\tnim_nim_read_write_data_lin(ctx, m_page_addressing,\n+\t\t\t\tSFP_CONTROL_STATUS_LIN_ADDR, sizeof(data),\n+\t\t\t\t&data, NIM_WRITE);\n+\n+\t/* Read the Extended Status/Control and set/reset Soft RS1 bit */\n+\tnim_nim_read_write_data_lin(ctx, m_page_addressing,\n+\t\t\t\tSFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(data),\n+\t\t\t\t&data, NIM_READ);\n+\n+\tif (tx_rate_high)\n+\t\tdata |= SFP_SOFT_RATE1_BIT;\n+\telse\n+\t\tdata &= (uint8_t)~(SFP_SOFT_RATE1_BIT);\n+\n+\tnim_nim_read_write_data_lin(ctx, m_page_addressing,\n+\t\t\t\tSFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(data),\n+\t\t\t\t&data, NIM_WRITE);\n+}\n+\n+/*\n+ * Some NIM modules requires some changes to a rate setting.\n+ */\n+static int nim_sfp_set_rate_select(nim_i2c_ctx_p ctx, nt_link_speed_t speed)\n+{\n+\tif ((speed & (int)ctx->speed_mask) == 0) {\n+\t\tchar buf[128];\n+\n+\t\tNT_LOG(ERR, ETHDEV, \"%s - Speed (%s) not within SpeedMask (%s)\",\n+\t\t       nt_translate_link_speed(speed),\n+\t\t       nt_translate_link_speed_mask(ctx->speed_mask, buf,\n+\t\t\t\t\t\t sizeof(buf)));\n+\t\treturn -1;\n+\t}\n+\n+\tif (ctx->specific_u.sfp.dual_rate) {\n+\t\tuint64_t req_speed = nt_get_link_speed(speed);\n+\t\tuint64_t other_speed =\n+\t\t\tnt_get_link_speed((nt_link_speed_t)(ctx->speed_mask ^ (uint32_t)speed));\n+\t\tbool rate_high = req_speed > other_speed;\n+\t\t/*\n+\t\t * Do this both for 1/10 and 10/25. For Sfp28 it is not known if\n+\t\t * this is necessary but it is believed not to do any harm.\n+\t\t */\n+\t\tnim_sfp_set_rate_sel_high(ctx, rate_high, rate_high);\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Disable TX laser.\n+ */\n+int nim_sfp_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable)\n+{\n+\tint res;\n+\tuint8_t value;\n+\tconst bool pg_addr = page_addressing(ctx->nim_id);\n+\n+\tres = nim_nim_read_write_data_lin(ctx, pg_addr, SFP_CONTROL_STATUS_LIN_ADDR,\n+\t\t\t\t      sizeof(value), &value, NIM_READ);\n+\tif (res != 0)\n+\t\treturn res;\n+\n+\tif (disable)\n+\t\tvalue |= SFP_SOFT_TX_DISABLE_BIT;\n+\telse\n+\t\tvalue &= (uint8_t)~SFP_SOFT_TX_DISABLE_BIT;\n+\n+\tres = nim_nim_read_write_data_lin(ctx, pg_addr, SFP_CONTROL_STATUS_LIN_ADDR,\n+\t\t\t\t      sizeof(value), &value, NIM_WRITE);\n+\n+\treturn res;\n+}\n+\n+/*\n+ * Disable laser for specific lane or all lanes\n+ */\n+int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable,\n+\t\t\t\t       int lane_idx)\n+{\n+\tuint8_t value;\n+\tuint8_t mask;\n+\tconst bool pg_addr = page_addressing(ctx->nim_id);\n+\n+\tif (lane_idx < 0) /* If no lane is specified then all lanes */\n+\t\tmask = QSFP_SOFT_TX_ALL_DISABLE_BITS;\n+\telse\n+\t\tmask = (uint8_t)(1U << lane_idx);\n+\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR,\n+\t\t\t\t    sizeof(value), &value, NIM_READ) != 0)\n+\t\treturn -1;\n+\n+\tif (disable)\n+\t\tvalue |= mask;\n+\telse\n+\t\tvalue &= (uint8_t)~mask;\n+\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_CONTROL_STATUS_LIN_ADDR,\n+\t\t\t\t    sizeof(value), &value, NIM_WRITE) != 0)\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+/*\n+ * Read vendor information at a certain address. Any trailing whitespace is\n+ * removed and a missing string termination in the NIM data is handled.\n+ */\n+static int nim_read_vendor_info(nim_i2c_ctx_p ctx, uint16_t addr,\n+\t\t\t\t uint8_t max_len, char *p_data)\n+{\n+\tconst bool pg_addr = page_addressing(ctx->nim_id);\n+\tint i;\n+\t/* Subtract \"1\" from maxLen that includes a terminating \"0\" */\n+\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, addr, (uint8_t)(max_len - 1),\n+\t\t\t\t    (uint8_t *)p_data, NIM_READ) != 0)\n+\t\treturn -1;\n+\n+\t/* Terminate at first found white space */\n+\tfor (i = 0; i < max_len - 1; i++) {\n+\t\tif (*p_data == ' ' || *p_data == '\\n' || *p_data == '\\t' ||\n+\t\t\t\t*p_data == '\\v' || *p_data == '\\f' || *p_data == '\\r') {\n+\t\t\t*p_data = '\\0';\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tp_data++;\n+\t}\n+\n+\t/*\n+\t * Add line termination as the very last character, if it was missing in the\n+\t * NIM data\n+\t */\n+\t*p_data = '\\0';\n+\treturn 0;\n+}\n+\n+/*\n+ * Import length info in various units from NIM module data and convert to meters\n+ */\n+static void nim_import_len_info(nim_i2c_ctx_p ctx, uint8_t *p_nim_len_info,\n+\t\t\t\tuint16_t *p_nim_units)\n+{\n+\tsize_t i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(ctx->len_info); i++)\n+\t\tif (*(p_nim_len_info + i) == 255) {\n+\t\t\tctx->len_info[i] = 65535;\n+\t\t} else {\n+\t\t\tuint32_t len = *(p_nim_len_info + i) * *(p_nim_units + i);\n+\n+\t\t\tif (len > 65535)\n+\t\t\t\tctx->len_info[i] = 65535;\n+\t\t\telse\n+\t\t\t\tctx->len_info[i] = (uint16_t)len;\n+\t\t}\n+}\n+\n+static int qsfpplus_read_basic_data(nim_i2c_ctx_t *ctx)\n+{\n+\tconst bool pg_addr = page_addressing(ctx->nim_id);\n+\tuint8_t options;\n+\tuint8_t value;\n+\tuint8_t nim_len_info[5];\n+\tuint16_t nim_units[5] = { 1000, 2, 1, 1,\n+\t\t\t\t 1\n+\t\t\t       }; /* QSFP MSA units in meters */\n+\tconst char *yes_no[2] _unused = { \"No\", \"Yes\" };\n+\n+\tNT_LOG(DBG, ETHDEV, \"Instance %d: NIM id: %s (%d)\\n\", ctx->instance,\n+\t       nim_id_to_text(ctx->nim_id), ctx->nim_id);\n+\n+\t/* Read DMI options */\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_DMI_OPTION_LIN_ADDR,\n+\t\t\t\t    sizeof(options), &options, NIM_READ) != 0)\n+\t\treturn -1;\n+\tctx->avg_pwr = options & QSFP_DMI_AVG_PWR_BIT;\n+\tNT_LOG(DBG, ETHDEV,\n+\t       \"Instance %d: NIM options: (DMI: Yes, AvgPwr: %s)\\n\",\n+\t       ctx->instance, yes_no[ctx->avg_pwr]);\n+\n+\tqsfp_read_vendor_info(ctx);\n+\tNT_LOG(DBG, PMD,\n+\t       \"Instance %d: NIM info: (Vendor: %s, PN: %s, SN: %s, Date: %s, Rev: %s)\\n\",\n+\t       ctx->instance, ctx->vendor_name, ctx->prod_no, ctx->serial_no,\n+\t       ctx->date, ctx->rev);\n+\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_SUP_LEN_INFO_LIN_ADDR,\n+\t\t\t\t    sizeof(nim_len_info), nim_len_info,\n+\t\t\t\t    NIM_READ) != 0)\n+\t\treturn -1;\n+\n+\t/*\n+\t * Returns supported length information in meters for various fibers as 5 indivi-\n+\t * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper]\n+\t * If no length information is available for a certain entry, the returned value\n+\t * will be zero. This will be the case for SFP modules - EBW entry.\n+\t * If the MSBit is set the returned value in the lower 31 bits indicates that the\n+\t * supported length is greater than this.\n+\t */\n+\n+\tnim_import_len_info(ctx, nim_len_info, nim_units);\n+\n+\t/* Read required power level */\n+\tif (nim_nim_read_write_data_lin(ctx, pg_addr, QSFP_EXTENDED_IDENTIFIER,\n+\t\t\t\t    sizeof(value), &value, NIM_READ) != 0)\n+\t\treturn -1;\n+\n+\t/*\n+\t * Get power class according to SFF-8636 Rev 2.7, Table 6-16, Page 43:\n+\t * If power class >= 5 setHighPower must be called for the module to be fully\n+\t * functional\n+\t */\n+\tif ((value & QSFP_POWER_CLASS_BITS_5_7) == 0) {\n+\t\t/* NIM in power class 1 - 4 */\n+\t\tctx->pwr_level_req =\n+\t\t\t(uint8_t)(((value & QSFP_POWER_CLASS_BITS_1_4) >> 6) +\n+\t\t\t\t  1);\n+\t} else {\n+\t\t/* NIM in power class 5 - 7 */\n+\t\tctx->pwr_level_req =\n+\t\t\t(uint8_t)((value & QSFP_POWER_CLASS_BITS_5_7) + 4);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * If true the user must actively select the desired rate. If false the module\n+ * however can still support several rates without the user is required to select\n+ * one of them. Supported rates must then be deduced from the product number.\n+ * SFF-8636, Rev 2.10a:\n+ * p40: 6.2.7 Rate Select\n+ * p85: A.2 Rate Select\n+ */\n+static bool qsfp28_is_speed_selection_enabled(nim_i2c_ctx_p ctx)\n+{\n+\tconst uint8_t options_reg_addr = 195;\n+\tconst uint8_t enh_options_reg_addr = 221;\n+\n+\tuint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) &\n+\t\t\t\t0x01; /* bit: 5 */\n+\n+\tif (rate_select_ena == 0)\n+\t\treturn false;\n+\n+\tuint8_t rate_select_type = (read_byte(ctx, enh_options_reg_addr) >> 2) &\n+\t\t\t\t 0x03; /* bit 3..2 */\n+\n+\tif (rate_select_type != 2) {\n+\t\tNT_LOG(DBG, NTHW, \"NIM has unhandled rate select type (%d)\",\n+\t\t       rate_select_type);\n+\t\treturn false;\n+\t}\n+\n+\treturn true; /* When true selectRate() can be used */\n+}\n+\n+/*\n+ * Select a speed that is supported for a multi rate module. The possible speed\n+ * values must be obtained by setSpeedMask().\n+ * Currently rate selection is assumed to be between 40Gb (10GBd) and 100G (25Gbd)\n+ * The value in () are the baud rates for PAM-4 and are valid for extended rate\n+ * select, version 2.\n+ */\n+static int qsfp28_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed)\n+{\n+\tconst uint8_t rx_rate_sel_addr = 87;\n+\tconst uint8_t tx_rate_sel_addr = 88;\n+\n+\tif (ctx->lane_idx < 0) {\n+\t\t/*\n+\t\t * All lanes together\n+\t\t * The condition below indicates that the module supports rate selection\n+\t\t */\n+\t\tif (ctx->speed_mask == (uint32_t)(NT_LINK_SPEED_40G | NT_LINK_SPEED_100G)) {\n+\t\t\tuint16_t data;\n+\n+\t\t\tif (speed == NT_LINK_SPEED_100G) {\n+\t\t\t\tdata = 0xAAAA;\n+\t\t\t} else if (speed == NT_LINK_SPEED_40G) {\n+\t\t\t\tdata = 0x0000;\n+\t\t\t} else {\n+\t\t\t\tNT_LOG(ERR, NTHW, \"Unhandled NIM speed (%s).\",\n+\t\t\t\t       nt_translate_link_speed(speed));\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\t/* Set speed for Rx and Tx on all lanes */\n+\t\t\twrite_data_lin(ctx, rx_rate_sel_addr, sizeof(data), &data);\n+\t\t\twrite_data_lin(ctx, tx_rate_sel_addr, sizeof(data), &data);\n+\t\t} else {\n+\t\t\t/* For ordinary modules only this speed is supported */\n+\t\t\tif (speed != NT_LINK_SPEED_100G) {\n+\t\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t\t       \"NIM cannot select this speed (%s).\",\n+\t\t\t\t       nt_translate_link_speed(speed));\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * Individual lanes\n+\t\t * Currently we do not support QSFP28 modules that support rate selection when\n+\t\t * running on individual lanes but that might change in the future\n+\t\t */\n+\t\tif (speed != NT_LINK_SPEED_25G) {\n+\t\t\tNT_LOG(ERR, NTHW,\n+\t\t\t       \"NIM cannot select this lane speed (%s).\",\n+\t\t\t       nt_translate_link_speed(speed));\n+\t\t\treturn -1;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+int nim_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed)\n+{\n+\tif (translate_nimid(ctx) == NT_NIM_SFP_SFP_PLUS) {\n+\t\treturn nim_sfp_set_rate_select(ctx, speed);\n+\t} else if (translate_nimid(ctx) == NT_NIM_QSFP28) {\n+\t\tif (qsfp28_is_speed_selection_enabled(ctx))\n+\t\t\treturn qsfp28_set_link_speed(ctx, speed);\n+\n+\t\treturn 0; /* NIM picks up the speed automatically */\n+\t}\n+\tNT_LOG(ERR, ETHDEV,\n+\t       \"%s nim is not supported for adjustable link speed.\",\n+\t       nim_id_to_text(ctx->nim_id));\n+\treturn -1;\n+}\n+\n+/*\n+ * Reads basic vendor and DMI information.\n+ */\n+static int sfp_read_basic_data(nim_i2c_ctx_p ctx)\n+{\n+\tconst char *yes_no[2] _unused = { \"No\", \"Yes\" };\n+\n+\tcheck_content_valid(ctx, 0);\n+\tNT_LOG(DBG, PMD, \"NIM id: %s (%d)\", nim_id_to_text(ctx->nim_id),\n+\t       ctx->nim_id);\n+\n+\t/* Read DMI options */\n+\tuint8_t options;\n+\n+\tread_data_lin(ctx, SFP_DMI_OPTION_LIN_ADDR, sizeof(options), &options);\n+\tctx->avg_pwr = options & SFP_DMI_AVG_PWR_BIT;\n+\tctx->dmi_supp = options & SFP_DMI_IMPL_BIT;\n+\tctx->specific_u.sfp.ext_cal = options & SFP_DMI_EXT_CAL_BIT;\n+\tctx->specific_u.sfp.addr_chg = options & SFP_DMI_ADDR_CHG_BIT;\n+\n+\tif (ctx->dmi_supp) {\n+\t\tctx->options |=\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t}\n+\n+\tif (ctx->dmi_supp) {\n+\t\tNT_LOG(DBG, PMD,\n+\t\t       \"NIM options: (DMI: %s, AvgPwr: %s, ExtCal: %s, AddrChg: %s)\",\n+\t\t       yes_no[ctx->dmi_supp], yes_no[ctx->avg_pwr],\n+\t\t       yes_no[ctx->specific_u.sfp.ext_cal],\n+\t\t       yes_no[ctx->specific_u.sfp.addr_chg]);\n+\t} else {\n+\t\tNT_LOG(DBG, PMD, \"NIM options: DMI not supported\");\n+\t}\n+\t/* Read enhanced options */\n+\tread_data_lin(ctx, SFP_ENHANCED_OPTIONS_LIN_ADDR, sizeof(options),\n+\t\t    &options);\n+\tctx->tx_disable = options & SFP_SOFT_TX_DISABLE_IMPL_BIT;\n+\n+\tif (ctx->tx_disable)\n+\t\tctx->options |= (1 << NIM_OPTION_TX_DISABLE);\n+\n+\tsfp_read_vendor_info(ctx);\n+\n+\tuint8_t nim_len_info[5];\n+\n+\tread_data_lin(ctx, SFP_SUP_LEN_INFO_LIN_ADDR, sizeof(nim_len_info),\n+\t\t    nim_len_info);\n+\n+\t/*\n+\t * Returns supported length information in meters for various fibers as 5 indivi-\n+\t * dual values: [SM(9um), EBW(50um), MM(50um), MM(62.5um), Copper]\n+\t * If no length information is available for a certain entry, the returned value\n+\t * will be zero. This will be the case for SFP modules - EBW entry.\n+\t * If the MSBit is set the returned value in the lower 31 bits indicates that the\n+\t * supported length is greater than this.\n+\t */\n+\n+\tuint16_t nim_units[5] = { 1000, 100, 10, 10,\n+\t\t\t\t 1\n+\t\t\t       }; /* SFP MSA units in meters */\n+\tnim_import_len_info(ctx, &nim_len_info[0], &nim_units[0]);\n+\n+\tif (ctx->len_info[0] != 0 || ctx->len_info[1] != 0) {\n+\t\t/*\n+\t\t * Make sure that for SFP modules the supported length for SM fibers\n+\t\t * which is given in both km and 100m units is are equal to the greatest\n+\t\t * value.\n+\t\t * The following test will also be valid if NIM_LEN_MAX has been set!\n+\t\t */\n+\t\tif (ctx->len_info[1] > ctx->len_info[0])\n+\t\t\tctx->len_info[0] = ctx->len_info[1];\n+\n+\t\tctx->len_info[1] = 0; /* EBW is not supported for SFP */\n+\t}\n+\n+\tread_data_lin(ctx, SFP_OPTION0_LIN_ADDR, sizeof(options), &options);\n+\n+\tif (options & SFP_POWER_LEVEL2_REQ_BIT)\n+\t\tctx->pwr_level_req = 2;\n+\telse\n+\t\tctx->pwr_level_req = 1;\n+\n+\tctx->pwr_level_cur = 1;\n+\n+\tif (ctx->pwr_level_req == 2) {\n+\t\t/* Read the current power level status */\n+\t\tread_data_lin(ctx, SFP_EXT_CTRL_STAT0_LIN_ADDR, sizeof(options),\n+\t\t\t    &options);\n+\n+\t\tif (options & SFP_POWER_LEVEL2_GET_BIT)\n+\t\t\tctx->pwr_level_cur = 2;\n+\t\telse\n+\t\t\tctx->pwr_level_cur = 1;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Read the vendor product number and from this determine which QSFP DMI options\n+ * that are present. This list also covers QSFP28 modules.\n+ * This function should be used if automatic detection does not work.\n+ */\n+static bool qsfpplus_get_qsfp_options_from_pn(nim_i2c_ctx_p ctx)\n+{\n+\tif (strcmp(ctx->prod_no, \"FTL410QE1C\") == 0) {\n+\t\t/* FINISAR FTL410QE1C, QSFP+ */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_TX_BIAS) | (1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"FTL410QE2C\") == 0) {\n+\t\t/* FINISAR FTL410QE2C, QSFP+ */\n+\t\tctx->options = (1 << NIM_OPTION_TEMP) |\n+\t\t\t       (1 << NIM_OPTION_SUPPLY);\n+\t} else if (strcmp(ctx->prod_no, \"FTL4C1QE1C\") == 0) {\n+\t\t/* FINISAR FTL4C1QE1C, QSFP+ */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79E4Z\") == 0) {\n+\t\t/*\n+\t\t * AFBR-79E4Z: The digital diagnostic accuracy is not guaranteed so only\n+\t\t * the mandatory temperature sensor is made available (although it will\n+\t\t * also be inaccurate)\n+\t\t */\n+\t\t/* AVAGO 79E4Z, QSFP+ */\n+\t\tctx->options = (1 << NIM_OPTION_TEMP);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79E4Z-D\") == 0) {\n+\t\t/* AVAGO 79E4Z-D, QSFP+ */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79EQDZ\") == 0) {\n+\t\t/* AVAGO 79EQDZ, QSFP+ */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79EBRZ\") == 0) {\n+\t\t/*\n+\t\t * Avago RxOnly BiDi NIM\n+\t\t * No sensors available not even the normally mandatory temp sensor and this\n+\t\t * is ok since the temp sensor is not mandatory on active optical modules\n+\t\t */\n+\t\t/* SFF-8436_rev4.1, p67 */\n+\t\tctx->options = (1 << NIM_OPTION_RX_ONLY);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79EBPZ-NU1\") == 0) {\n+\t\t/*\n+\t\t * Avago RxTx BiDi NIM\n+\t\t * No sensors available not even the normally mandatory temp sensor and this\n+\t\t * is ok since the temp sensor is not mandatory on active optical modules\n+\t\t */\n+\t\tctx->options = 0;\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-79EBPZ\") == 0) {\n+\t\t/*\n+\t\t * Avago RxTx BiDi NIM\n+\t\t * No sensors available not even the normally mandatory temp sensor and this\n+\t\t * is ok since the temp sensor is not mandatory on active optical modules\n+\t\t */\n+\t\tctx->options = 0;\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-89CDDZ\") == 0) {\n+\t\t/* AVAGO 89CDDZ, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-89BDDZ\") == 0) {\n+\t\t/* AVAGO 89BDDZ, QSFP28, BiDi */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"AFBR-89BRDZ\") == 0) {\n+\t\t/*\n+\t\t * AVAGO 89BRDZ, QSFP28, BiDi, RxOnly\n+\t\t * but sensors have been set as above except for Tx sensors\n+\t\t */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_RX_ONLY);\n+\t\t/*\n+\t\t * According to mail correspondence AFBR-89BRDZ is a RxOnly version of\n+\t\t * AFBR-89BDDZ with lasers default off.\n+\t\t * The lasers can be turned on however but should probably not because the\n+\t\t * receivers might be degraded, and this is the cause for selling them as RxOnly.\n+\t\t */\n+\t} else if (strcmp(ctx->prod_no, \"SQF1000L4LNGG01P\") == 0) {\n+\t\t/* Sumitomo SQF1000L4LNGG01P, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"SQF1000L4LNGG01B\") == 0) {\n+\t\t/* Sumitomo SQF1000L4LNGG01B, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"SQF1001L4LNGG01P\") == 0) {\n+\t\t/* Sumitomo SQF1001L4LNGG01P, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"SQF1001L4LNGG01B\") == 0) {\n+\t\t/* Sumitomo SQF1001L4LNGG01B, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"SQF1002L4LNGG01B\") == 0) {\n+\t\t/* Sumitomo SQF1002L4LNGG01B, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"FIM37700/171\") == 0) {\n+\t\t/* Fujitsu FIM37700/171, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"FIM37700/172\") == 0) {\n+\t\t/* Fujitsu FIM37700/172, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"TR-FC85S-NVS\") == 0) {\n+\t\t/* InnoLight TR-FC85S-NVS, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"TR-FC13L-NVS\") == 0) {\n+\t\t/* InnoLight TR-FC13L-NVS, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"FTLC9551REPM\") == 0) {\n+\t\t/* Finisar FTLC9551REPM, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else if (strcmp(ctx->prod_no, \"FTLC9558REPM\") == 0) {\n+\t\t/* Finisar FTLC9558REPM, QSFP28 */\n+\t\tctx->options =\n+\t\t\t(1 << NIM_OPTION_TEMP) | (1 << NIM_OPTION_SUPPLY) |\n+\t\t\t(1 << NIM_OPTION_RX_POWER) | (1 << NIM_OPTION_TX_BIAS) |\n+\t\t\t(1 << NIM_OPTION_TX_POWER);\n+\t} else {\n+\t\t/*\n+\t\t * DO NOTE: The temperature sensor is not mandatory on active/passive copper\n+\t\t * and active optical modules\n+\t\t */\n+\t\tctx->options = (1 << NIM_OPTION_TEMP);\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Try to figure out if a sensor is present by reading its value(s) and its limits.\n+ * This is a highly impirical way that cannot be guaranteed to give the correct\n+ * result but it was a wish not to be dependent on a PN table based solution.\n+ */\n+static void qsfpplus_find_qsfp_sensor_option(nim_i2c_ctx_p ctx,\n+\t\tuint16_t value_addr,\n+\t\tuint8_t lane_count,\n+\t\tuint16_t limit_addr, bool two_compl,\n+\t\tuint32_t sensor_option)\n+{\n+\tuint8_t data[8];\n+\tint i, j;\n+\tint value;\n+\tint value_list[4];\n+\tint limit;\n+\tint limit_list[4];\n+\tbool present;\n+\n+\t/* Read current value(s) */\n+\tread_data_lin(ctx, value_addr, (uint16_t)(lane_count * 2), data);\n+\n+\tfor (j = 0; j < lane_count; j++) {\n+\t\tvalue = 0;\n+\n+\t\tfor (i = 0; i < 2; i++) {\n+\t\t\tvalue = value << 8;\n+\t\t\tvalue += data[2 * j + i];\n+\t\t}\n+\n+\t\tif (two_compl && value >= 0x8000)\n+\t\t\tvalue = value - 0x10000;\n+\n+\t\tvalue_list[j] = value;\n+\t}\n+\n+\t/* Read limits Warning high/low Alarm high/low 4 values each two bytes */\n+\tread_data_lin(ctx, limit_addr, 8, data);\n+\n+\tfor (j = 0; j < 4; j++) {\n+\t\tlimit = 0;\n+\n+\t\tfor (i = 0; i < 2; i++) {\n+\t\t\tlimit = limit << 8;\n+\t\t\tlimit += data[2 * j + i];\n+\t\t}\n+\n+\t\tif (two_compl && limit >= 0x8000)\n+\t\t\tlimit = limit - 0x10000;\n+\n+\t\tlimit_list[j] = limit;\n+\t}\n+\n+\t/* Find out if limits contradicts each other */\n+\tint alarm_high = limit_list[0];\n+\tint alarm_low = limit_list[1];\n+\tint warn_high = limit_list[2];\n+\tint warn_low = limit_list[3];\n+\n+\tbool alarm_limits = false; /* Are they present - that is both not zero */\n+\tbool warn_limits = false;\n+\tbool limit_conflict = false;\n+\n+\tif (alarm_high != 0 || alarm_low != 0) {\n+\t\talarm_limits = true;\n+\n+\t\tif (alarm_high <= alarm_low)\n+\t\t\tlimit_conflict = true;\n+\t}\n+\n+\tif (warn_high != 0 || warn_low != 0) {\n+\t\twarn_limits = true;\n+\n+\t\t/* Warning limits must be least restrictive */\n+\t\tif (warn_high <= warn_low)\n+\t\t\tlimit_conflict = true;\n+\t\telse if ((warn_high > alarm_high) || (warn_low < alarm_low))\n+\t\t\tlimit_conflict = true;\n+\t}\n+\n+\t/* Try to deduce if the sensor is present or not */\n+\tpresent = false;\n+\n+\tif (limit_conflict) {\n+\t\tpresent = false;\n+\t} else if (warn_limits ||\n+\t\t alarm_limits) { /* Is one or both present and not contradictory */\n+\t\tpresent = true;\n+\t} else {\n+\t\t/*\n+\t\t * All limits are zero - look at the sensor value\n+\t\t * If one sensor is non-zero the sensor is set to be present\n+\t\t */\n+\t\tfor (j = 0; j < lane_count; j++) {\n+\t\t\tif (value_list[j] != 0) {\n+\t\t\t\tpresent = true;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/*\n+\t\t * If all limits and values are zero then present will be false here. In this\n+\t\t * case it is assumed that the sensor is not present:\n+\t\t * Experience indicates that for QSFP+ modules RxPwr will be non-zero even with\n+\t\t * no optical input. QSFP28 modules however can easily have RxPwr equal to zero\n+\t\t * with no optical input.\n+\t\t * For all investigated modules it was found that if RxPwr is implemented then\n+\t\t * the limits are also set. This is not always the case with TxBias and TxPwr\n+\t\t * but here the measured values will be non-zero when the laser is on what it\n+\t\t * will be just after initialization since it has no external hardware disable.\n+\t\t */\n+\t}\n+\n+\tif (present)\n+\t\tctx->options |= (1U << sensor_option);\n+}\n+\n+/*\n+ * Find active QSFP sensors.\n+ */\n+static void qsfpplus_get_qsfp_options_from_data(nim_i2c_ctx_p ctx)\n+{\n+\tctx->options = 0;\n+\n+\tqsfpplus_find_qsfp_sensor_option(ctx, QSFP_TEMP_LIN_ADDR, 1,\n+\t\t\t\t\t QSFP_TEMP_THRESH_LIN_ADDR, true,\n+\t\t\t\t\t NIM_OPTION_TEMP);\n+\n+\tqsfpplus_find_qsfp_sensor_option(ctx, QSFP_VOLT_LIN_ADDR, 1,\n+\t\t\t\t\t QSFP_VOLT_THRESH_LIN_ADDR, false,\n+\t\t\t\t\t NIM_OPTION_SUPPLY);\n+\n+\tqsfpplus_find_qsfp_sensor_option(ctx, QSFP_RX_PWR_LIN_ADDR, 4,\n+\t\t\t\t\t QSFP_RX_PWR_THRESH_LIN_ADDR, false,\n+\t\t\t\t\t NIM_OPTION_RX_POWER);\n+\n+\tqsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_PWR_LIN_ADDR, 4,\n+\t\t\t\t\t QSFP_TX_PWR_THRESH_LIN_ADDR, false,\n+\t\t\t\t\t NIM_OPTION_TX_POWER);\n+\n+\tqsfpplus_find_qsfp_sensor_option(ctx, QSFP_TX_BIAS_LIN_ADDR, 4,\n+\t\t\t\t\t QSFP_BIAS_THRESH_LIN_ADDR, false,\n+\t\t\t\t\t NIM_OPTION_TX_BIAS);\n+}\n+\n+static void sfp_find_port_params(nim_i2c_ctx_p ctx)\n+{\n+\tuint8_t data;\n+\tuint16_t bit_rate_nom;\n+\tuint8_t connector;\n+\tuint8_t gig_eth_comp;\n+\tuint8_t dmi_opt;\n+\tuint8_t fiber_chan_tx_tech;\n+\tunsigned int len_sm;\n+\tunsigned int len_mm_50um;\n+\tunsigned int len_mm_62_5um;\n+\n+\tctx->specific_u.sfp.sfp28 = false;\n+\n+\t/* gigEthComp: */\n+\tstatic const uint8_t eth_1000_b_t = 1 << 3;\n+\tstatic const uint8_t eth_1000_b_sx = 1 << 0;\n+\tstatic const uint8_t eth_1000_b_lx = 1 << 1;\n+\n+\t/* fiberChanTxTech: */\n+\tstatic const uint8_t cu_passive = 1 << 2;\n+\tstatic const uint8_t cu_active = 1 << 3;\n+\n+\t/* dmiOpt: */\n+\tstatic const uint8_t dd_present = 1 << 6;\n+\n+\t/* connector: */\n+\tstatic const uint8_t cu_pig_tail = 0x21;\n+\n+\tctx->port_type = NT_PORT_TYPE_SFP_NOT_RECOGNISED;\n+\n+\tread_data_lin(ctx, 12, sizeof(data), &data);\n+\tbit_rate_nom = (uint16_t)(data * 100);\n+\n+\tread_data_lin(ctx, 2, sizeof(connector), &connector);\n+\tread_data_lin(ctx, 6, sizeof(gig_eth_comp), &gig_eth_comp);\n+\tread_data_lin(ctx, 92, sizeof(dmi_opt), &dmi_opt);\n+\tread_data_lin(ctx, 8, sizeof(fiber_chan_tx_tech), &fiber_chan_tx_tech);\n+\n+\tread_data_lin(ctx, 15, sizeof(data), &data);\n+\tlen_sm = (unsigned int)data * 100; /* Unit is 100m */\n+\n+\tread_data_lin(ctx, 16, sizeof(data), &data);\n+\tlen_mm_50um = (unsigned int)data * 10; /* Unit is 10m */\n+\n+\tread_data_lin(ctx, 17, sizeof(data), &data);\n+\tlen_mm_62_5um = (unsigned int)data * 10; /* Unit is 10m */\n+\n+\t/* First find out if it is a SFP or a SFP+ NIM */\n+\tif (bit_rate_nom == 0) {\n+\t\t/*\n+\t\t * A Nominal bit rate of zero indicates that it has not been defined and must\n+\t\t * be deduced from transceiver technology\n+\t\t */\n+\t\tctx->specific_u.sfp.sfpplus = !(gig_eth_comp & eth_1000_b_t);\n+\t} else if (bit_rate_nom == 25500) {\n+\t\t/* SFF-8024 - 4.4 Extended Specification Compliance References */\n+\t\tread_data_lin(ctx, 36, sizeof(data), &data);\n+\n+\t\tif (data == 0x02)\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_SR;\n+\t\telse if (data == 0x03)\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_LR;\n+\t\telse if (data == 0x0B)\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_L;\n+\t\telse if (data == 0x0C)\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_S;\n+\t\telse if (data == 0x0D)\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_CR_CA_N;\n+\t\telse\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28;\n+\n+\t\tctx->specific_u.sfp.sfp28 = true;\n+\t\tctx->specific_u.sfp.sfpplus = true;\n+\n+\t\t/*\n+\t\t * Whitelist of 25G transceivers known to also support 10G.\n+\t\t * There is no way to inquire about this capability.\n+\t\t */\n+\t\tif ((strcmp(ctx->prod_no, \"TR-PZ85S-N00\") == 0) ||\n+\t\t\t\t(strcmp(ctx->prod_no, \"TR-PZ13L-N00\") == 0) ||\n+\t\t\t\t(strcmp(ctx->prod_no, \"FTLF8536P4BCV\") == 0) ||\n+\t\t\t\t(strcmp(ctx->prod_no, \"FTLF1436P4BCV\") == 0)) {\n+\t\t\tctx->specific_u.sfp.dual_rate = true;\n+\n+\t\t\t/* Change the port type for dual rate modules */\n+\t\t\tif (ctx->port_type == NT_PORT_TYPE_SFP_28_SR)\n+\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_SR_DR;\n+\t\t\telse if (ctx->port_type == NT_PORT_TYPE_SFP_28_LR)\n+\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_28_LR_DR;\n+\t\t}\n+\n+\t\treturn;\n+\t}\n+\tctx->specific_u.sfp.sfpplus = (bit_rate_nom >= 10000);\n+\t/* Then find sub-types of each */\n+\tif (ctx->specific_u.sfp.sfpplus) {\n+\t\tif (fiber_chan_tx_tech & cu_active) {\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC;\n+\t\t} else if (fiber_chan_tx_tech & cu_passive) {\n+\t\t\tif (connector == cu_pig_tail)\n+\t\t\t\tctx->port_type =\n+\t\t\t\t\tNT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC;\n+\t\t\telse\n+\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_PLUS_CU;\n+\t\t} else {\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_PLUS;\n+\t\t}\n+\t\tif (gig_eth_comp & (eth_1000_b_sx | eth_1000_b_lx)) {\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_PLUS_DUAL_RATE;\n+\t\t\tctx->specific_u.sfp.dual_rate = true;\n+\t\t}\n+\n+\t\tread_data_lin(ctx, 65, sizeof(data), &data);\n+\t\t/* Test hard RATE_SELECT bit */\n+\t\tctx->specific_u.sfp.hw_rate_sel = ((data & (1 << 5)) != 0);\n+\n+\t\tread_data_lin(ctx, 93, sizeof(data), &data);\n+\t\t/* Test soft RATE_SELECT bit */\n+\t\tctx->specific_u.sfp.sw_rate_sel = ((data & (1 << 3)) != 0);\n+\t} else { /* SFP */\n+\t\t/* 100M */\n+\t\tif (bit_rate_nom != 0 && bit_rate_nom < 1000) {\n+\t\t\tctx->port_type = NT_PORT_TYPE_SFP_FX;\n+\t\t/* 1G */\n+\t\t} else {\n+\t\t\tctx->specific_u.sfp.cu_type = false;\n+\t\t\tif (gig_eth_comp & eth_1000_b_sx) {\n+\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_SX;\n+\t\t\t} else if (gig_eth_comp & eth_1000_b_lx) {\n+\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_LX;\n+\t\t\t} else if (gig_eth_comp & eth_1000_b_t) {\n+\t\t\t\tctx->specific_u.sfp.tri_speed =\n+\t\t\t\t\tsfp_is_supported_tri_speed_pn(ctx->prod_no);\n+\n+\t\t\t\tif (ctx->specific_u.sfp.tri_speed) {\n+\t\t\t\t\tctx->port_type =\n+\t\t\t\t\t\tNT_PORT_TYPE_SFP_CU_TRI_SPEED;\n+\t\t\t\t} else {\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_CU;\n+\t\t\t\t}\n+\t\t\t\tctx->specific_u.sfp.cu_type = true;\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * Not all modules report their ethernet compliance correctly so use\n+\t\t\t\t * length indicators\n+\t\t\t\t */\n+\t\t\t\tif (len_sm > 0)\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_LX;\n+\t\t\t\telse if ((len_mm_50um > 0) || (len_mm_62_5um > 0))\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_SX;\n+\t\t\t}\n+\n+\t\t\t/* Add Diagnostic Data suffix if necessary */\n+\t\t\tif (dmi_opt & dd_present) {\n+\t\t\t\tif (ctx->port_type == NT_PORT_TYPE_SFP_SX)\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_SX_DD;\n+\t\t\t\telse if (ctx->port_type == NT_PORT_TYPE_SFP_LX)\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_LX_DD;\n+\t\t\t\telse if (ctx->port_type == NT_PORT_TYPE_SFP_CU)\n+\t\t\t\t\tctx->port_type = NT_PORT_TYPE_SFP_CU_DD;\n+\t\t\t\telse if (ctx->port_type ==\n+\t\t\t\t\t\tNT_PORT_TYPE_SFP_CU_TRI_SPEED)\n+\t\t\t\t\tctx->port_type =\n+\t\t\t\t\t\tNT_PORT_TYPE_SFP_CU_TRI_SPEED_DD;\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+\n+static void sfp_set_speed_mask(nim_i2c_ctx_p ctx)\n+{\n+\tif (ctx->specific_u.sfp.sfp28) {\n+\t\tctx->speed_mask = NT_LINK_SPEED_25G; /* Default for SFP28 */\n+\t\tif (ctx->specific_u.sfp.dual_rate)\n+\t\t\tctx->speed_mask |= NT_LINK_SPEED_10G;\n+\t} else if (ctx->specific_u.sfp.sfpplus) {\n+\t\tctx->speed_mask = NT_LINK_SPEED_10G; /* Default for SFP+ */\n+\t\tif (ctx->specific_u.sfp.dual_rate)\n+\t\t\tctx->speed_mask |= NT_LINK_SPEED_1G;\n+\t\tif (ctx->port_type == NT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC)\n+\t\t\tctx->speed_mask |= NT_LINK_SPEED_1G;\n+\t\tif (ctx->port_type == NT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC)\n+\t\t\tctx->speed_mask |= NT_LINK_SPEED_1G;\n+\t} else { /* SFP */\n+\t\tif (ctx->port_type == NT_PORT_TYPE_SFP_FX) {\n+\t\t\tctx->speed_mask = NT_LINK_SPEED_100M;\n+\t\t} else {\n+\t\t\tctx->speed_mask = NT_LINK_SPEED_1G; /* Default for SFP */\n+\t\t\tif (ctx->specific_u.sfp.dual_rate ||\n+\t\t\t\t\tctx->specific_u.sfp.tri_speed)\n+\t\t\t\tctx->speed_mask |= NT_LINK_SPEED_100M;\n+\t\t\tif (ctx->specific_u.sfp.tri_speed)\n+\t\t\t\tctx->speed_mask |= NT_LINK_SPEED_10M;\n+\t\t}\n+\t}\n+\tif (ctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_L ||\n+\t\t\tctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_S ||\n+\t\t\tctx->port_type == NT_PORT_TYPE_SFP_28_CR_CA_N) {\n+\t\t/* Enable multiple speed setting for SFP28 DAC cables */\n+\t\tctx->speed_mask = (NT_LINK_SPEED_25G | NT_LINK_SPEED_10G |\n+\t\t\t\t  NT_LINK_SPEED_1G);\n+\t}\n+}\n+\n+static void qsfp28_find_port_params(nim_i2c_ctx_p ctx)\n+{\n+\tuint8_t fiber_chan_speed;\n+\n+\t/* Table 6-17 SFF-8636 */\n+\tread_data_lin(ctx, QSFP_SPEC_COMPLIANCE_CODES_ADDR, 1, &fiber_chan_speed);\n+\n+\tif (fiber_chan_speed & (1 << 7)) {\n+\t\t/* SFF-8024, Rev 4.7, Table 4-4 */\n+\t\tuint8_t extended_specification_compliance_code = 0;\n+\n+\t\tread_data_lin(ctx, QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR, 1,\n+\t\t\t    &extended_specification_compliance_code);\n+\n+\t\tswitch (extended_specification_compliance_code) {\n+\t\tcase 0x02:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_SR4;\n+\t\t\tbreak;\n+\t\tcase 0x03:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_LR4;\n+\t\t\tbreak;\n+\t\tcase 0x0B:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_L;\n+\t\t\tbreak;\n+\t\tcase 0x0C:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_S;\n+\t\t\tbreak;\n+\t\tcase 0x0D:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_CR_CA_N;\n+\t\t\tbreak;\n+\t\tcase 0x25:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_DR;\n+\t\t\tbreak;\n+\t\tcase 0x26:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_FR;\n+\t\t\tbreak;\n+\t\tcase 0x27:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28_LR;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tctx->port_type = NT_PORT_TYPE_QSFP28;\n+\t\t}\n+\t} else {\n+\t\tctx->port_type = NT_PORT_TYPE_QSFP28;\n+\t}\n+}\n+\n+/*\n+ * If true the user must actively select the desired rate. If false the module\n+ * however can still support several rates without the user is required to select\n+ * one of them. Supported rates must then be deduced from the product number.\n+ * SFF-8636, Rev 2.10a:\n+ * p40: 6.2.7 Rate Select\n+ * p85: A.2 Rate Select\n+ */\n+static bool qsfp28_is_rate_selection_enabled(nim_i2c_ctx_p ctx)\n+{\n+\tconst uint8_t ext_rate_select_compl_reg_addr = 141;\n+\tconst uint8_t options_reg_addr = 195;\n+\tconst uint8_t enh_options_reg_addr = 221;\n+\n+\tuint8_t rate_select_ena = (read_byte(ctx, options_reg_addr) >> 5) &\n+\t\t\t\t0x01; /* bit: 5 */\n+\n+\tif (rate_select_ena == 0)\n+\t\treturn false;\n+\n+\tuint8_t rate_select_type = (read_byte(ctx, enh_options_reg_addr) >> 2) &\n+\t\t\t\t 0x03; /* bit 3..2 */\n+\n+\tif (rate_select_type != 2) {\n+\t\tNT_LOG(DBG, PMD, \"NIM has unhandled rate select type (%d)\",\n+\t\t       rate_select_type);\n+\t\treturn false;\n+\t}\n+\n+\tuint8_t ext_rate_select_ver = read_byte(ctx, ext_rate_select_compl_reg_addr) &\n+\t\t\t\t   0x03; /* bit 1..0 */\n+\n+\tif (ext_rate_select_ver != 0x02) {\n+\t\tNT_LOG(DBG, PMD,\n+\t\t       \"NIM has unhandled extended rate select version (%d)\",\n+\t\t       ext_rate_select_ver);\n+\t\treturn false;\n+\t}\n+\n+\treturn true; /* When true selectRate() can be used */\n+}\n+\n+static void qsfp28_set_speed_mask(nim_i2c_ctx_p ctx)\n+{\n+\tif (ctx->port_type == NT_PORT_TYPE_QSFP28_FR ||\n+\t\t\tctx->port_type == NT_PORT_TYPE_QSFP28_DR ||\n+\t\t\tctx->port_type == NT_PORT_TYPE_QSFP28_LR) {\n+\t\tif (ctx->lane_idx < 0)\n+\t\t\tctx->speed_mask = NT_LINK_SPEED_100G;\n+\t\telse\n+\t\t\tctx->speed_mask =\n+\t\t\t\t0; /* PAM-4 modules can only run on all lanes together */\n+\t} else {\n+\t\tif (ctx->lane_idx < 0)\n+\t\t\tctx->speed_mask = NT_LINK_SPEED_100G;\n+\t\telse\n+\t\t\tctx->speed_mask = NT_LINK_SPEED_25G;\n+\n+\t\tif (qsfp28_is_rate_selection_enabled(ctx)) {\n+\t\t\t/*\n+\t\t\t * It is assumed that if the module supports dual rates then the other rate\n+\t\t\t * is 10G per lane or 40G for all lanes.\n+\t\t\t */\n+\t\t\tif (ctx->lane_idx < 0)\n+\t\t\t\tctx->speed_mask |= NT_LINK_SPEED_40G;\n+\t\t\telse\n+\t\t\t\tctx->speed_mask = NT_LINK_SPEED_10G;\n+\t\t}\n+\t}\n+}\n+\n+static void qsfpplus_find_port_params(nim_i2c_ctx_p ctx)\n+{\n+\tuint8_t device_tech;\n+\n+\tread_data_lin(ctx, QSFP_TRANSMITTER_TYPE_LIN_ADDR, sizeof(device_tech),\n+\t\t    &device_tech);\n+\n+\tswitch (device_tech & 0xF0) {\n+\tcase 0xA0: /* Copper cable unequalized */\n+\tcase 0xB0: /* Copper cable passive equalized */\n+\t\tctx->port_type = NT_PORT_TYPE_QSFP_PASSIVE_DAC;\n+\t\tbreak;\n+\tcase 0xC0: /* Copper cable, near and far end limiting active equalizers */\n+\tcase 0xD0: /* Copper cable, far end limiting active equalizers */\n+\tcase 0xE0: /* Copper cable, near end limiting active equalizers */\n+\tcase 0xF0: /* Copper cable, linear active equalizers */\n+\t\tctx->port_type = NT_PORT_TYPE_QSFP_ACTIVE_DAC;\n+\t\tbreak;\n+\tdefault: /* Optical */\n+\t\tctx->port_type = NT_PORT_TYPE_QSFP_PLUS;\n+\t\tbreak;\n+\t}\n+}\n+\n+static void qsfpplus_set_speed_mask(nim_i2c_ctx_p ctx)\n+{\n+\tctx->speed_mask = (ctx->lane_idx < 0) ? NT_LINK_SPEED_40G :\n+\t\t\t (NT_LINK_SPEED_10G);\n+}\n+\n+static int sfp_preinit(nim_i2c_ctx_p ctx)\n+{\n+\tint res = sfp_read_basic_data(ctx);\n+\n+\tif (!res) {\n+\t\tsfp_find_port_params(ctx);\n+\t\tsfp_set_speed_mask(ctx);\n+\t}\n+\treturn res;\n+}\n+\n+static void qsfpplus_construct(nim_i2c_ctx_p ctx, int8_t lane_idx)\n+{\n+\tassert(lane_idx < 4);\n+\tctx->specific_u.qsfp.qsfp28 = false;\n+\tctx->lane_idx = lane_idx;\n+\tctx->lane_count = 4;\n+}\n+\n+static int qsfpplus_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)\n+{\n+\tqsfpplus_construct(ctx, lane_idx);\n+\tint res = qsfpplus_read_basic_data(ctx);\n+\n+\tif (!res) {\n+\t\tqsfpplus_find_port_params(ctx);\n+\t\t/*\n+\t\t * If not on the known modules list try to figure out which sensors that are present\n+\t\t */\n+\t\tif (!qsfpplus_get_qsfp_options_from_pn(ctx)) {\n+\t\t\tNT_LOG(DBG, NTHW,\n+\t\t\t       \"NIM options not known in advance - trying to detect\");\n+\t\t\tqsfpplus_get_qsfp_options_from_data(ctx);\n+\t\t}\n+\n+\t\t/*\n+\t\t * Read if TX_DISABLE has been implemented\n+\t\t * For passive optical modules this is required while it for copper and active\n+\t\t * optical modules is optional. Under all circumstances register 195.4 will\n+\t\t * indicate, if TX_DISABLE has been implemented in register 86.0-3\n+\t\t */\n+\t\tuint8_t value;\n+\n+\t\tread_data_lin(ctx, QSFP_OPTION3_LIN_ADDR, sizeof(value), &value);\n+\n+\t\tctx->tx_disable = (value & QSFP_OPTION3_TX_DISABLE_BIT) != 0;\n+\n+\t\tif (ctx->tx_disable)\n+\t\t\tctx->options |= (1 << NIM_OPTION_TX_DISABLE);\n+\n+\t\t/*\n+\t\t * Previously - considering AFBR-89BRDZ - code tried to establish if a module was\n+\t\t * RxOnly by testing the state of the lasers after reset. Lasers were for this\n+\t\t * module default disabled.\n+\t\t * However that code did not work for GigaLight, GQS-MPO400-SR4C so it was\n+\t\t * decided that this option should not be detected automatically but from PN\n+\t\t */\n+\t\tctx->specific_u.qsfp.rx_only =\n+\t\t\t(ctx->options & (1 << NIM_OPTION_RX_ONLY)) != 0;\n+\t\tqsfpplus_set_speed_mask(ctx);\n+\t}\n+\treturn res;\n+}\n+\n+static void qsfp28_wait_for_ready_after_reset(nim_i2c_ctx_p ctx)\n+{\n+\tuint8_t data;\n+\tbool init_complete_flag_present = false;\n+\n+\t/*\n+\t * Revision compliance\n+\t * 7: SFF-8636 Rev 2.5, 2.6 and 2.7\n+\t * 8: SFF-8636 Rev 2.8, 2.9 and 2.10\n+\t */\n+\tread_data_lin(ctx, 1,\n+\t\t      sizeof(ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance),\n+\t\t      &ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);\n+\tNT_LOG(DBG, NTHW, \"NIM RevCompliance = %d\",\n+\t       ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance);\n+\n+\t/* Wait if lane_idx == -1 (all lanes are used) or lane_idx == 0 (the first lane) */\n+\tif (ctx->lane_idx > 0)\n+\t\treturn;\n+\n+\tif (ctx->specific_u.qsfp.specific_u.qsfp28.rev_compliance >= 7) {\n+\t\t/* Check if init complete flag is implemented */\n+\t\tread_data_lin(ctx, 221, sizeof(data), &data);\n+\t\tinit_complete_flag_present = (data & (1 << 4)) != 0;\n+\t}\n+\n+\tNT_LOG(DBG, NTHW, \"NIM InitCompleteFlagPresent = %d\",\n+\t       init_complete_flag_present);\n+\n+\t/*\n+\t * If the init complete flag is not present then wait 500ms that together with 500ms\n+\t * after reset (in the adapter code) should be enough to read data from upper pages\n+\t * that otherwise would not be ready. Especially BiDi modules AFBR-89BDDZ have been\n+\t * prone to this when trying to read sensor options using getQsfpOptionsFromData()\n+\t * Probably because access to the paged address space is required.\n+\t */\n+\tif (!init_complete_flag_present) {\n+\t\tNT_OS_WAIT_USEC(500000);\n+\t\treturn;\n+\t}\n+\n+\t/* Otherwise wait for the init complete flag to be set */\n+\tint count = 0;\n+\n+\twhile (true) {\n+\t\tif (count > 10) { /* 1 s timeout */\n+\t\t\tNT_LOG(WRN, NTHW, \"Timeout waiting for module ready\");\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tread_data_lin(ctx, 6, sizeof(data), &data);\n+\n+\t\tif (data & 0x01) {\n+\t\t\tNT_LOG(DBG, NTHW, \"Module ready after %dms\",\n+\t\t\t       count * 100);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tNT_OS_WAIT_USEC(100000); /* 100 ms */\n+\t\tcount++;\n+\t}\n+}\n+\n+static void qsfp28_get_fec_options(nim_i2c_ctx_p ctx)\n+{\n+\tconst char *const nim_list[] = {\n+\t\t\"AFBR-89BDDZ\", /* Avago BiDi */\n+\t\t\"AFBR-89BRDZ\", /* Avago BiDi, RxOnly */\n+\t\t\"FTLC4352RKPL\", /* Finisar QSFP28-LR */\n+\t\t\"FTLC4352RHPL\", /* Finisar QSFP28-DR */\n+\t\t\"FTLC4352RJPL\", /* Finisar QSFP28-FR */\n+\t\t\"SFBR-89BDDZ-CS4\", /* Foxconn, QSFP28 100G/40G BiDi */\n+\t};\n+\n+\tfor (size_t i = 0; i < ARRAY_SIZE(nim_list); i++) {\n+\t\tif (ctx->prod_no == nim_list[i]) {\n+\t\t\tctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);\n+\t\t\tctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ena =\n+\t\t\t\ttrue;\n+\t\t\tNT_LOG(DBG, NTHW, \"Found FEC info via PN list\");\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * For modules not in the list find FEC info via registers\n+\t * Read if the module has controllable FEC\n+\t * SFF-8636, Rev 2.10a TABLE 6-28 Equalizer, Emphasis, Amplitude and Timing)\n+\t * (Page 03h, Bytes 224-229)\n+\t */\n+\tuint8_t data;\n+\tuint16_t addr = 227 + 3 * 128;\n+\n+\tread_data_lin(ctx, addr, sizeof(data), &data);\n+\n+\t/* Check if the module has FEC support that can be controlled */\n+\tctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl =\n+\t\t(data & (1 << 6)) != 0;\n+\tctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl =\n+\t\t(data & (1 << 7)) != 0;\n+\n+\tif (ctx->specific_u.qsfp.specific_u.qsfp28.media_side_fec_ctrl)\n+\t\tctx->options |= (1 << NIM_OPTION_MEDIA_SIDE_FEC);\n+\n+\tif (ctx->specific_u.qsfp.specific_u.qsfp28.host_side_fec_ctrl)\n+\t\tctx->options |= (1 << NIM_OPTION_HOST_SIDE_FEC);\n+}\n+\n+static int qsfp28_preinit(nim_i2c_ctx_p ctx, int8_t lane_idx)\n+{\n+\tint res = qsfpplus_preinit(ctx, lane_idx);\n+\n+\tif (!res) {\n+\t\tqsfp28_wait_for_ready_after_reset(ctx);\n+\t\tmemset(&ctx->specific_u.qsfp.specific_u.qsfp28, 0,\n+\t\t       sizeof(ctx->specific_u.qsfp.specific_u.qsfp28));\n+\t\tctx->specific_u.qsfp.qsfp28 = true;\n+\t\tqsfp28_find_port_params(ctx);\n+\t\tqsfp28_get_fec_options(ctx);\n+\t\tqsfp28_set_speed_mask(ctx);\n+\t}\n+\treturn res;\n+}\n+\n+static void sfp_nim_add_all_sensors(uint8_t m_port_no, nim_i2c_ctx_t *ctx,\n+\t\t\t\t  struct nim_sensor_group **nim_sensors_ptr,\n+\t\t\t\t  uint16_t *nim_sensors_cnt)\n+{\n+\tstruct nim_sensor_group *sensor = NULL;\n+\t*nim_sensors_cnt = 0;\n+\n+\tif (ctx == NULL || nim_sensors_ptr == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * If the user has not provided a name for the temperature sensor then apply\n+\t * one automatically\n+\t */\n+\tif (strlen(sfp_sensors_level0[0].name) == 0) {\n+\t\tif (ctx->specific_u.sfp.sfp28) {\n+\t\t\trte_strscpy(sfp_sensors_level0[0].name, \"SFP28\",\n+\t\t\t\tsizeof(sfp_sensors_level0[0].name));\n+\t\t} else if (ctx->specific_u.sfp.sfpplus) {\n+\t\t\trte_strscpy(sfp_sensors_level0[0].name, \"SFP+\",\n+\t\t\t\tsizeof(sfp_sensors_level0[0].name));\n+\t\t} else {\n+\t\t\trte_strscpy(sfp_sensors_level0[0].name, \"SFP\",\n+\t\t\t\tsizeof(sfp_sensors_level0[0].name));\n+\t\t}\n+\t}\n+\n+\t/* allocate temperature sensor */\n+\tnim_sensors_ptr[m_port_no] = allocate_nim_sensor_group(m_port_no,\n+\t\t\t\t\t\t\t       ctx,\n+\t\t\t\t\t\t\t       NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t\t       &sfp_sensors_level0[0]);\n+\tsensor = nim_sensors_ptr[m_port_no];\n+\tsensor->read = &nim_read_sfp_temp;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* voltage */\n+\tsensor->next = allocate_nim_sensor_group(m_port_no,\n+\t\t\t\t\t\t ctx,\n+\t\t\t\t\t\t NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t &sfp_sensors_level1[0]);\n+\tsensor = sensor->next;\n+\tsensor->read = &nim_read_sfp_voltage;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* bias current */\n+\tsensor->next = allocate_nim_sensor_group(m_port_no,\n+\t\t\t\t\t\t ctx,\n+\t\t\t\t\t\t NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t &sfp_sensors_level1[1]);\n+\tsensor = sensor->next;\n+\tsensor->read = &nim_read_sfp_bias_current;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* tx power */\n+\tsensor->next = allocate_nim_sensor_group(m_port_no,\n+\t\t\t\t\t\t ctx,\n+\t\t\t\t\t\t NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t &sfp_sensors_level1[2]);\n+\tsensor = sensor->next;\n+\tsensor->read = &nim_read_sfp_tx_power;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* rx power */\n+\tsensor->next = allocate_nim_sensor_group(m_port_no,\n+\t\t\t\t\t\t ctx,\n+\t\t\t\t\t\t NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t &sfp_sensors_level1[3]);\n+\tsensor = sensor->next;\n+\tsensor->read = &nim_read_sfp_rx_power;\n+\t(*nim_sensors_cnt)++;\n+}\n+\n+static void\n+qsfp_plus_nim_add_all_sensors(uint8_t m_port_no, nim_i2c_ctx_t *ctx,\n+\t\t\t   struct nim_sensor_group **nim_sensors_ptr,\n+\t\t\t   uint16_t *nim_sensors_cnt)\n+{\n+\tstruct nim_sensor_group *sensor = NULL;\n+\n+\tif (ctx == NULL || nim_sensors_ptr == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * If the user has not provided a name for the temperature sensor then apply\n+\t * one automatically\n+\t */\n+\tif (strlen(qsfp_sensor_level0[0].name) == 0) {\n+\t\tif (ctx->specific_u.qsfp.qsfp28)\n+\t\t\trte_strscpy(qsfp_sensor_level0[0].name, \"QSFP28\",\n+\t\t\t\tsizeof(qsfp_sensor_level0[0].name));\n+\t\telse\n+\t\t\trte_strscpy(qsfp_sensor_level0[0].name, \"QSFP+\",\n+\t\t\t\tsizeof(qsfp_sensor_level0[0].name));\n+\t}\n+\n+\t/* temperature sensor */\n+\tnim_sensors_ptr[m_port_no] = allocate_nim_sensor_group(m_port_no, ctx,\n+\t\t\t\t\t\t\t       NT_SENSOR_SOURCE_PORT,\n+\t\t\t\t\t\t\t       &qsfp_sensor_level0[0]);\n+\tsensor = nim_sensors_ptr[m_port_no];\n+\tsensor->read = &nim_read_qsfp_temp;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* voltage */\n+\tsensor->next = allocate_nim_sensor_group(m_port_no, ctx,\n+\t\t\t\t\t\t NT_SENSOR_SOURCE_LEVEL1_PORT,\n+\t\t\t\t\t\t &qsfp_sensor_level1[0]);\n+\tsensor = sensor->next;\n+\tsensor->read = &nim_read_qsfp_voltage;\n+\t(*nim_sensors_cnt)++;\n+\n+\t/* bias current sensors */\n+\tfor (uint8_t i = 1; i < 5; i++) {\n+\t\tsensor->next = allocate_nim_sensor_group(m_port_no, ctx,\n+\t\t\t\t\t\t\t NT_SENSOR_SOURCE_LEVEL1_PORT,\n+\t\t\t\t\t\t\t &qsfp_sensor_level1[i]);\n+\t\tsensor = sensor->next;\n+\t\tsensor->read = &nim_read_qsfp_bias_current;\n+\t\t(*nim_sensors_cnt)++;\n+\t}\n+\n+\t/* tx power */\n+\tfor (uint8_t i = 5; i < 9; i++) {\n+\t\tsensor->next = allocate_nim_sensor_group(m_port_no, ctx,\n+\t\t\t\t\t\t\t NT_SENSOR_SOURCE_LEVEL1_PORT,\n+\t\t\t\t\t\t\t &qsfp_sensor_level1[i]);\n+\t\tsensor = sensor->next;\n+\t\tsensor->read = &nim_read_qsfp_tx_power;\n+\t\t(*nim_sensors_cnt)++;\n+\t}\n+\n+\t/* rx power */\n+\tfor (uint8_t i = 9; i < 13; i++) {\n+\t\tsensor->next = allocate_nim_sensor_group(m_port_no, ctx,\n+\t\t\t\t\t\t\t NT_SENSOR_SOURCE_LEVEL1_PORT,\n+\t\t\t\t\t\t\t &qsfp_sensor_level1[i]);\n+\t\tsensor = sensor->next;\n+\t\tsensor->read = &nim_read_qsfp_rx_power;\n+\t\t(*nim_sensors_cnt)++;\n+\t}\n+}\n+\n+struct nim_sensor_group *\n+allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx,\n+\t\t\t  enum nt_sensor_source_e ssrc,\n+\t\t\t  struct nt_adapter_sensor_description *sd)\n+{\n+\tstruct nim_sensor_group *sg = malloc(sizeof(struct nim_sensor_group));\n+\n+\tif (sg == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor group is NULL\", __func__);\n+\t\treturn NULL;\n+\t}\n+\tsg->sensor = allocate_sensor_by_description(port, ssrc, sd);\n+\tsg->ctx = ctx;\n+\tsg->next = NULL;\n+\treturn sg;\n+}\n+\n+int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port,\n+\t\t\t      struct nim_sensor_group **nim_sensors_ptr,\n+\t\t\t      uint16_t *nim_sensors_cnt)\n+{\n+\tint res = i2c_nim_common_construct(ctx);\n+\n+\tswitch (translate_nimid(ctx)) {\n+\tcase NT_NIM_SFP_SFP_PLUS:\n+\t\tsfp_preinit(ctx);\n+\t\tsfp_nim_add_all_sensors(port, ctx, nim_sensors_ptr,\n+\t\t\t\t\tnim_sensors_cnt);\n+\t\tbreak;\n+\tcase NT_NIM_QSFP_PLUS:\n+\t\tqsfpplus_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);\n+\t\tqsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr,\n+\t\t\t\t\t      nim_sensors_cnt);\n+\t\tbreak;\n+\tcase NT_NIM_QSFP28:\n+\t\tqsfp28_preinit(ctx, extra ? *(int8_t *)extra : (int8_t)-1);\n+\t\tqsfp_plus_nim_add_all_sensors(port, ctx, nim_sensors_ptr,\n+\t\t\t\t\t      nim_sensors_cnt);\n+\t\tbreak;\n+\tdefault:\n+\t\tres = 1;\n+\t\tNT_LOG(ERR, NTHW, \"NIM type %s is not supported.\\n\",\n+\t\t       nim_id_to_text(ctx->nim_id));\n+\t}\n+\n+\treturn res;\n+}\ndiff --git a/drivers/net/ntnic/nim/i2c_nim.h b/drivers/net/ntnic/nim/i2c_nim.h\nnew file mode 100644\nindex 0000000000..f664e6b7ee\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/i2c_nim.h\n@@ -0,0 +1,122 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef I2C_NIM_H_\n+#define I2C_NIM_H_\n+\n+#include \"nthw_drv.h\"\n+#include \"nim_defines.h\"\n+#include \"nt_link_speed.h\"\n+\n+#include \"sensors.h\"\n+\n+typedef struct sfp_nim_state {\n+\tuint8_t br; /* bit rate, units of 100 MBits/sec */\n+} sfp_nim_state_t, *sfp_nim_state_p;\n+\n+typedef struct nim_i2c_ctx {\n+\tnthw_iic_t hwiic; /* depends on *Fpga_t, instance number, and cycle time */\n+\tuint8_t instance;\n+\tuint8_t devaddr;\n+\tuint8_t regaddr;\n+\tuint8_t nim_id;\n+\tnt_port_type_t port_type;\n+\n+\tchar vendor_name[17];\n+\tchar prod_no[17];\n+\tchar serial_no[17];\n+\tchar date[9];\n+\tchar rev[5];\n+\tbool avg_pwr;\n+\tbool content_valid;\n+\tuint8_t pwr_level_req;\n+\tuint8_t pwr_level_cur;\n+\tuint16_t len_info[5];\n+\tuint32_t speed_mask; /* Speeds supported by the NIM */\n+\tint8_t lane_idx; /* Is this associated with a single lane or all lanes (-1) */\n+\tuint8_t lane_count;\n+\tuint32_t options;\n+\tbool tx_disable;\n+\tbool dmi_supp;\n+\n+\tunion {\n+\t\tstruct {\n+\t\t\tbool sfp28;\n+\t\t\tbool sfpplus;\n+\t\t\tbool dual_rate;\n+\t\t\tbool hw_rate_sel;\n+\t\t\tbool sw_rate_sel;\n+\t\t\tbool cu_type;\n+\t\t\tbool tri_speed;\n+\t\t\tbool ext_cal;\n+\t\t\tbool addr_chg;\n+\t\t} sfp;\n+\n+\t\tstruct {\n+\t\t\tbool rx_only;\n+\t\t\tbool qsfp28;\n+\t\t\tunion {\n+\t\t\t\tstruct {\n+\t\t\t\t\tuint8_t rev_compliance;\n+\t\t\t\t\tbool media_side_fec_ctrl;\n+\t\t\t\t\tbool host_side_fec_ctrl;\n+\t\t\t\t\tbool media_side_fec_ena;\n+\t\t\t\t\tbool host_side_fec_ena;\n+\t\t\t\t} qsfp28;\n+\t\t\t} specific_u;\n+\t\t} qsfp;\n+\n+\t} specific_u;\n+} nim_i2c_ctx_t, *nim_i2c_ctx_p;\n+\n+struct nim_sensor_group {\n+\tstruct nt_adapter_sensor *sensor;\n+\tvoid (*read)(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+\tstruct nim_i2c_ctx *ctx;\n+\tstruct nim_sensor_group *next;\n+};\n+\n+struct nim_sensor_group *\n+allocate_nim_sensor_group(uint8_t port, struct nim_i2c_ctx *ctx,\n+\t\t\t  enum nt_sensor_source_e ssrc,\n+\t\t\t  struct nt_adapter_sensor_description *sd);\n+\n+/*\n+ * Utility functions\n+ */\n+\n+nt_nim_identifier_t translate_nimid(const nim_i2c_ctx_t *ctx);\n+\n+/*\n+ * Builds an nim state for the port implied by `ctx`, returns zero\n+ * if successful, and non-zero otherwise. SFP and QSFP nims are supported\n+ */\n+int nim_state_build(nim_i2c_ctx_t *ctx, sfp_nim_state_t *state);\n+\n+/*\n+ * Returns a type name such as \"SFP/SFP+\" for a given NIM type identifier,\n+ * or the string \"ILLEGAL!\".\n+ */\n+const char *nim_id_to_text(uint8_t nim_id);\n+\n+int nim_sfp_nim_set_tx_laser_disable(nim_i2c_ctx_p ctx, bool disable);\n+\n+int nim_qsfp_plus_nim_set_tx_laser_disable(nim_i2c_ctx_t *ctx, bool disable,\n+\t\t\t\t       int lane_idx);\n+\n+int nim_set_link_speed(nim_i2c_ctx_p ctx, nt_link_speed_t speed);\n+\n+/*\n+ * This function tries to classify NIM based on it's ID and some register reads\n+ * and collects information into ctx structure. The @extra parameter could contain\n+ * the initialization argument for specific type of NIMS.\n+ */\n+int construct_and_preinit_nim(nim_i2c_ctx_p ctx, void *extra, uint8_t port,\n+\t\t\t      struct nim_sensor_group **nim_sensors_ptr,\n+\t\t\t      uint16_t *nim_sensors_cnt);\n+\n+int read_data_lin(nim_i2c_ctx_p ctx, uint16_t lin_addr, uint16_t length,\n+\t\tvoid *data);\n+\n+#endif /* I2C_NIM_H_ */\ndiff --git a/drivers/net/ntnic/nim/nim_defines.h b/drivers/net/ntnic/nim/nim_defines.h\nnew file mode 100644\nindex 0000000000..da3567d073\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/nim_defines.h\n@@ -0,0 +1,146 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NIM_DEFINES_H_\n+#define NIM_DEFINES_H_\n+\n+#define NIM_IDENTIFIER_ADDR 0 /* 1 byte */\n+\n+#define SFP_BIT_RATE_ADDR 12 /* 1 byte */\n+#define SFP_VENDOR_NAME_ADDR 20 /* 16bytes */\n+#define SFP_VENDOR_PN_ADDR 40 /* 16bytes */\n+#define SFP_VENDOR_REV_ADDR 56 /* 4bytes */\n+#define SFP_VENDOR_SN_ADDR 68 /* 16bytes */\n+#define SFP_VENDOR_DATE_ADDR 84 /* 8bytes */\n+\n+#define SFP_CONTROL_STATUS_LIN_ADDR (110U + 256U) /* 0xA2 */\n+#define SFP_SOFT_TX_DISABLE_BIT (1U << 6)\n+\n+#define QSFP_EXTENDED_IDENTIFIER 129\n+#define QSFP_SUP_LEN_INFO_ADDR 142 /* 5bytes */\n+#define QSFP_TRANSMITTER_TYPE_ADDR 147 /* 1byte */\n+#define QSFP_VENDOR_NAME_ADDR 148 /* 16bytes */\n+#define QSFP_VENDOR_PN_ADDR 168 /* 16bytes */\n+#define QSFP_VENDOR_REV_ADDR 184 /* 2bytes */\n+#define QSFP_VENDOR_SN_ADDR 196 /* 16bytes */\n+#define QSFP_VENDOR_DATE_ADDR 212 /* 8bytes */\n+\n+/* I2C addresses */\n+#define nim_i2c_0xa0 0xA0 /* Basic I2C address */\n+#define nim_i2c_0xa2 0xA2 /* Diagnostic monitoring */\n+#define nim_i2c_0xac 0xAC /* Address of integrated PHY */\n+\n+typedef enum {\n+\tNIM_OPTION_TEMP = 0,\n+\tNIM_OPTION_SUPPLY,\n+\tNIM_OPTION_RX_POWER,\n+\tNIM_OPTION_TX_BIAS,\n+\tNIM_OPTION_TX_POWER,\n+\tNIM_OPTION_TX_DISABLE,\n+\t/* Indicates that the module should be checked for the two next FEC types */\n+\tNIM_OPTION_FEC,\n+\tNIM_OPTION_MEDIA_SIDE_FEC,\n+\tNIM_OPTION_HOST_SIDE_FEC,\n+\tNIM_OPTION_RX_ONLY\n+} nim_option_t;\n+\n+enum nt_nim_identifier_e {\n+\tNT_NIM_UNKNOWN = 0x00, /* Nim type is unknown */\n+\tNT_NIM_GBIC = 0x01, /* Nim type = GBIC */\n+\tNT_NIM_FIXED = 0x02, /* Nim type = FIXED */\n+\tNT_NIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */\n+\tNT_NIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */\n+\tNT_NIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */\n+\tNT_NIM_XFP = 0x06, /* Nim type = XFP */\n+\tNT_NIM_XFF = 0x07, /* Nim type = XFF */\n+\tNT_NIM_XFP_E = 0x08, /* Nim type = XFP-E */\n+\tNT_NIM_XPAK = 0x09, /* Nim type = XPAK */\n+\tNT_NIM_X2 = 0x0A, /* Nim type = X2 */\n+\tNT_NIM_DWDM = 0x0B, /* Nim type = DWDM */\n+\tNT_NIM_QSFP = 0x0C, /* Nim type = QSFP */\n+\tNT_NIM_QSFP_PLUS = 0x0D, /* Nim type = QSFP+ */\n+\tNT_NIM_QSFP28 = 0x11, /* Nim type = QSFP28 */\n+\tNT_NIM_CFP4 = 0x12, /* Nim type = CFP4 */\n+};\n+\n+typedef enum nt_nim_identifier_e nt_nim_identifier_t;\n+\n+/*\n+ * Port types\n+ * The use of all non-generic XX_NOT_PRESENT is deprecated - use\n+ * NT_PORT_TYPE_NIM_NOT_PRESENT instead\n+ */\n+enum nt_port_type_e {\n+\tNT_PORT_TYPE_NOT_AVAILABLE =\n+\t\t0, /* The NIM/port type is not available (unknown) */\n+\tNT_PORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */\n+\tNT_PORT_TYPE_RJ45, /* RJ45 type */\n+\tNT_PORT_TYPE_SFP_NOT_PRESENT, /* SFP type but slot is empty */\n+\tNT_PORT_TYPE_SFP_SX, /* SFP SX */\n+\tNT_PORT_TYPE_SFP_SX_DD, /* SFP SX digital diagnostic */\n+\tNT_PORT_TYPE_SFP_LX, /* SFP LX */\n+\tNT_PORT_TYPE_SFP_LX_DD, /* SFP LX digital diagnostic */\n+\tNT_PORT_TYPE_SFP_ZX, /* SFP ZX */\n+\tNT_PORT_TYPE_SFP_ZX_DD, /* SFP ZX digital diagnostic */\n+\tNT_PORT_TYPE_SFP_CU, /* SFP copper */\n+\tNT_PORT_TYPE_SFP_CU_DD, /* SFP copper digital diagnostic */\n+\tNT_PORT_TYPE_SFP_NOT_RECOGNISED, /* SFP unknown */\n+\tNT_PORT_TYPE_XFP, /* XFP */\n+\tNT_PORT_TYPE_XPAK, /* XPAK */\n+\tNT_PORT_TYPE_SFP_CU_TRI_SPEED, /* SFP copper tri-speed */\n+\tNT_PORT_TYPE_SFP_CU_TRI_SPEED_DD, /* SFP copper tri-speed digital diagnostic */\n+\tNT_PORT_TYPE_SFP_PLUS, /* SFP+ type */\n+\tNT_PORT_TYPE_SFP_PLUS_NOT_PRESENT, /* SFP+ type but slot is empty */\n+\tNT_PORT_TYPE_XFP_NOT_PRESENT, /* XFP type but slot is empty */\n+\tNT_PORT_TYPE_QSFP_PLUS_NOT_PRESENT, /* QSFP type but slot is empty */\n+\tNT_PORT_TYPE_QSFP_PLUS, /* QSFP type */\n+\tNT_PORT_TYPE_SFP_PLUS_PASSIVE_DAC, /* SFP+ Passive DAC */\n+\tNT_PORT_TYPE_SFP_PLUS_ACTIVE_DAC, /* SFP+ Active DAC */\n+\tNT_PORT_TYPE_CFP4, /* CFP4 type */\n+\tNT_PORT_TYPE_CFP4_LR4 = NT_PORT_TYPE_CFP4, /* CFP4 100G, LR4 type */\n+\tNT_PORT_TYPE_CFP4_NOT_PRESENT, /* CFP4 type but slot is empty */\n+\tNT_PORT_TYPE_INITIALIZE, /* The port type is not fully established yet */\n+\tNT_PORT_TYPE_NIM_NOT_PRESENT, /* Generic \"Not present\" */\n+\tNT_PORT_TYPE_HCB, /* Test mode: Host Compliance Board */\n+\tNT_PORT_TYPE_NOT_SUPPORTED, /* The NIM type is not supported in this context */\n+\tNT_PORT_TYPE_SFP_PLUS_DUAL_RATE, /* SFP+ supports 1G/10G */\n+\tNT_PORT_TYPE_CFP4_SR4, /* CFP4 100G, SR4 type */\n+\tNT_PORT_TYPE_QSFP28_NOT_PRESENT, /* QSFP28 type but slot is empty */\n+\tNT_PORT_TYPE_QSFP28, /* QSFP28 type */\n+\tNT_PORT_TYPE_QSFP28_SR4, /* QSFP28-SR4 type */\n+\tNT_PORT_TYPE_QSFP28_LR4, /* QSFP28-LR4 type */\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tNT_PORT_TYPE_QSFP_PLUS_4X10,\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tNT_PORT_TYPE_QSFP_PASSIVE_DAC_4X10,\n+\tNT_PORT_TYPE_QSFP_PASSIVE_DAC =\n+\t\tNT_PORT_TYPE_QSFP_PASSIVE_DAC_4X10, /* QSFP passive DAC type */\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tNT_PORT_TYPE_QSFP_ACTIVE_DAC_4X10,\n+\tNT_PORT_TYPE_QSFP_ACTIVE_DAC =\n+\t\tNT_PORT_TYPE_QSFP_ACTIVE_DAC_4X10, /* QSFP active DAC type */\n+\tNT_PORT_TYPE_SFP_28, /* SFP28 type */\n+\tNT_PORT_TYPE_SFP_28_SR, /* SFP28-SR type */\n+\tNT_PORT_TYPE_SFP_28_LR, /* SFP28-LR type */\n+\tNT_PORT_TYPE_SFP_28_CR_CA_L, /* SFP28-CR-CA-L type */\n+\tNT_PORT_TYPE_SFP_28_CR_CA_S, /* SFP28-CR-CA-S type */\n+\tNT_PORT_TYPE_SFP_28_CR_CA_N, /* SFP28-CR-CA-N type */\n+\tNT_PORT_TYPE_QSFP28_CR_CA_L, /* QSFP28-CR-CA-L type */\n+\tNT_PORT_TYPE_QSFP28_CR_CA_S, /* QSFP28-CR-CA-S type */\n+\tNT_PORT_TYPE_QSFP28_CR_CA_N, /* QSFP28-CR-CA-N type */\n+\tNT_PORT_TYPE_SFP_28_SR_DR, /* SFP28-SR-DR type */\n+\tNT_PORT_TYPE_SFP_28_LR_DR, /* SFP28-LR-DR type */\n+\tNT_PORT_TYPE_SFP_FX, /* SFP FX */\n+\tNT_PORT_TYPE_SFP_PLUS_CU, /* SFP+ CU type */\n+\t/* QSFP28-FR type. Uses PAM4 modulation on one lane only */\n+\tNT_PORT_TYPE_QSFP28_FR,\n+\t/* QSFP28-DR type. Uses PAM4 modulation on one lane only */\n+\tNT_PORT_TYPE_QSFP28_DR,\n+\t/* QSFP28-LR type. Uses PAM4 modulation on one lane only */\n+\tNT_PORT_TYPE_QSFP28_LR,\n+};\n+\n+typedef enum nt_port_type_e nt_port_type_t, *nt_port_type_p;\n+\n+#endif /* NIM_DEFINES_H_ */\ndiff --git a/drivers/net/ntnic/nim/nt_link_speed.c b/drivers/net/ntnic/nim/nt_link_speed.c\nnew file mode 100644\nindex 0000000000..35c75f5e56\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/nt_link_speed.c\n@@ -0,0 +1,105 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <assert.h>\n+#include <stdbool.h>\n+#include <string.h>\n+\n+#include \"nt_link_speed.h\"\n+\n+const char *nt_translate_link_speed(nt_link_speed_t link_speed)\n+{\n+\tswitch (link_speed) {\n+\tcase NT_LINK_SPEED_UNKNOWN:\n+\t\treturn \"NotAvail\";\n+\tcase NT_LINK_SPEED_10M:\n+\t\treturn \"10M\";\n+\tcase NT_LINK_SPEED_100M:\n+\t\treturn \"100M\";\n+\tcase NT_LINK_SPEED_1G:\n+\t\treturn \"1G\";\n+\tcase NT_LINK_SPEED_10G:\n+\t\treturn \"10G\";\n+\tcase NT_LINK_SPEED_25G:\n+\t\treturn \"25G\";\n+\tcase NT_LINK_SPEED_40G:\n+\t\treturn \"40G\";\n+\tcase NT_LINK_SPEED_50G:\n+\t\treturn \"50G\";\n+\tcase NT_LINK_SPEED_100G:\n+\t\treturn \"100G\";\n+\tdefault:\n+\t\t/* DEBUG assert: remind developer that a switch/case entry is needed here.... */\n+\t\tassert(false);\n+\t\treturn \"Unhandled\";\n+\t}\n+}\n+\n+uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed)\n+{\n+\tuint64_t n_link_speed = 0ULL;\n+\n+\tswitch (e_link_speed) {\n+\tcase NT_LINK_SPEED_UNKNOWN:\n+\t\tn_link_speed = 0UL;\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_10M:\n+\t\tn_link_speed = (10ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_100M:\n+\t\tn_link_speed = (100ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_1G:\n+\t\tn_link_speed = (1ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_10G:\n+\t\tn_link_speed = (10ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_25G:\n+\t\tn_link_speed = (25ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_40G:\n+\t\tn_link_speed = (40ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_50G:\n+\t\tn_link_speed = (50ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tcase NT_LINK_SPEED_100G:\n+\t\tn_link_speed = (100ULL * 1000ULL * 1000ULL * 1000ULL);\n+\t\tbreak;\n+\tdefault:\n+\t\t/* DEBUG assert: remind developer that a switch/case entry is needed here.... */\n+\t\tassert(false);\n+\t\tn_link_speed = 0UL;\n+\t\tbreak;\n+\t}\n+\treturn n_link_speed;\n+}\n+\n+const char *nt_translate_link_speed_mask(uint32_t link_speed_mask, char *buffer,\n+\t\t\t\t      uint32_t length)\n+{\n+\tsize_t len = 0;\n+\n+\tbuffer[0] = 0;\n+\n+\tfor (int i = 0; i < 32; i++) {\n+\t\tif ((1U << i) & link_speed_mask) {\n+\t\t\tlen = strlen(buffer);\n+\n+\t\t\tif (len > 0) {\n+\t\t\t\tif ((length - len - 1) > 2) {\n+\t\t\t\t\tstrncat(buffer, \", \", length);\n+\t\t\t\t\tlen = strlen(buffer);\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (len < (length - 1))\n+\t\t\t\tstrncat(buffer, nt_translate_link_speed(1 << i),\n+\t\t\t\t\tlength);\n+\t\t}\n+\t}\n+\n+\treturn buffer;\n+}\ndiff --git a/drivers/net/ntnic/nim/nt_link_speed.h b/drivers/net/ntnic/nim/nt_link_speed.h\nnew file mode 100644\nindex 0000000000..969e3fb867\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/nt_link_speed.h\n@@ -0,0 +1,34 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NT_LINK_SPEED_H_\n+#define NT_LINK_SPEED_H_\n+\n+#include <stdint.h>\n+\n+/*\n+ * Link speed.\n+ * Note this is a bitmask.\n+ */\n+enum nt_link_speed_e {\n+\tNT_LINK_SPEED_UNKNOWN = 0,\n+\tNT_LINK_SPEED_10M = 0x01, /* 10 Mbps */\n+\tNT_LINK_SPEED_100M = 0x02, /* 100 Mbps */\n+\tNT_LINK_SPEED_1G = 0x04, /* 1 Gbps  (Autoneg only) */\n+\tNT_LINK_SPEED_10G = 0x08, /* 10 Gbps (Autoneg only) */\n+\tNT_LINK_SPEED_40G = 0x10, /* 40 Gbps (Autoneg only) */\n+\tNT_LINK_SPEED_100G = 0x20, /* 100 Gbps (Autoneg only) */\n+\tNT_LINK_SPEED_50G = 0x40, /* 50 Gbps (Autoneg only) */\n+\tNT_LINK_SPEED_25G = 0x80, /* 25 Gbps (Autoneg only) */\n+\tNT_LINK_SPEED_END /* always keep this entry as the last in enum */\n+};\n+\n+typedef enum nt_link_speed_e nt_link_speed_t;\n+\n+const char *nt_translate_link_speed(nt_link_speed_t link_speed);\n+const char *nt_translate_link_speed_mask(uint32_t link_speed_mask, char *buffer,\n+\t\t\t\t      uint32_t length);\n+uint64_t nt_get_link_speed(nt_link_speed_t e_link_speed);\n+\n+#endif /* NT_LINK_SPEED_H_ */\ndiff --git a/drivers/net/ntnic/nim/qsfp_registers.h b/drivers/net/ntnic/nim/qsfp_registers.h\nnew file mode 100644\nindex 0000000000..366dcbf06e\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/qsfp_registers.h\n@@ -0,0 +1,57 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _QSFP_REGISTERS_H\n+#define _QSFP_REGISTERS_H\n+\n+/*\n+ * QSFP Registers\n+ */\n+#define QSFP_INT_STATUS_RX_LOS_ADDR 3\n+#define QSFP_TEMP_LIN_ADDR 22\n+#define QSFP_VOLT_LIN_ADDR 26\n+#define QSFP_RX_PWR_LIN_ADDR 34 /* uint16_t [0..3] */\n+#define QSFP_TX_BIAS_LIN_ADDR 42 /* uint16_t [0..3] */\n+#define QSFP_TX_PWR_LIN_ADDR 50 /* uint16_t [0..3] */\n+\n+#define QSFP_CONTROL_STATUS_LIN_ADDR 86\n+#define QSFP_SOFT_TX_ALL_DISABLE_BITS 0x0F\n+\n+#define QSFP_EXTENDED_IDENTIFIER 129\n+#define QSFP_POWER_CLASS_BITS_1_4 0xC0\n+#define QSFP_POWER_CLASS_BITS_5_7 0x03\n+\n+#define QSFP_SUP_LEN_INFO_LIN_ADDR 142 /* 5bytes */\n+#define QSFP_TRANSMITTER_TYPE_LIN_ADDR 147 /* 1byte */\n+#define QSFP_VENDOR_NAME_LIN_ADDR 148 /* 16bytes */\n+#define QSFP_VENDOR_PN_LIN_ADDR 168 /* 16bytes */\n+#define QSFP_VENDOR_SN_LIN_ADDR 196 /* 16bytes */\n+#define QSFP_VENDOR_DATE_LIN_ADDR 212 /* 8bytes */\n+#define QSFP_VENDOR_REV_LIN_ADDR 184 /* 2bytes */\n+\n+#define QSFP_SPEC_COMPLIANCE_CODES_ADDR 131 /* 8 bytes */\n+#define QSFP_EXT_SPEC_COMPLIANCE_CODES_ADDR 192 /* 1 byte */\n+\n+#define QSFP_OPTION3_LIN_ADDR 195\n+#define QSFP_OPTION3_TX_DISABLE_BIT (1 << 4)\n+\n+#define QSFP_DMI_OPTION_LIN_ADDR 220\n+#define QSFP_DMI_AVG_PWR_BIT (1 << 3)\n+\n+#define QSFP_TEMP_THRESH_LIN_ADDR (128 + (3 * 128)) /* Page 3 */\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define QSFP_VOLT_THRESH_LIN_ADDR (144 + (3 * 128)) /* Page 3 */\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define QSFP_RX_PWR_THRESH_LIN_ADDR (176 + (3 * 128)) /* Page 3 */\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define QSFP_BIAS_THRESH_LIN_ADDR (184 + (3 * 128)) /* Page 3 */\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define QSFP_TX_PWR_THRESH_LIN_ADDR (192 + (3 * 128)) /* Page 3 */\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#endif /* _QSFP_REGISTERS_H */\ndiff --git a/drivers/net/ntnic/nim/qsfp_sensors.c b/drivers/net/ntnic/nim/qsfp_sensors.c\nnew file mode 100644\nindex 0000000000..8264f8fb62\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/qsfp_sensors.c\n@@ -0,0 +1,174 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <stdbool.h>\n+\n+#include \"qsfp_sensors.h\"\n+\n+#include \"ntlog.h\"\n+#include \"qsfp_registers.h\"\n+\n+static bool qsfp_plus_nim_get_sensor(nim_i2c_ctx_p ctx, uint16_t addr,\n+\t\t\t\t   nim_option_t nim_option, uint8_t count,\n+\t\t\t\t   uint16_t *p_lane_values)\n+{\n+\t(void)nim_option;\n+\n+\tread_data_lin(ctx, addr, (uint16_t)(sizeof(uint16_t) * count),\n+\t\t    p_lane_values);\n+\n+\tfor (int i = 0; i < count; i++) {\n+\t\t*p_lane_values = (*p_lane_values); /* Swap to little endian */\n+\n+#ifdef NIM_DMI_TEST_VALUE\n+\t\tif (nim_option == NIM_OPTION_RX_POWER)\n+\t\t\t*p_lane_values = (uint16_t)NIM_DMI_RX_PWR_TEST_VALUE;\n+\t\telse\n+\t\t\t*p_lane_values = (uint16_t)NIM_DMI_TEST_VALUE;\n+#endif\n+\n+\t\tp_lane_values++;\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Read NIM temperature\n+ */\n+static bool qsfp_plus_nim_get_temperature(nim_i2c_ctx_p ctx, int16_t *p_value)\n+{\n+\treturn qsfp_plus_nim_get_sensor(ctx, QSFP_TEMP_LIN_ADDR, NIM_OPTION_TEMP,\n+\t\t\t\t      1, (uint16_t *)p_value);\n+}\n+\n+/*\n+ * Read NIM supply voltage\n+ */\n+static bool qsfp_plus_nim_get_supply_voltage(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn qsfp_plus_nim_get_sensor(ctx, QSFP_VOLT_LIN_ADDR,\n+\t\t\t\t      NIM_OPTION_SUPPLY, 1, p_value);\n+}\n+\n+/*\n+ * Read NIM bias current for four lanes\n+ */\n+static bool qsfp_plus_nim_get_tx_bias_current(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn qsfp_plus_nim_get_sensor(ctx, QSFP_TX_BIAS_LIN_ADDR,\n+\t\t\t\t      NIM_OPTION_TX_BIAS, 4, p_value);\n+}\n+\n+/*\n+ * Read NIM TX optical power for four lanes\n+ */\n+static bool qsfp_plus_nim_get_tx_power(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn qsfp_plus_nim_get_sensor(ctx, QSFP_TX_PWR_LIN_ADDR,\n+\t\t\t\t      NIM_OPTION_TX_POWER, 4, p_value);\n+}\n+\n+/*\n+ * Read NIM RX optical power for four lanes\n+ */\n+static bool qsfp_plus_nim_get_rx_power(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn qsfp_plus_nim_get_sensor(ctx, QSFP_TX_PWR_LIN_ADDR,\n+\t\t\t\t      NIM_OPTION_RX_POWER, 4, p_value);\n+}\n+\n+void nim_read_qsfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tint16_t res;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (qsfp_plus_nim_get_temperature(sg->ctx, &res))\n+\t\tupdate_sensor_value(sg->sensor, (int)(res * 10 / 256));\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\n+\n+void nim_read_qsfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t res;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (qsfp_plus_nim_get_supply_voltage(sg->ctx, &res))\n+\t\tupdate_sensor_value(sg->sensor, (int)((res) / 10));\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\n+\n+void nim_read_qsfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp[4] = { 0 };\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tbool res = qsfp_plus_nim_get_tx_bias_current(sg->ctx, temp);\n+\n+\tif (res) {\n+\t\tfor (uint8_t i = 0; i < sg->ctx->lane_count; i++)\n+\t\t\tupdate_sensor_value(sg->sensor, (int)temp[i] * 2);\n+\t} else {\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+\t}\n+}\n+\n+void nim_read_qsfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp[4] = { 0 };\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tbool res = qsfp_plus_nim_get_tx_power(sg->ctx, temp);\n+\n+\tif (res) {\n+\t\tfor (uint8_t i = 0; i < sg->ctx->lane_count; i++)\n+\t\t\tupdate_sensor_value(sg->sensor, (int)temp[i]);\n+\t} else {\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+\t}\n+}\n+\n+void nim_read_qsfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp[4] = { 0 };\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tbool res = qsfp_plus_nim_get_rx_power(sg->ctx, temp);\n+\n+\tif (res) {\n+\t\tfor (uint8_t i = 0; i < sg->ctx->lane_count; i++)\n+\t\t\tupdate_sensor_value(sg->sensor, (int)temp[i]);\n+\t} else {\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+\t}\n+}\ndiff --git a/drivers/net/ntnic/nim/qsfp_sensors.h b/drivers/net/ntnic/nim/qsfp_sensors.h\nnew file mode 100644\nindex 0000000000..de64b978cb\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/qsfp_sensors.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _QSFP_H\n+#define _QSFP_H\n+\n+#include \"sensors.h\"\n+#include \"i2c_nim.h\"\n+\n+/* Read functions */\n+void nim_read_qsfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_qsfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_qsfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_qsfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_qsfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+\n+#endif /* _QSFP_H */\ndiff --git a/drivers/net/ntnic/nim/sfp_p_registers.h b/drivers/net/ntnic/nim/sfp_p_registers.h\nnew file mode 100644\nindex 0000000000..a0fbe2afd7\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/sfp_p_registers.h\n@@ -0,0 +1,100 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _SFP_P_REG_H\n+#define _SFP_P_REG_H\n+\n+/*\n+ * SFP/SFP+ Registers\n+ */\n+#define SFP_GB_ETH_COMP_CODES_LIN_ADDR 6\n+#define SFP_GB_ETH_COMP_1000BASET_BIT (1 << 3)\n+#define SFP_GB_ETH_COMP_1000BASECX_BIT (1 << 2)\n+#define SFP_GB_ETH_COMP_1000BASELX_BIT (1 << 1)\n+#define SFP_GB_ETH_COMP_1000BASESX_BIT (1 << 0)\n+\n+#define SFP_FIBER_CHAN_TRANS_TECH_LIN_ADDR 8\n+#define SFP_FIBER_CHAN_TRANS_TECH_ACTIVE_CU_BIT (1 << 3)\n+#define SFP_FIBER_CHAN_TRANS_TECH_PASSIVE_CU_BIT (1 << 2)\n+\n+#define SFP_FIBER_CHAN_TRANS_MEDIA_LIN_ADDR 9\n+#define SFP_FIBER_CHAN_TRANS_MEDIA_MM62_BIT (1 << 3)\n+#define SFP_FIBER_CHAN_TRANS_MEDIA_MM50_BIT (1 << 2)\n+#define SFP_FIBER_CHAN_TRANS_MEDIA_SM_BIT (1 << 0)\n+\n+#define SFP_CU_LINK_LEN_LIN_ADDR 18 /* 1byte */\n+#define SFP_SUP_LEN_INFO_LIN_ADDR 14 /* 5bytes */\n+#define SFP_CU_LINK_LEN_LIN_ADDR 18 /* 1byte */\n+#define SFP_VENDOR_NAME_LIN_ADDR 20 /* 16bytes */\n+#define SFP_VENDOR_PN_LIN_ADDR 40 /* 16bytes */\n+#define SFP_VENDOR_REV_LIN_ADDR 56 /* 4bytes */\n+#define SFP_VENDOR_SN_LIN_ADDR 68 /* 16bytes */\n+#define SFP_VENDOR_DATE_LIN_ADDR 84 /* 8bytes */\n+\n+/* The following field is only relevant to SFP+ and is marked as reserved for SFP */\n+#define SFP_OPTION0_LIN_ADDR 64\n+#define SFP_POWER_LEVEL2_REQ_BIT (1 << 1)\n+\n+#define SFP_DMI_OPTION_LIN_ADDR (92)\n+#define SFP_DMI_IMPL_BIT (1 << 6)\n+#define SFP_DMI_EXT_CAL_BIT (1 << 4)\n+#define SFP_DMI_AVG_PWR_BIT (1 << 3)\n+#define SFP_DMI_ADDR_CHG_BIT (1 << 2)\n+\n+#define SFP_ENHANCED_OPTIONS_LIN_ADDR (93)\n+#define SFP_SOFT_TX_FAULT_IMPL_BIT (1 << 5)\n+#define SFP_SOFT_TX_DISABLE_IMPL_BIT (1 << 6)\n+\n+#define SFP_SFF8472_COMPLIANCE_LIN_ADDR 94\n+\n+#define SFP_TEMP_THRESH_LIN_ADDR (0 + 256)\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define SFP_VOLT_THRESH_LIN_ADDR (8 + 256)\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define SFP_TX_BIAS_THRESH_LIN_ADDR (16 + 256)\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define SFP_TX_PWR_THRESH_LIN_ADDR (24 + 256)\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+#define SFP_RX_PWR_THRESH_LIN_ADDR (32 + 256)\n+/* 8bytes: HighAlarm, LowAlarm, HighWarn, LowWarn each 2 bytes */\n+\n+/* Calibration data addresses */\n+#define SFP_RX_PWR_COEFF_LIN_ADDR (56 + 256) /* 5 x 32bit float  values */\n+\n+#define SFP_TX_BIAS_SLOPE_LIN_ADDR (76 + 256)\n+#define SFP_TX_BIAS_OFFSET_LIN_ADDR (78 + 256)\n+\n+#define SFP_TX_PWR_SLOPE_LIN_ADDR (80 + 256)\n+#define SFP_TX_PWR_OFFSET_LIN_ADDR (82 + 256)\n+\n+#define SFP_TEMP_SLOPE_LIN_ADDR (84 + 256)\n+#define SFP_TEMP_OFFSET_LIN_ADDR (86 + 256)\n+\n+#define SFP_VOLT_SLOPE_LIN_ADDR (88 + 256)\n+#define SFP_VOLT_OFFSET_LIN_ADDR (90 + 256)\n+\n+/* Live data */\n+#define SFP_TEMP_LIN_ADDR (96 + 256)\n+#define SFP_VOLT_LIN_ADDR (98 + 256)\n+#define SFP_TX_BIAS_LIN_ADDR (100 + 256)\n+#define SFP_TX_PWR_LIN_ADDR (102 + 256)\n+#define SFP_RX_PWR_LIN_ADDR (104 + 256)\n+\n+#define SFP_SOFT_RATE0_BIT (1 << 3)\n+#define SFP_TX_FAULT_SET_BIT (1 << 2)\n+\n+#define SFP_EXT_CTRL_STAT0_LIN_ADDR (118 + 256) /* 0xA2 area */\n+#define SFP_SOFT_RATE1_BIT (1 << 3)\n+#define SFP_POWER_LEVEL2_GET_BIT (1 << 1) /* For reading the actual level */\n+#define SFP_POWER_LEVEL2_SET_BIT (1 << 0) /* For setting the wanted level */\n+\n+/* PHY addresses */\n+#define SFP_PHY_LIN_ADDR (12 * 128)\n+#define SFP_PHY_LIN_RNG 32 /* 16bit words */\n+\n+#endif /* _SFP_P_REG_H */\ndiff --git a/drivers/net/ntnic/nim/sfp_sensors.c b/drivers/net/ntnic/nim/sfp_sensors.c\nnew file mode 100644\nindex 0000000000..766d6feaf3\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/sfp_sensors.c\n@@ -0,0 +1,288 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <arpa/inet.h>\n+#include <stdbool.h>\n+\n+#include \"ntlog.h\"\n+#include \"sfp_sensors.h\"\n+\n+#include \"sfp_p_registers.h\"\n+\n+/*\n+ * Return calibrated data from an SFP module.\n+ * It is first investigated if external calibration is to be used and if it is\n+ * calibration data is retrieved. The function can only be used when calibration\n+ * consists of a slope and offset factor. After function return p_data will point\n+ * to 16bit data that can be either signed or unsigned.\n+ */\n+static bool sfp_nim_get_dmi_data(uint16_t data_addr, uint16_t slope_addr,\n+\t\t\t       uint16_t offset_addr, void *p_value,\n+\t\t\t       bool signed_data, nim_i2c_ctx_p ctx)\n+{\n+\tint32_t value;\n+\tuint16_t slope = 1;\n+\tint16_t offset = 0;\n+\n+\tif (!ctx->dmi_supp)\n+\t\treturn false;\n+\n+\t/* Read data in big endian format */\n+\tread_data_lin(ctx, data_addr, 2, p_value);\n+\t*(uint16_t *)p_value =\n+\t\thtons(*(uint16_t *)p_value); /* Swap to little endian */\n+\n+\t/*\n+\t * Inject test value which can be both signed and unsigned but handle\n+\t * here as unsigned\n+\t */\n+#ifdef NIM_DMI_TEST_VALUE\n+\t*(uint16_t *)p_value = (uint16_t)NIM_DMI_TEST_VALUE;\n+#endif\n+\n+#if defined(NIM_DMI_TEST_SLOPE) || defined(NIM_DMI_TEST_OFFSET)\n+\tctx->specific_u.sfp.ext_cal = true;\n+#endif\n+\n+\tif (ctx->specific_u.sfp.ext_cal) {\n+\t\t/* External calibration is needed */\n+\t\tread_data_lin(ctx, slope_addr, sizeof(slope), &slope);\n+\t\tread_data_lin(ctx, offset_addr, sizeof(offset), &offset);\n+\n+\t\t/* Swap calibration to little endian */\n+\t\tslope = htons(slope);\n+\t\toffset = htons(offset);\n+\n+#ifdef NIM_DMI_TEST_SLOPE\n+\t\tslope = NIM_DMI_TEST_SLOPE;\n+#endif\n+\n+#ifdef NIM_DMI_TEST_OFFSET\n+\t\toffset = NIM_DMI_TEST_OFFSET; /* 0x0140 equals 1.25 */\n+#endif\n+\n+\t\tif (signed_data) {\n+\t\t\tvalue = *(int16_t *)p_value * slope / 256 + offset;\n+\n+\t\t\tif (value > INT16_MAX)\n+\t\t\t\tvalue = INT16_MAX;\n+\t\t\telse if (value < INT16_MIN)\n+\t\t\t\tvalue = INT16_MIN;\n+\n+\t\t\t*(int16_t *)p_value = (int16_t)value;\n+\t\t} else {\n+\t\t\tvalue = *(uint16_t *)p_value * slope / 256 + offset;\n+\n+\t\t\tif (value > UINT16_MAX)\n+\t\t\t\tvalue = UINT16_MAX;\n+\t\t\telse if (value < 0)\n+\t\t\t\tvalue = 0;\n+\n+\t\t\t*(uint16_t *)p_value = (uint16_t)value;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Read NIM temperature\n+ */\n+static bool sfp_nim_get_temperature(nim_i2c_ctx_p ctx, int16_t *p_value)\n+{\n+\treturn sfp_nim_get_dmi_data(SFP_TEMP_LIN_ADDR, SFP_TEMP_SLOPE_LIN_ADDR,\n+\t\t\t\t  SFP_TEMP_OFFSET_LIN_ADDR, p_value, true, ctx);\n+}\n+\n+/*\n+ * Read NIM supply voltage\n+ */\n+static bool sfp_nim_get_supply_voltage(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn sfp_nim_get_dmi_data(SFP_VOLT_LIN_ADDR, SFP_VOLT_SLOPE_LIN_ADDR,\n+\t\t\t\t  SFP_VOLT_OFFSET_LIN_ADDR, p_value, false, ctx);\n+}\n+\n+/*\n+ * Read NIM bias current\n+ */\n+static bool sfp_nim_get_tx_bias_current(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn sfp_nim_get_dmi_data(SFP_TX_BIAS_LIN_ADDR,\n+\t\t\t\t  SFP_TX_BIAS_SLOPE_LIN_ADDR,\n+\t\t\t\t  SFP_TX_BIAS_OFFSET_LIN_ADDR, p_value, false,\n+\t\t\t\t  ctx);\n+}\n+\n+/*\n+ * Read NIM TX optical power\n+ */\n+static bool sfp_nim_get_tx_power(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn sfp_nim_get_dmi_data(SFP_TX_PWR_LIN_ADDR,\n+\t\t\t\t  SFP_TX_PWR_SLOPE_LIN_ADDR,\n+\t\t\t\t  SFP_TX_PWR_OFFSET_LIN_ADDR, p_value, false,\n+\t\t\t\t  ctx);\n+}\n+\n+/*\n+ * Return the SFP received power in units of 0.1uW from DMI data.\n+ * If external calibration is necessary, the calibration data is retrieved and\n+ * the calibration is carried out.\n+ */\n+static bool sfp_nim_get_calibrated_rx_power(nim_i2c_ctx_p ctx, uint16_t addr,\n+\t\tuint16_t *p_value)\n+{\n+\tfloat rx_pwr_cal[5];\n+\tfloat power_raised;\n+\tfloat rx_power;\n+\n+\t/* Read data in big endian format */\n+\tread_data_lin(ctx, addr, sizeof(*p_value), p_value);\n+\t*(uint16_t *)p_value =\n+\t\thtons(*(uint16_t *)p_value); /* Swap to little endian */\n+\n+#ifdef NIM_DMI_RX_PWR_TEST_VALUE\n+\t*p_value = NIM_DMI_RX_PWR_TEST_VALUE;\n+#endif\n+\n+#ifdef NIM_DMI_RX_PWR_CAL_DATA\n+\tctx->specific_u.sfp.ext_cal = true;\n+#endif\n+\n+\tif (ctx->specific_u.sfp.ext_cal) {\n+\t\t/* Read calibration data in big endian format */\n+\t\tread_data_lin(ctx, SFP_RX_PWR_COEFF_LIN_ADDR, sizeof(rx_pwr_cal),\n+\t\t\t    rx_pwr_cal);\n+\n+\t\tfor (int i = 0; i < 5; i++) {\n+\t\t\tuint32_t *p_val = (uint32_t *)&rx_pwr_cal[i];\n+\t\t\t*p_val = ntohl(*p_val); /* 32 bit swap */\n+\t\t}\n+\n+#ifdef NIM_DMI_RX_PWR_CAL_DATA\n+\t\t/* Testdata for verification */\n+\t\tNIM_DMI_RX_PWR_CAL_DATA\n+#endif\n+\n+\t\t/*\n+\t\t * If SFP module specifies external calibration - use calibration data\n+\t\t * according to the polynomial correction formula\n+\t\t * RxPwrCal = Coeff0 + Coeff1 * RxPwr   + Coeff2 * RxPwr^2 +\n+\t\t *                     Coeff3 * RxPwr^3 + Coeff4 * RxPwr^4\n+\t\t */\n+\t\tpower_raised = 1.0;\n+\t\trx_power = rx_pwr_cal[4]; /* Coeff0 */\n+\n+\t\tfor (int i = 3; i >= 0; i--) {\n+\t\t\tpower_raised *= (float)*p_value;\n+\t\t\trx_power += rx_pwr_cal[i] * power_raised;\n+\t\t}\n+\n+\t\t/* Check out for out of range */\n+\t\tif (rx_power > 65535)\n+\t\t\treturn false;\n+\n+\t\tif (rx_power < 0)\n+\t\t\t*p_value = 0;\n+\t\telse\n+\t\t\t*p_value = (uint16_t)rx_power;\n+\t}\n+\n+\treturn true;\n+}\n+\n+/*\n+ * Read RX optical power if it exists\n+ */\n+static bool sfp_nim_get_rx_power(nim_i2c_ctx_p ctx, uint16_t *p_value)\n+{\n+\treturn sfp_nim_get_calibrated_rx_power(ctx, SFP_RX_PWR_LIN_ADDR, p_value);\n+}\n+\n+void nim_read_sfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tint16_t temp;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (sfp_nim_get_temperature(sg->ctx, &temp))\n+\t\tupdate_sensor_value(sg->sensor, (int)(temp * 10 / 256));\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\n+\n+void nim_read_sfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (sfp_nim_get_supply_voltage(sg->ctx, &temp)) {\n+\t\tupdate_sensor_value(sg->sensor,\n+\t\t\t\t    (int)(temp / 10)); /* Unit: 100uV -> 1mV */\n+\t} else {\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+\t}\n+}\n+\n+void nim_read_sfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (sfp_nim_get_tx_bias_current(sg->ctx, &temp))\n+\t\tupdate_sensor_value(sg->sensor, (int)(temp * 2));\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\n+\n+void nim_read_sfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (sfp_nim_get_tx_power(sg->ctx, &temp))\n+\t\tupdate_sensor_value(sg->sensor, (int)temp);\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\n+\n+void nim_read_sfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint16_t temp;\n+\t(void)t_spi;\n+\n+\tif (sg == NULL || sg->ctx == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\n+\tif (sfp_nim_get_rx_power(sg->ctx, &temp))\n+\t\tupdate_sensor_value(sg->sensor, (int)temp);\n+\n+\telse\n+\t\tupdate_sensor_value(sg->sensor, -1);\n+}\ndiff --git a/drivers/net/ntnic/nim/sfp_sensors.h b/drivers/net/ntnic/nim/sfp_sensors.h\nnew file mode 100644\nindex 0000000000..ab56027dc8\n--- /dev/null\n+++ b/drivers/net/ntnic/nim/sfp_sensors.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _SFP_H\n+#define _SFP_H\n+\n+#include \"sensors.h\"\n+#include \"i2c_nim.h\"\n+\n+/* Read functions */\n+void nim_read_sfp_temp(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_sfp_voltage(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_sfp_bias_current(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_sfp_tx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+void nim_read_sfp_rx_power(struct nim_sensor_group *sg, nthw_spis_t *t_spi);\n+\n+#endif /* _SFP_H */\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c b/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c\nindex efdcc222a8..bd7cd2a27c 100644\n--- a/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c\n+++ b/drivers/net/ntnic/nthw/core/nthw_clock_profiles.c\n@@ -5,5 +5,12 @@\n #include \"nthw_clock_profiles.h\"\n \n /* Clock profile for NT200A02 2x40G, 2x100G */\n-const int n_data_si5340_nt200a02_u23_v5;\n-const clk_profile_data_fmt2_t *p_data_si5340_nt200a02_u23_v5;\n+#define si5340_revd_register_t type_si5340_nt200a02_u23_v5\n+#define si5340_revd_registers data_si5340_nt200a02_u23_v5\n+#include \"nthw_nt200a02_u23_si5340_v5.h\"\n+const int n_data_si5340_nt200a02_u23_v5 = SI5340_REVD_REG_CONFIG_NUM_REGS;\n+const clk_profile_data_fmt2_t *p_data_si5340_nt200a02_u23_v5 =\n+\t(const clk_profile_data_fmt2_t *)&data_si5340_nt200a02_u23_v5[0];\n+#undef si5340_revd_registers\n+#undef si5340_revd_register_t\n+#undef SI5340_REVD_REG_CONFIG_HEADER /*Disable the include once protection */\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_core.h b/drivers/net/ntnic/nthw/core/nthw_core.h\nindex 798a95d5cf..025b6b61cc 100644\n--- a/drivers/net/ntnic/nthw/core/nthw_core.h\n+++ b/drivers/net/ntnic/nthw/core/nthw_core.h\n@@ -16,9 +16,11 @@\n #include \"nthw_pci_ta.h\"\n #include \"nthw_iic.h\"\n \n+#include \"nthw_gmf.h\"\n #include \"nthw_gpio_phy.h\"\n #include \"nthw_mac_pcs.h\"\n #include \"nthw_mac_pcs_xxv.h\"\n+#include \"nthw_rmc.h\"\n #include \"nthw_sdc.h\"\n \n #include \"nthw_spim.h\"\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.c b/drivers/net/ntnic/nthw/core/nthw_gmf.c\nnew file mode 100644\nindex 0000000000..fe63c461e5\n--- /dev/null\n+++ b/drivers/net/ntnic/nthw/core/nthw_gmf.c\n@@ -0,0 +1,290 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <limits.h>\n+#include <math.h>\n+#include \"ntlog.h\"\n+\n+#include \"nthw_drv.h\"\n+#include \"nthw_register.h\"\n+\n+#include \"nthw_gmf.h\"\n+\n+nthw_gmf_t *nthw_gmf_new(void)\n+{\n+\tnthw_gmf_t *p = malloc(sizeof(nthw_gmf_t));\n+\n+\tif (p)\n+\t\tmemset(p, 0, sizeof(nthw_gmf_t));\n+\treturn p;\n+}\n+\n+void nthw_gmf_delete(nthw_gmf_t *p)\n+{\n+\tif (p) {\n+\t\tmemset(p, 0, sizeof(nthw_gmf_t));\n+\t\tfree(p);\n+\t}\n+}\n+\n+int nthw_gmf_init(nthw_gmf_t *p, nt_fpga_t *p_fpga, int n_instance)\n+{\n+\tnt_module_t *mod = fpga_query_module(p_fpga, MOD_GMF, n_instance);\n+\n+\tif (p == NULL)\n+\t\treturn mod == NULL ? -1 : 0;\n+\n+\tif (mod == NULL) {\n+\t\tNT_LOG(ERR, NTHW, \"%s: GMF %d: no such instance\\n\",\n+\t\t       p_fpga->p_fpga_info->mp_adapter_id_str, n_instance);\n+\t\treturn -1;\n+\t}\n+\n+\tp->mp_fpga = p_fpga;\n+\tp->mn_instance = n_instance;\n+\tp->mp_mod_gmf = mod;\n+\n+\tp->mp_ctrl = module_get_register(p->mp_mod_gmf, GMF_CTRL);\n+\tp->mp_ctrl_enable = register_get_field(p->mp_ctrl, GMF_CTRL_ENABLE);\n+\tp->mp_ctrl_ifg_enable = register_get_field(p->mp_ctrl, GMF_CTRL_IFG_ENABLE);\n+\tp->mp_ctrl_ifg_auto_adjust_enable =\n+\t\tregister_get_field(p->mp_ctrl, GMF_CTRL_IFG_AUTO_ADJUST_ENABLE);\n+\n+\tp->mp_speed = module_get_register(p->mp_mod_gmf, GMF_SPEED);\n+\tp->mp_speed_ifg_speed = register_get_field(p->mp_speed, GMF_SPEED_IFG_SPEED);\n+\n+\tp->mp_ifg_clock_delta =\n+\t\tmodule_get_register(p->mp_mod_gmf, GMF_IFG_SET_CLOCK_DELTA);\n+\tp->mp_ifg_clock_delta_delta =\n+\t\tregister_get_field(p->mp_ifg_clock_delta, GMF_IFG_SET_CLOCK_DELTA_DELTA);\n+\n+\tp->mp_ifg_max_adjust_slack =\n+\t\tmodule_get_register(p->mp_mod_gmf, GMF_IFG_MAX_ADJUST_SLACK);\n+\tp->mp_ifg_max_adjust_slack_slack =\n+\t\tregister_get_field(p->mp_ifg_max_adjust_slack, GMF_IFG_MAX_ADJUST_SLACK_SLACK);\n+\n+\tp->mp_debug_lane_marker =\n+\t\tmodule_get_register(p->mp_mod_gmf, GMF_DEBUG_LANE_MARKER);\n+\tp->mp_debug_lane_marker_compensation =\n+\t\tregister_get_field(p->mp_debug_lane_marker, GMF_DEBUG_LANE_MARKER_COMPENSATION);\n+\n+\tp->mp_stat_sticky = module_get_register(p->mp_mod_gmf, GMF_STAT_STICKY);\n+\tp->mp_stat_sticky_data_underflowed =\n+\t\tregister_get_field(p->mp_stat_sticky, GMF_STAT_STICKY_DATA_UNDERFLOWED);\n+\tp->mp_stat_sticky_ifg_adjusted =\n+\t\tregister_get_field(p->mp_stat_sticky, GMF_STAT_STICKY_IFG_ADJUSTED);\n+\n+\tp->mn_param_gmf_ifg_speed_mul =\n+\t\tfpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_MUL, 1);\n+\tp->mn_param_gmf_ifg_speed_div =\n+\t\tfpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_DIV, 1);\n+\n+\tp->m_administrative_block = false;\n+\n+\tp->mp_stat_next_pkt = module_query_register(p->mp_mod_gmf, GMF_STAT_NEXT_PKT);\n+\tif (p->mp_stat_next_pkt) {\n+\t\tp->mp_stat_next_pkt_ns =\n+\t\t\tregister_query_field(p->mp_stat_next_pkt,\n+\t\t\t\t\t     GMF_STAT_NEXT_PKT_NS);\n+\t} else {\n+\t\tp->mp_stat_next_pkt_ns = NULL;\n+\t}\n+\tp->mp_stat_max_delayed_pkt =\n+\t\tmodule_query_register(p->mp_mod_gmf, GMF_STAT_MAX_DELAYED_PKT);\n+\tif (p->mp_stat_max_delayed_pkt) {\n+\t\tp->mp_stat_max_delayed_pkt_ns =\n+\t\t\tregister_query_field(p->mp_stat_max_delayed_pkt,\n+\t\t\t\t\t     GMF_STAT_MAX_DELAYED_PKT_NS);\n+\t} else {\n+\t\tp->mp_stat_max_delayed_pkt_ns = NULL;\n+\t}\n+\tp->mp_ctrl_ifg_tx_now_always =\n+\t\tregister_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_NOW_ALWAYS);\n+\tp->mp_ctrl_ifg_tx_on_ts_always =\n+\t\tregister_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_ON_TS_ALWAYS);\n+\n+\tp->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock =\n+\t\tregister_query_field(p->mp_ctrl, GMF_CTRL_IFG_TX_ON_TS_ADJUST_ON_SET_CLOCK);\n+\n+\tp->mp_ifg_clock_delta_adjust =\n+\t\tmodule_query_register(p->mp_mod_gmf, GMF_IFG_SET_CLOCK_DELTA_ADJUST);\n+\tif (p->mp_ifg_clock_delta_adjust) {\n+\t\tp->mp_ifg_clock_delta_adjust_delta =\n+\t\t\tregister_query_field(p->mp_ifg_clock_delta_adjust,\n+\t\t\t\t\t     GMF_IFG_SET_CLOCK_DELTA_ADJUST_DELTA);\n+\t} else {\n+\t\tp->mp_ifg_clock_delta_adjust_delta = NULL;\n+\t}\n+\treturn 0;\n+}\n+\n+void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable)\n+{\n+\tif (!p->m_administrative_block)\n+\t\tfield_set_val_flush32(p->mp_ctrl_enable, enable ? 1 : 0);\n+}\n+\n+void nthw_gmf_set_ifg_enable(nthw_gmf_t *p, bool enable)\n+{\n+\tfield_set_val_flush32(p->mp_ctrl_ifg_enable, enable ? 1 : 0);\n+}\n+\n+void nthw_gmf_set_tx_now_always_enable(nthw_gmf_t *p, bool enable)\n+{\n+\tif (p->mp_ctrl_ifg_tx_now_always)\n+\t\tfield_set_val_flush32(p->mp_ctrl_ifg_tx_now_always, enable ? 1 : 0);\n+}\n+\n+void nthw_gmf_set_tx_on_ts_always_enable(nthw_gmf_t *p, bool enable)\n+{\n+\tif (p->mp_ctrl_ifg_tx_on_ts_always)\n+\t\tfield_set_val_flush32(p->mp_ctrl_ifg_tx_on_ts_always, enable ? 1 : 0);\n+}\n+\n+void nthw_gmf_set_tx_on_ts_adjust_on_set_clock(nthw_gmf_t *p, bool enable)\n+{\n+\tif (p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock) {\n+\t\tfield_set_val_flush32(p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock,\n+\t\t\t\t    enable ? 1 : 0);\n+\t}\n+}\n+\n+void nthw_gmf_set_ifg_auto_adjust_enable(nthw_gmf_t *p, bool enable)\n+{\n+\tfield_set_val_flush32(p->mp_ctrl_ifg_auto_adjust_enable, enable);\n+}\n+\n+int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val)\n+{\n+\tif (n_speed_val <=\n+\t\t\t(1ULL << (field_get_bit_width(p->mp_speed_ifg_speed) - 1))) {\n+\t\tfield_set_val(p->mp_speed_ifg_speed, (uint32_t *)&n_speed_val,\n+\t\t\t     (field_get_bit_width(p->mp_speed_ifg_speed) <= 32 ? 1 :\n+\t\t\t      2));\n+\t\tfield_flush_register(p->mp_speed_ifg_speed);\n+\t\treturn 0;\n+\t}\n+\treturn -1;\n+}\n+\n+int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p)\n+{\n+\tconst int n_bit_width = field_get_bit_width(p->mp_speed_ifg_speed);\n+\n+\tassert(n_bit_width >=\n+\t       22); /* Sanity check: GMF ver 1.2 is bw 22 - GMF ver 1.3 is bw 64 */\n+\treturn n_bit_width;\n+}\n+\n+int nthw_gmf_set_ifg_speed_bits(nthw_gmf_t *p, const uint64_t n_rate_limit_bits,\n+\t\t\t    const uint64_t n_link_speed)\n+{\n+\tconst int n_bit_width = (nthw_gmf_get_ifg_speed_bit_width(p) / 2);\n+\tconst double f_adj_rate =\n+\t\t((double)((((double)n_rate_limit_bits) / (double)n_link_speed) *\n+\t\t\t  p->mn_param_gmf_ifg_speed_mul) /\n+\t\t p->mn_param_gmf_ifg_speed_div);\n+\tconst double f_speed = ((1UL / f_adj_rate) - 1) * exp2(n_bit_width);\n+\tuint64_t n_speed_val = (uint64_t)round(f_speed);\n+\n+\treturn nthw_gmf_set_ifg_speed_raw(p, n_speed_val);\n+}\n+\n+int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double f_rate_limit_percent)\n+{\n+\tuint64_t n_speed_val;\n+\n+\tif (f_rate_limit_percent == 0.0 || f_rate_limit_percent == 100.0) {\n+\t\tn_speed_val = 0;\n+\t} else if (f_rate_limit_percent <= 99) {\n+\t\tconst int n_bit_width = (nthw_gmf_get_ifg_speed_bit_width(p) / 2);\n+\t\tconst double f_adj_rate =\n+\t\t\t((double)(f_rate_limit_percent *\n+\t\t\t\t  (double)p->mn_param_gmf_ifg_speed_mul) /\n+\t\t\t p->mn_param_gmf_ifg_speed_div / 100);\n+\t\tconst double f_speed = ((1UL / f_adj_rate) - 1) * exp2(n_bit_width);\n+\n+\t\tn_speed_val = (uint64_t)f_speed;\n+\t} else {\n+\t\treturn -1;\n+\t}\n+\n+\treturn nthw_gmf_set_ifg_speed_raw(p, n_speed_val);\n+}\n+\n+void nthw_gmf_set_delta(nthw_gmf_t *p, uint64_t delta)\n+{\n+\tfield_set_val(p->mp_ifg_clock_delta_delta, (uint32_t *)&delta, 2);\n+\tfield_flush_register(p->mp_ifg_clock_delta_delta);\n+}\n+\n+void nthw_gmf_set_delta_adjust(nthw_gmf_t *p, uint64_t delta_adjust)\n+{\n+\tif (p->mp_ifg_clock_delta_adjust) {\n+\t\tfield_set_val(p->mp_ifg_clock_delta_adjust_delta,\n+\t\t\t     (uint32_t *)&delta_adjust, 2);\n+\t\tfield_flush_register(p->mp_ifg_clock_delta_adjust_delta);\n+\t}\n+}\n+\n+void nthw_gmf_set_slack(nthw_gmf_t *p, uint64_t slack)\n+{\n+\tfield_set_val(p->mp_ifg_max_adjust_slack_slack, (uint32_t *)&slack, 2);\n+\tfield_flush_register(p->mp_ifg_max_adjust_slack_slack);\n+}\n+\n+void nthw_gmf_set_compensation(nthw_gmf_t *p, uint32_t compensation)\n+{\n+\tfield_set_val_flush32(p->mp_debug_lane_marker_compensation, compensation);\n+}\n+\n+uint32_t nthw_gmf_get_status_sticky(nthw_gmf_t *p)\n+{\n+\tuint32_t status = 0;\n+\n+\tregister_update(p->mp_stat_sticky);\n+\n+\tif (field_get_val32(p->mp_stat_sticky_data_underflowed))\n+\t\tstatus |= GMF_STATUS_MASK_DATA_UNDERFLOWED;\n+\tif (field_get_val32(p->mp_stat_sticky_ifg_adjusted))\n+\t\tstatus |= GMF_STATUS_MASK_IFG_ADJUSTED;\n+\n+\treturn status;\n+}\n+\n+void nthw_gmf_set_status_sticky(nthw_gmf_t *p, uint32_t status)\n+{\n+\tif (status & GMF_STATUS_MASK_DATA_UNDERFLOWED)\n+\t\tfield_set_flush(p->mp_stat_sticky_data_underflowed);\n+\tif (status & GMF_STATUS_MASK_IFG_ADJUSTED)\n+\t\tfield_set_flush(p->mp_stat_sticky_ifg_adjusted);\n+}\n+\n+uint64_t nthw_gmf_get_stat_next_pkt_ns(nthw_gmf_t *p)\n+{\n+\tuint64_t value = ULONG_MAX;\n+\n+\tif (p->mp_stat_next_pkt) {\n+\t\tregister_update(p->mp_stat_next_pkt);\n+\t\tfield_get_val(p->mp_stat_next_pkt_ns, (uint32_t *)&value, 2);\n+\t}\n+\treturn value;\n+}\n+\n+uint64_t nthw_gmf_get_stat_max_pk_delayedt_ns(nthw_gmf_t *p)\n+{\n+\tuint64_t value = ULONG_MAX;\n+\n+\tif (p->mp_stat_max_delayed_pkt) {\n+\t\tregister_update(p->mp_stat_max_delayed_pkt);\n+\t\tfield_get_val(p->mp_stat_max_delayed_pkt_ns, (uint32_t *)&value, 2);\n+\t}\n+\treturn value;\n+}\n+\n+void nthw_gmf_administrative_block(nthw_gmf_t *p)\n+{\n+\tnthw_gmf_set_enable(p, false);\n+\tp->m_administrative_block = true;\n+}\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.h b/drivers/net/ntnic/nthw/core/nthw_gmf.h\nnew file mode 100644\nindex 0000000000..aec1342be7\n--- /dev/null\n+++ b/drivers/net/ntnic/nthw/core/nthw_gmf.h\n@@ -0,0 +1,93 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef __NTHW_GMF_H__\n+#define __NTHW_GMF_H__\n+\n+enum gmf_status_mask {\n+\tGMF_STATUS_MASK_DATA_UNDERFLOWED = 1,\n+\tGMF_STATUS_MASK_IFG_ADJUSTED\n+};\n+\n+struct nthw_gmf {\n+\tnt_fpga_t *mp_fpga;\n+\tnt_module_t *mp_mod_gmf;\n+\tint mn_instance;\n+\t/*  */\n+\n+\tnt_register_t *mp_ctrl;\n+\tnt_field_t *mp_ctrl_enable;\n+\tnt_field_t *mp_ctrl_ifg_enable;\n+\tnt_field_t *mp_ctrl_ifg_tx_now_always;\n+\tnt_field_t *mp_ctrl_ifg_tx_on_ts_always;\n+\tnt_field_t *mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock;\n+\tnt_field_t *mp_ctrl_ifg_auto_adjust_enable;\n+\n+\tnt_register_t *mp_speed;\n+\tnt_field_t *mp_speed_ifg_speed;\n+\n+\tnt_register_t *mp_ifg_clock_delta;\n+\tnt_field_t *mp_ifg_clock_delta_delta;\n+\n+\tnt_register_t *mp_ifg_clock_delta_adjust;\n+\tnt_field_t *mp_ifg_clock_delta_adjust_delta;\n+\n+\tnt_register_t *mp_ifg_max_adjust_slack;\n+\tnt_field_t *mp_ifg_max_adjust_slack_slack;\n+\n+\tnt_register_t *mp_debug_lane_marker;\n+\tnt_field_t *mp_debug_lane_marker_compensation;\n+\n+\tnt_register_t *mp_stat_sticky;\n+\tnt_field_t *mp_stat_sticky_data_underflowed;\n+\tnt_field_t *mp_stat_sticky_ifg_adjusted;\n+\n+\tnt_register_t *mp_stat_next_pkt;\n+\tnt_field_t *mp_stat_next_pkt_ns;\n+\n+\tnt_register_t *mp_stat_max_delayed_pkt;\n+\tnt_field_t *mp_stat_max_delayed_pkt_ns;\n+\n+\tint mn_param_gmf_ifg_speed_mul;\n+\tint mn_param_gmf_ifg_speed_div;\n+\n+\tbool m_administrative_block; /* Used to enforce license expiry */\n+};\n+\n+typedef struct nthw_gmf nthw_gmf_t;\n+typedef struct nthw_gmf nthw_gmf;\n+\n+nthw_gmf_t *nthw_gmf_new(void);\n+void nthw_gmf_delete(nthw_gmf_t *p);\n+int nthw_gmf_init(nthw_gmf_t *p, nt_fpga_t *p_fpga, int n_instance);\n+\n+void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable);\n+void nthw_gmf_set_ifg_enable(nthw_gmf_t *p, bool enable);\n+\n+void nthw_gmf_set_tx_now_always_enable(nthw_gmf_t *p, bool enable);\n+void nthw_gmf_set_tx_on_ts_always_enable(nthw_gmf_t *p, bool enable);\n+void nthw_gmf_set_tx_on_ts_adjust_on_set_clock(nthw_gmf_t *p, bool enable);\n+void nthw_gmf_set_ifg_auto_adjust_enable(nthw_gmf_t *p, bool enable);\n+\n+int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p);\n+\n+int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val);\n+int nthw_gmf_set_ifg_speed_bits(nthw_gmf_t *p, const uint64_t n_rate_limit_bits,\n+\t\t\t    const uint64_t n_link_speed);\n+int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double f_rate_limit_percent);\n+\n+void nthw_gmf_set_delta(nthw_gmf_t *p, uint64_t delta);\n+void nthw_gmf_set_delta_adjust(nthw_gmf_t *p, uint64_t delta_adjust);\n+void nthw_gmf_set_slack(nthw_gmf_t *p, uint64_t slack);\n+void nthw_gmf_set_compensation(nthw_gmf_t *p, uint32_t compensation);\n+\n+uint32_t nthw_gmf_get_status_sticky(nthw_gmf_t *p);\n+void nthw_gmf_set_status_sticky(nthw_gmf_t *p, uint32_t status);\n+\n+uint64_t nthw_gmf_get_stat_next_pkt_ns(nthw_gmf_t *p);\n+uint64_t nthw_gmf_get_stat_max_pk_delayedt_ns(nthw_gmf_t *p);\n+\n+void nthw_gmf_administrative_block(nthw_gmf_t *p); /* Used to enforce license expiry blocking */\n+\n+#endif /* __NTHW_GMF_H__ */\ndiff --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\nnew file mode 100644\nindex 0000000000..f063a1048a\n--- /dev/null\n+++ b/drivers/net/ntnic/nthw/core/nthw_nt200a02_u23_si5340_v5.h\n@@ -0,0 +1,344 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef SI5340_REVD_REG_CONFIG_HEADER\n+#define SI5340_REVD_REG_CONFIG_HEADER\n+\n+#define SI5340_REVD_REG_CONFIG_NUM_REGS 326\n+\n+typedef struct {\n+\tunsigned int address; /* 16-bit register address */\n+\tunsigned char value; /* 8-bit register data */\n+} si5340_revd_register_t;\n+\n+si5340_revd_register_t const si5340_revd_registers[SI5340_REVD_REG_CONFIG_NUM_REGS] = {\n+\t{ 0x0B24, 0xC0 },\n+\t{ 0x0B25, 0x00 },\n+\t{ 0x0502, 0x01 },\n+\t{ 0x0505, 0x03 },\n+\t{ 0x0957, 0x17 },\n+\t{ 0x0B4E, 0x1A },\n+\t{ 0x0006, 0x00 },\n+\t{ 0x0007, 0x00 },\n+\t{ 0x0008, 0x00 },\n+\t{ 0x000B, 0x74 },\n+\t{ 0x0017, 0xF0 },\n+\t{ 0x0018, 0xFF },\n+\t{ 0x0021, 0x0F },\n+\t{ 0x0022, 0x00 },\n+\t{ 0x002B, 0x0A },\n+\t{ 0x002C, 0x20 },\n+\t{ 0x002D, 0x00 },\n+\t{ 0x002E, 0x00 },\n+\t{ 0x002F, 0x00 },\n+\t{ 0x0030, 0x00 },\n+\t{ 0x0031, 0x00 },\n+\t{ 0x0032, 0x00 },\n+\t{ 0x0033, 0x00 },\n+\t{ 0x0034, 0x00 },\n+\t{ 0x0035, 0x00 },\n+\t{ 0x0036, 0x00 },\n+\t{ 0x0037, 0x00 },\n+\t{ 0x0038, 0x00 },\n+\t{ 0x0039, 0x00 },\n+\t{ 0x003A, 0x00 },\n+\t{ 0x003B, 0x00 },\n+\t{ 0x003C, 0x00 },\n+\t{ 0x003D, 0x00 },\n+\t{ 0x0041, 0x00 },\n+\t{ 0x0042, 0x00 },\n+\t{ 0x0043, 0x00 },\n+\t{ 0x0044, 0x00 },\n+\t{ 0x009E, 0x00 },\n+\t{ 0x0102, 0x01 },\n+\t{ 0x0112, 0x02 },\n+\t{ 0x0113, 0x09 },\n+\t{ 0x0114, 0x3E },\n+\t{ 0x0115, 0x19 },\n+\t{ 0x0117, 0x06 },\n+\t{ 0x0118, 0x09 },\n+\t{ 0x0119, 0x3E },\n+\t{ 0x011A, 0x18 },\n+\t{ 0x0126, 0x06 },\n+\t{ 0x0127, 0x09 },\n+\t{ 0x0128, 0x3E },\n+\t{ 0x0129, 0x18 },\n+\t{ 0x012B, 0x06 },\n+\t{ 0x012C, 0x09 },\n+\t{ 0x012D, 0x3E },\n+\t{ 0x012E, 0x1A },\n+\t{ 0x013F, 0x00 },\n+\t{ 0x0140, 0x00 },\n+\t{ 0x0141, 0x40 },\n+\t{ 0x0206, 0x00 },\n+\t{ 0x0208, 0x00 },\n+\t{ 0x0209, 0x00 },\n+\t{ 0x020A, 0x00 },\n+\t{ 0x020B, 0x00 },\n+\t{ 0x020C, 0x00 },\n+\t{ 0x020D, 0x00 },\n+\t{ 0x020E, 0x00 },\n+\t{ 0x020F, 0x00 },\n+\t{ 0x0210, 0x00 },\n+\t{ 0x0211, 0x00 },\n+\t{ 0x0212, 0x00 },\n+\t{ 0x0213, 0x00 },\n+\t{ 0x0214, 0x00 },\n+\t{ 0x0215, 0x00 },\n+\t{ 0x0216, 0x00 },\n+\t{ 0x0217, 0x00 },\n+\t{ 0x0218, 0x00 },\n+\t{ 0x0219, 0x00 },\n+\t{ 0x021A, 0x00 },\n+\t{ 0x021B, 0x00 },\n+\t{ 0x021C, 0x00 },\n+\t{ 0x021D, 0x00 },\n+\t{ 0x021E, 0x00 },\n+\t{ 0x021F, 0x00 },\n+\t{ 0x0220, 0x00 },\n+\t{ 0x0221, 0x00 },\n+\t{ 0x0222, 0x00 },\n+\t{ 0x0223, 0x00 },\n+\t{ 0x0224, 0x00 },\n+\t{ 0x0225, 0x00 },\n+\t{ 0x0226, 0x00 },\n+\t{ 0x0227, 0x00 },\n+\t{ 0x0228, 0x00 },\n+\t{ 0x0229, 0x00 },\n+\t{ 0x022A, 0x00 },\n+\t{ 0x022B, 0x00 },\n+\t{ 0x022C, 0x00 },\n+\t{ 0x022D, 0x00 },\n+\t{ 0x022E, 0x00 },\n+\t{ 0x022F, 0x00 },\n+\t{ 0x0235, 0x00 },\n+\t{ 0x0236, 0x00 },\n+\t{ 0x0237, 0x00 },\n+\t{ 0x0238, 0xA6 },\n+\t{ 0x0239, 0x8B },\n+\t{ 0x023A, 0x00 },\n+\t{ 0x023B, 0x00 },\n+\t{ 0x023C, 0x00 },\n+\t{ 0x023D, 0x00 },\n+\t{ 0x023E, 0x80 },\n+\t{ 0x0250, 0x03 },\n+\t{ 0x0251, 0x00 },\n+\t{ 0x0252, 0x00 },\n+\t{ 0x0253, 0x00 },\n+\t{ 0x0254, 0x00 },\n+\t{ 0x0255, 0x00 },\n+\t{ 0x025C, 0x00 },\n+\t{ 0x025D, 0x00 },\n+\t{ 0x025E, 0x00 },\n+\t{ 0x025F, 0x00 },\n+\t{ 0x0260, 0x00 },\n+\t{ 0x0261, 0x00 },\n+\t{ 0x026B, 0x30 },\n+\t{ 0x026C, 0x35 },\n+\t{ 0x026D, 0x00 },\n+\t{ 0x026E, 0x00 },\n+\t{ 0x026F, 0x00 },\n+\t{ 0x0270, 0x00 },\n+\t{ 0x0271, 0x00 },\n+\t{ 0x0272, 0x00 },\n+\t{ 0x0302, 0x00 },\n+\t{ 0x0303, 0x00 },\n+\t{ 0x0304, 0x00 },\n+\t{ 0x0305, 0x00 },\n+\t{ 0x0306, 0x0D },\n+\t{ 0x0307, 0x00 },\n+\t{ 0x0308, 0x00 },\n+\t{ 0x0309, 0x00 },\n+\t{ 0x030A, 0x00 },\n+\t{ 0x030B, 0x80 },\n+\t{ 0x030C, 0x00 },\n+\t{ 0x030D, 0x00 },\n+\t{ 0x030E, 0x00 },\n+\t{ 0x030F, 0x00 },\n+\t{ 0x0310, 0x61 },\n+\t{ 0x0311, 0x08 },\n+\t{ 0x0312, 0x00 },\n+\t{ 0x0313, 0x00 },\n+\t{ 0x0314, 0x00 },\n+\t{ 0x0315, 0x00 },\n+\t{ 0x0316, 0x80 },\n+\t{ 0x0317, 0x00 },\n+\t{ 0x0318, 0x00 },\n+\t{ 0x0319, 0x00 },\n+\t{ 0x031A, 0x00 },\n+\t{ 0x031B, 0xD0 },\n+\t{ 0x031C, 0x1A },\n+\t{ 0x031D, 0x00 },\n+\t{ 0x031E, 0x00 },\n+\t{ 0x031F, 0x00 },\n+\t{ 0x0320, 0x00 },\n+\t{ 0x0321, 0xA0 },\n+\t{ 0x0322, 0x00 },\n+\t{ 0x0323, 0x00 },\n+\t{ 0x0324, 0x00 },\n+\t{ 0x0325, 0x00 },\n+\t{ 0x0326, 0x00 },\n+\t{ 0x0327, 0x00 },\n+\t{ 0x0328, 0x00 },\n+\t{ 0x0329, 0x00 },\n+\t{ 0x032A, 0x00 },\n+\t{ 0x032B, 0x00 },\n+\t{ 0x032C, 0x00 },\n+\t{ 0x032D, 0x00 },\n+\t{ 0x0338, 0x00 },\n+\t{ 0x0339, 0x1F },\n+\t{ 0x033B, 0x00 },\n+\t{ 0x033C, 0x00 },\n+\t{ 0x033D, 0x00 },\n+\t{ 0x033E, 0x00 },\n+\t{ 0x033F, 0x00 },\n+\t{ 0x0340, 0x00 },\n+\t{ 0x0341, 0x00 },\n+\t{ 0x0342, 0x00 },\n+\t{ 0x0343, 0x00 },\n+\t{ 0x0344, 0x00 },\n+\t{ 0x0345, 0x00 },\n+\t{ 0x0346, 0x00 },\n+\t{ 0x0347, 0x00 },\n+\t{ 0x0348, 0x00 },\n+\t{ 0x0349, 0x00 },\n+\t{ 0x034A, 0x00 },\n+\t{ 0x034B, 0x00 },\n+\t{ 0x034C, 0x00 },\n+\t{ 0x034D, 0x00 },\n+\t{ 0x034E, 0x00 },\n+\t{ 0x034F, 0x00 },\n+\t{ 0x0350, 0x00 },\n+\t{ 0x0351, 0x00 },\n+\t{ 0x0352, 0x00 },\n+\t{ 0x0359, 0x00 },\n+\t{ 0x035A, 0x00 },\n+\t{ 0x035B, 0x00 },\n+\t{ 0x035C, 0x00 },\n+\t{ 0x035D, 0x00 },\n+\t{ 0x035E, 0x00 },\n+\t{ 0x035F, 0x00 },\n+\t{ 0x0360, 0x00 },\n+\t{ 0x0802, 0x00 },\n+\t{ 0x0803, 0x00 },\n+\t{ 0x0804, 0x00 },\n+\t{ 0x0805, 0x00 },\n+\t{ 0x0806, 0x00 },\n+\t{ 0x0807, 0x00 },\n+\t{ 0x0808, 0x00 },\n+\t{ 0x0809, 0x00 },\n+\t{ 0x080A, 0x00 },\n+\t{ 0x080B, 0x00 },\n+\t{ 0x080C, 0x00 },\n+\t{ 0x080D, 0x00 },\n+\t{ 0x080E, 0x00 },\n+\t{ 0x080F, 0x00 },\n+\t{ 0x0810, 0x00 },\n+\t{ 0x0811, 0x00 },\n+\t{ 0x0812, 0x00 },\n+\t{ 0x0813, 0x00 },\n+\t{ 0x0814, 0x00 },\n+\t{ 0x0815, 0x00 },\n+\t{ 0x0816, 0x00 },\n+\t{ 0x0817, 0x00 },\n+\t{ 0x0818, 0x00 },\n+\t{ 0x0819, 0x00 },\n+\t{ 0x081A, 0x00 },\n+\t{ 0x081B, 0x00 },\n+\t{ 0x081C, 0x00 },\n+\t{ 0x081D, 0x00 },\n+\t{ 0x081E, 0x00 },\n+\t{ 0x081F, 0x00 },\n+\t{ 0x0820, 0x00 },\n+\t{ 0x0821, 0x00 },\n+\t{ 0x0822, 0x00 },\n+\t{ 0x0823, 0x00 },\n+\t{ 0x0824, 0x00 },\n+\t{ 0x0825, 0x00 },\n+\t{ 0x0826, 0x00 },\n+\t{ 0x0827, 0x00 },\n+\t{ 0x0828, 0x00 },\n+\t{ 0x0829, 0x00 },\n+\t{ 0x082A, 0x00 },\n+\t{ 0x082B, 0x00 },\n+\t{ 0x082C, 0x00 },\n+\t{ 0x082D, 0x00 },\n+\t{ 0x082E, 0x00 },\n+\t{ 0x082F, 0x00 },\n+\t{ 0x0830, 0x00 },\n+\t{ 0x0831, 0x00 },\n+\t{ 0x0832, 0x00 },\n+\t{ 0x0833, 0x00 },\n+\t{ 0x0834, 0x00 },\n+\t{ 0x0835, 0x00 },\n+\t{ 0x0836, 0x00 },\n+\t{ 0x0837, 0x00 },\n+\t{ 0x0838, 0x00 },\n+\t{ 0x0839, 0x00 },\n+\t{ 0x083A, 0x00 },\n+\t{ 0x083B, 0x00 },\n+\t{ 0x083C, 0x00 },\n+\t{ 0x083D, 0x00 },\n+\t{ 0x083E, 0x00 },\n+\t{ 0x083F, 0x00 },\n+\t{ 0x0840, 0x00 },\n+\t{ 0x0841, 0x00 },\n+\t{ 0x0842, 0x00 },\n+\t{ 0x0843, 0x00 },\n+\t{ 0x0844, 0x00 },\n+\t{ 0x0845, 0x00 },\n+\t{ 0x0846, 0x00 },\n+\t{ 0x0847, 0x00 },\n+\t{ 0x0848, 0x00 },\n+\t{ 0x0849, 0x00 },\n+\t{ 0x084A, 0x00 },\n+\t{ 0x084B, 0x00 },\n+\t{ 0x084C, 0x00 },\n+\t{ 0x084D, 0x00 },\n+\t{ 0x084E, 0x00 },\n+\t{ 0x084F, 0x00 },\n+\t{ 0x0850, 0x00 },\n+\t{ 0x0851, 0x00 },\n+\t{ 0x0852, 0x00 },\n+\t{ 0x0853, 0x00 },\n+\t{ 0x0854, 0x00 },\n+\t{ 0x0855, 0x00 },\n+\t{ 0x0856, 0x00 },\n+\t{ 0x0857, 0x00 },\n+\t{ 0x0858, 0x00 },\n+\t{ 0x0859, 0x00 },\n+\t{ 0x085A, 0x00 },\n+\t{ 0x085B, 0x00 },\n+\t{ 0x085C, 0x00 },\n+\t{ 0x085D, 0x00 },\n+\t{ 0x085E, 0x00 },\n+\t{ 0x085F, 0x00 },\n+\t{ 0x0860, 0x00 },\n+\t{ 0x0861, 0x00 },\n+\t{ 0x090E, 0x02 },\n+\t{ 0x091C, 0x04 },\n+\t{ 0x0943, 0x00 },\n+\t{ 0x0949, 0x00 },\n+\t{ 0x094A, 0x00 },\n+\t{ 0x094E, 0x49 },\n+\t{ 0x094F, 0x02 },\n+\t{ 0x095E, 0x00 },\n+\t{ 0x0A02, 0x00 },\n+\t{ 0x0A03, 0x07 },\n+\t{ 0x0A04, 0x01 },\n+\t{ 0x0A05, 0x07 },\n+\t{ 0x0A14, 0x00 },\n+\t{ 0x0A1A, 0x00 },\n+\t{ 0x0A20, 0x00 },\n+\t{ 0x0A26, 0x00 },\n+\t{ 0x0B44, 0x0F },\n+\t{ 0x0B4A, 0x08 },\n+\t{ 0x0B57, 0x0E },\n+\t{ 0x0B58, 0x01 },\n+\t{ 0x001C, 0x01 },\n+\t{ 0x0B24, 0xC3 },\n+\t{ 0x0B25, 0x02 },\n+};\n+\n+#endif /* SI5340_REVD_REG_CONFIG_HEADER */\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_rmc.c b/drivers/net/ntnic/nthw/core/nthw_rmc.c\nnew file mode 100644\nindex 0000000000..c4c6779ce0\n--- /dev/null\n+++ b/drivers/net/ntnic/nthw/core/nthw_rmc.c\n@@ -0,0 +1,156 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntlog.h\"\n+\n+#include \"nthw_drv.h\"\n+#include \"nthw_register.h\"\n+\n+#include \"nthw_rmc.h\"\n+\n+nthw_rmc_t *nthw_rmc_new(void)\n+{\n+\tnthw_rmc_t *p = malloc(sizeof(nthw_rmc_t));\n+\n+\tif (p)\n+\t\tmemset(p, 0, sizeof(nthw_rmc_t));\n+\treturn p;\n+}\n+\n+void nthw_rmc_delete(nthw_rmc_t *p)\n+{\n+\tif (p) {\n+\t\tmemset(p, 0, sizeof(nthw_rmc_t));\n+\t\tfree(p);\n+\t}\n+}\n+\n+int nthw_rmc_init(nthw_rmc_t *p, nt_fpga_t *p_fpga, int n_instance)\n+{\n+\tconst char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;\n+\tnt_module_t *p_mod = fpga_query_module(p_fpga, MOD_RMC, n_instance);\n+\n+\tif (p == NULL)\n+\t\treturn p_mod == NULL ? -1 : 0;\n+\n+\tif (p_mod == NULL) {\n+\t\tNT_LOG(ERR, NTHW, \"%s: RMC %d: no such instance\\n\",\n+\t\t       p_adapter_id_str, n_instance);\n+\t\treturn -1;\n+\t}\n+\n+\tp->mp_fpga = p_fpga;\n+\tp->mn_instance = n_instance;\n+\tp->mp_mod_rmc = p_mod;\n+\n+\t/* Params */\n+\tp->mb_is_vswitch = p_fpga->p_fpga_info->profile == FPGA_INFO_PROFILE_VSWITCH;\n+\tp->mn_ports = fpga_get_product_param(p_fpga, NT_RX_PORTS,\n+\t\t\t\t\t     fpga_get_product_param(p_fpga, NT_PORTS, 0));\n+\tp->mn_nims = fpga_get_product_param(p_fpga, NT_NIMS, 0);\n+\tp->mb_administrative_block = false;\n+\n+\tNT_LOG(DBG, NTHW, \"%s: RMC %d: vswitch=%d\\n\", p_adapter_id_str,\n+\t       p->mn_instance, p->mb_is_vswitch);\n+\n+\tp->mp_reg_ctrl = module_get_register(p->mp_mod_rmc, RMC_CTRL);\n+\n+\tp->mp_fld_ctrl_block_stat_drop =\n+\t\tregister_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_STATT);\n+\tp->mp_fld_ctrl_block_keep_alive =\n+\t\tregister_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_KEEPA);\n+\tp->mp_fld_ctrl_block_mac_port =\n+\t\tregister_get_field(p->mp_reg_ctrl, RMC_CTRL_BLOCK_MAC_PORT);\n+\n+\tp->mp_reg_status = module_query_register(p->mp_mod_rmc, RMC_STATUS);\n+\tif (p->mp_reg_status) {\n+\t\tp->mp_fld_sf_ram_of =\n+\t\t\tregister_get_field(p->mp_reg_status, RMC_STATUS_SF_RAM_OF);\n+\t\tp->mp_fld_descr_fifo_of =\n+\t\t\tregister_get_field(p->mp_reg_status, RMC_STATUS_DESCR_FIFO_OF);\n+\t}\n+\n+\tp->mp_reg_dbg = module_query_register(p->mp_mod_rmc, RMC_DBG);\n+\tif (p->mp_reg_dbg) {\n+\t\tp->mp_fld_dbg_merge =\n+\t\t\tregister_get_field(p->mp_reg_dbg, RMC_DBG_MERGE);\n+\t}\n+\n+\tp->mp_reg_mac_if = module_query_register(p->mp_mod_rmc, RMC_MAC_IF);\n+\tif (p->mp_reg_mac_if) {\n+\t\tp->mp_fld_mac_if_err =\n+\t\t\tregister_get_field(p->mp_reg_mac_if, RMC_MAC_IF_ERR);\n+\t}\n+\treturn 0;\n+}\n+\n+uint32_t nthw_rmc_get_mac_block(nthw_rmc_t *p)\n+{\n+\treturn field_get_updated(p->mp_fld_ctrl_block_mac_port);\n+}\n+\n+uint32_t nthw_rmc_get_status_sf_ram_of(nthw_rmc_t *p)\n+{\n+\treturn (p->mp_reg_status) ? field_get_updated(p->mp_fld_sf_ram_of) :\n+\t       0xffffffff;\n+}\n+\n+uint32_t nthw_rmc_get_status_descr_fifo_of(nthw_rmc_t *p)\n+{\n+\treturn (p->mp_reg_status) ? field_get_updated(p->mp_fld_descr_fifo_of) :\n+\t       0xffffffff;\n+}\n+\n+uint32_t nthw_rmc_get_dbg_merge(nthw_rmc_t *p)\n+{\n+\treturn (p->mp_reg_dbg) ? field_get_updated(p->mp_fld_dbg_merge) : 0xffffffff;\n+}\n+\n+uint32_t nthw_rmc_get_mac_if_err(nthw_rmc_t *p)\n+{\n+\treturn (p->mp_reg_mac_if) ? field_get_updated(p->mp_fld_mac_if_err) :\n+\t       0xffffffff;\n+}\n+\n+void nthw_rmc_set_mac_block(nthw_rmc_t *p, uint32_t mask)\n+{\n+\tfield_set_val_flush32(p->mp_fld_ctrl_block_mac_port, mask);\n+}\n+\n+void nthw_rmc_block(nthw_rmc_t *p)\n+{\n+\t/* BLOCK_STATT(0)=1 BLOCK_KEEPA(1)=1 BLOCK_MAC_PORT(8:11)=~0 */\n+\tif (!p->mb_administrative_block) {\n+\t\tfield_set_flush(p->mp_fld_ctrl_block_stat_drop);\n+\t\tfield_set_flush(p->mp_fld_ctrl_block_keep_alive);\n+\t\tfield_set_flush(p->mp_fld_ctrl_block_mac_port);\n+\t}\n+}\n+\n+void nthw_rmc_unblock(nthw_rmc_t *p, bool b_is_slave)\n+{\n+\tuint32_t n_block_mask = ~0U << (b_is_slave ? p->mn_nims : p->mn_ports);\n+\n+\tif (p->mb_is_vswitch) {\n+\t\t/*\n+\t\t * VSWITCH: NFV: block bits: phy_nim_ports(2) + rtd_ports(4) +\n+\t\t * roa_recirculate_port(1)\n+\t\t */\n+\t\tn_block_mask = 1 << (2 + 4); /* block only ROA recirculate */\n+\t}\n+\n+\t/* BLOCK_STATT(0)=0 BLOCK_KEEPA(1)=0 BLOCK_MAC_PORT(8:11)=0 */\n+\tif (!p->mb_administrative_block) {\n+\t\tfield_clr_flush(p->mp_fld_ctrl_block_stat_drop);\n+\t\tfield_clr_flush(p->mp_fld_ctrl_block_keep_alive);\n+\t\tfield_set_val_flush32(p->mp_fld_ctrl_block_mac_port, n_block_mask);\n+\t}\n+}\n+\n+void nthw_rmc_administrative_block(nthw_rmc_t *p)\n+{\n+\t/* block all MAC ports */\n+\tfield_set_flush(p->mp_fld_ctrl_block_mac_port);\n+\tp->mb_administrative_block = true;\n+}\ndiff --git a/drivers/net/ntnic/nthw/core/nthw_rmc.h b/drivers/net/ntnic/nthw/core/nthw_rmc.h\nnew file mode 100644\nindex 0000000000..b40f0a0994\n--- /dev/null\n+++ b/drivers/net/ntnic/nthw/core/nthw_rmc.h\n@@ -0,0 +1,57 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef NTHW_RMC_H_\n+#define NTHW_RMC_H_\n+\n+struct nthw_rmc {\n+\tnt_fpga_t *mp_fpga;\n+\tnt_module_t *mp_mod_rmc;\n+\tint mn_instance;\n+\n+\tint mn_ports;\n+\tint mn_nims;\n+\tbool mb_is_vswitch;\n+\n+\tbool mb_administrative_block;\n+\n+\t/* RMC CTRL register */\n+\tnt_register_t *mp_reg_ctrl;\n+\tnt_field_t *mp_fld_ctrl_block_stat_drop;\n+\tnt_field_t *mp_fld_ctrl_block_keep_alive;\n+\tnt_field_t *mp_fld_ctrl_block_mac_port;\n+\n+\t/* RMC Status register */\n+\tnt_register_t *mp_reg_status;\n+\tnt_field_t *mp_fld_sf_ram_of;\n+\tnt_field_t *mp_fld_descr_fifo_of;\n+\n+\t/* RMC DBG register */\n+\tnt_register_t *mp_reg_dbg;\n+\tnt_field_t *mp_fld_dbg_merge;\n+\n+\t/* RMC MAC_IF register */\n+\tnt_register_t *mp_reg_mac_if;\n+\tnt_field_t *mp_fld_mac_if_err;\n+};\n+\n+typedef struct nthw_rmc nthw_rmc_t;\n+typedef struct nthw_rmc nthw_rmc;\n+\n+nthw_rmc_t *nthw_rmc_new(void);\n+void nthw_rmc_delete(nthw_rmc_t *p);\n+int nthw_rmc_init(nthw_rmc_t *p, nt_fpga_t *p_fpga, int n_instance);\n+\n+uint32_t nthw_rmc_get_mac_block(nthw_rmc_t *p);\n+void nthw_rmc_set_mac_block(nthw_rmc_t *p, uint32_t mask);\n+void nthw_rmc_block(nthw_rmc_t *p);\n+void nthw_rmc_unblock(nthw_rmc_t *p, bool b_is_slave);\n+void nthw_rmc_administrative_block(nthw_rmc_t *p);\n+\n+uint32_t nthw_rmc_get_status_sf_ram_of(nthw_rmc_t *p);\n+uint32_t nthw_rmc_get_status_descr_fifo_of(nthw_rmc_t *p);\n+uint32_t nthw_rmc_get_dbg_merge(nthw_rmc_t *p);\n+uint32_t nthw_rmc_get_mac_if_err(nthw_rmc_t *p);\n+\n+#endif /* NTHW_RMC_H_ */\ndiff --git a/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c\nnew file mode 100644\nindex 0000000000..bf120ccb39\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.c\n@@ -0,0 +1,104 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"avr_sensors.h\"\n+#include \"ntlog.h\"\n+\n+#define MAX_ADAPTERS 2\n+\n+uint8_t s_fpga_indexes[MAX_ADAPTERS] = { 0 }; /* _NTSD_MAX_NUM_ADAPTERS_ */\n+static uint8_t get_fpga_idx(unsigned int adapter_no);\n+\n+/*\n+ * This function setups monitoring of AVR sensors\n+ */\n+static uint8_t _avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no,\n+\t\t\t\tconst char *p_name,\n+\t\t\t\tenum sensor_mon_device avr_dev,\n+\t\t\t\tuint8_t avr_dev_reg, enum sensor_mon_endian end,\n+\t\t\t\tenum sensor_mon_sign si, uint16_t mask)\n+{\n+\tuint8_t fpga_idx = get_fpga_idx(m_adapter_no);\n+\tstruct sensor_mon_setup16 avr_sensor_setup;\n+\n+\t/* Setup monitoring in AVR placing results in FPGA */\n+\tavr_sensor_setup.setup_cnt = 1;\n+\tavr_sensor_setup.setup_data[0].fpga_idx = fpga_idx;\n+\tavr_sensor_setup.setup_data[0].device = avr_dev;\n+\tavr_sensor_setup.setup_data[0].device_register = avr_dev_reg;\n+\tavr_sensor_setup.setup_data[0].format = (uint16_t)(end | si << 2);\n+\n+\tavr_sensor_setup.setup_data[0].mask = mask;\n+\tavr_sensor_setup.setup_data[0].pos =\n+\t\t0; /* So far for all sensors in table */\n+\n+\t/*\n+\t * At first it is the task of ntservice to test limit_low and limit_high on all\n+\t * board sensors. Later the test is going to be carried out by the AVR\n+\t */\n+\tif (si == SENSOR_MON_SIGNED) {\n+\t\tavr_sensor_setup.setup_data[0].int16.limit_low =\n+\t\t\tSENSOR_MON_INT16_NAN;\n+\t\tavr_sensor_setup.setup_data[0].int16.limit_high =\n+\t\t\tSENSOR_MON_INT16_NAN;\n+\t} else {\n+\t\tavr_sensor_setup.setup_data[0].uint16.limit_low =\n+\t\t\tSENSOR_MON_UINT16_NAN;\n+\t\tavr_sensor_setup.setup_data[0].uint16.limit_high =\n+\t\t\tSENSOR_MON_UINT16_NAN;\n+\t}\n+\n+\tint result = nt_avr_sensor_mon_setup(&avr_sensor_setup, s_spi);\n+\n+\tif (result)\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor initialization error\\n\", p_name);\n+\n+\treturn fpga_idx;\n+}\n+\n+static void avr_read(struct nt_sensor_group *sg, nthw_spis_t *t_spi)\n+{\n+\tuint32_t p_sensor_result;\n+\n+\tif (sg == NULL || sg->sensor == NULL)\n+\t\treturn;\n+\n+\tsensor_read(t_spi, sg->sensor->fpga_idx, &p_sensor_result);\n+\tupdate_sensor_value(sg->sensor, sg->conv_func(p_sensor_result));\n+}\n+\n+struct nt_sensor_group *\n+avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no, const char *p_name,\n+\t\tenum nt_sensor_source_e ssrc, enum nt_sensor_type_e type,\n+\t\tunsigned int index, enum sensor_mon_device avr_dev,\n+\t\tuint8_t avr_dev_reg, enum sensor_mon_endian end,\n+\t\tenum sensor_mon_sign si, int (*conv_func)(uint32_t),\n+\t\tuint16_t mask)\n+{\n+\tstruct nt_sensor_group *sg = malloc(sizeof(struct nt_sensor_group));\n+\n+\tif (sg == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor group is NULL\", __func__);\n+\t\treturn NULL;\n+\t}\n+\tinit_sensor_group(sg);\n+\tsg->sensor = allocate_sensor(m_adapter_no, p_name, ssrc, type, index,\n+\t\t\t\t     NT_SENSOR_DISABLE_ALARM, si);\n+\tsg->sensor->fpga_idx = _avr_sensor_init(s_spi, m_adapter_no, p_name, avr_dev,\n+\t\t\t\t\t       avr_dev_reg, end, si, mask);\n+\tsg->read = &avr_read;\n+\tsg->conv_func = conv_func;\n+\tsg->monitor = NULL;\n+\tsg->next = NULL;\n+\treturn sg;\n+}\n+\n+static uint8_t get_fpga_idx(unsigned int adapter_no)\n+{\n+\tuint8_t tmp = s_fpga_indexes[adapter_no];\n+\n+\ts_fpga_indexes[adapter_no] = (uint8_t)(tmp + 1);\n+\n+\treturn tmp;\n+}\ndiff --git a/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h\nnew file mode 100644\nindex 0000000000..b8c37a12cb\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/avr_sensors/avr_sensors.h\n@@ -0,0 +1,22 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _AVR_SENSORS_H\n+#define _AVR_SENSORS_H\n+\n+#include <stdint.h>\n+\n+#include \"sensors.h\"\n+#include \"avr_intf.h\"\n+#include \"ntavr.h\"\n+\n+struct nt_sensor_group *\n+avr_sensor_init(nthw_spi_v3_t *s_spi, uint8_t m_adapter_no, const char *p_name,\n+\t\tenum nt_sensor_source_e ssrc, enum nt_sensor_type_e type,\n+\t\tunsigned int index, enum sensor_mon_device avr_dev,\n+\t\tuint8_t avr_dev_reg, enum sensor_mon_endian end,\n+\t\tenum sensor_mon_sign si, int (*conv_func)(uint32_t),\n+\t\tuint16_t mask);\n+\n+#endif /* _AVR_SENSORS_H */\ndiff --git a/drivers/net/ntnic/sensors/board_sensors/board_sensors.c b/drivers/net/ntnic/sensors/board_sensors/board_sensors.c\nnew file mode 100644\nindex 0000000000..8e52379df8\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/board_sensors/board_sensors.c\n@@ -0,0 +1,48 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <stddef.h>\n+#include <math.h>\n+\n+#include \"tempmon.h\"\n+#include \"board_sensors.h\"\n+#include \"ntlog.h\"\n+\n+static void fpga_temperature_sensor_read(struct nt_sensor_group *sg,\n+\t\tnthw_spis_t *t_spi)\n+{\n+\tint temp = 0;\n+\t(void)t_spi;\n+\tif (sg == NULL || sg->sensor == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"failed to read FPGA temperature\\n\");\n+\t\treturn;\n+\t}\n+\tstruct nt_fpga_sensor_monitor *temp_monitor = sg->monitor;\n+\tuint32_t val = field_get_updated(temp_monitor->fields[0]);\n+\n+\ttemp = (val * 20159 - 44752896) / 16384;\n+\n+\tupdate_sensor_value(sg->sensor, temp);\n+}\n+\n+struct nt_sensor_group *fpga_temperature_sensor_init(uint8_t adapter_no,\n+\t\tunsigned int sensor_idx,\n+\t\tnt_fpga_t *p_fpga)\n+{\n+\tstruct nt_sensor_group *sg = malloc(sizeof(struct nt_sensor_group));\n+\n+\tif (sg == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor group is NULL\", __func__);\n+\t\treturn NULL;\n+\t}\n+\tinit_sensor_group(sg);\n+\tsg->monitor = tempmon_new();\n+\ttempmon_init(sg->monitor, p_fpga);\n+\tsg->sensor =\n+\t\tallocate_sensor(adapter_no, \"FPGA\", NT_SENSOR_SOURCE_ADAPTER,\n+\t\t\t\tNT_SENSOR_TYPE_TEMPERATURE, sensor_idx,\n+\t\t\t\tNT_SENSOR_DISABLE_ALARM, SENSOR_MON_UNSIGNED);\n+\tsg->read = &fpga_temperature_sensor_read;\n+\treturn sg;\n+}\ndiff --git a/drivers/net/ntnic/sensors/board_sensors/board_sensors.h b/drivers/net/ntnic/sensors/board_sensors/board_sensors.h\nnew file mode 100644\nindex 0000000000..a7f75b7ae4\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/board_sensors/board_sensors.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _BOARD_SENSORS_H\n+#define _BOARD_SENSORS_H\n+\n+#include <stdint.h>\n+\n+#include \"sensors.h\"\n+\n+#include \"nthw_fpga_model.h\"\n+\n+struct nt_sensor_group *fpga_temperature_sensor_init(uint8_t adapter_no,\n+\t\tunsigned int sensor_idx,\n+\t\tnt_fpga_t *p_fpga);\n+\n+#endif /* _BOARD_SENSORS_H */\ndiff --git a/drivers/net/ntnic/sensors/board_sensors/tempmon.c b/drivers/net/ntnic/sensors/board_sensors/tempmon.c\nnew file mode 100644\nindex 0000000000..2cd3709205\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/board_sensors/tempmon.c\n@@ -0,0 +1,42 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"tempmon.h\"\n+#include \"ntlog.h\"\n+#include \"nthw_register.h\"\n+\n+struct nt_fpga_sensor_monitor *tempmon_new(void)\n+{\n+\tstruct nt_fpga_sensor_monitor *temp =\n+\t\tmalloc(sizeof(struct nt_fpga_sensor_monitor));\n+\tif (temp == NULL)\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: monitor is NULL\\n\", __func__);\n+\treturn temp;\n+}\n+\n+void tempmon_init(struct nt_fpga_sensor_monitor *t, nt_fpga_t *p_fpga)\n+{\n+\tif (t == NULL || p_fpga == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: bad argument(s)\\n\", __func__);\n+\t\treturn;\n+\t}\n+\t/* fetch initialized module */\n+\tt->fpga = p_fpga;\n+\tt->mod = nthw_get_module(t->fpga, MOD_TEMPMON, 0);\n+\tif (t->mod == NULL)\n+\t\tNT_LOG(ERR, ETHDEV, \"module is NULL\\n\");\n+\t/* fetch register */\n+\tt->reg = module_get_register(t->mod, TEMPMON_STAT);\n+\tif (t->reg == NULL)\n+\t\tNT_LOG(ERR, ETHDEV, \"register is NULL\\n\");\n+\t/* fetch fields */\n+\tt->fields = malloc(sizeof(nt_field_t *));\n+\tif (t->fields == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: field is NULL\", __func__);\n+\t\treturn;\n+\t}\n+\tt->fields[0] = register_get_field(t->reg, TEMPMON_STAT_TEMP);\n+\tif (t->fields[0] == NULL)\n+\t\tNT_LOG(ERR, ETHDEV, \"field is NULL\\n\");\n+}\ndiff --git a/drivers/net/ntnic/sensors/board_sensors/tempmon.h b/drivers/net/ntnic/sensors/board_sensors/tempmon.h\nnew file mode 100644\nindex 0000000000..6f2017b714\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/board_sensors/tempmon.h\n@@ -0,0 +1,16 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _TEMPMON_H\n+#define _TEMPMON_H\n+\n+#include \"nthw_fpga_model.h\"\n+#include <stdlib.h>\n+\n+#include \"sensors.h\"\n+\n+struct nt_fpga_sensor_monitor *tempmon_new(void);\n+void tempmon_init(struct nt_fpga_sensor_monitor *t, nt_fpga_t *p_fpga);\n+\n+#endif /* _TEMPMON_H */\ndiff --git a/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c\nnew file mode 100644\nindex 0000000000..e130855a35\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.c\n@@ -0,0 +1,54 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <arpa/inet.h>\n+\n+#include \"nim_sensors.h\"\n+#include \"ntlog.h\"\n+\n+#define TEMP NT_SENSOR_TYPE_TEMPERATURE\n+#define VOLT NT_SENSOR_TYPE_VOLTAGE\n+#define CURR NT_SENSOR_TYPE_CURRENT\n+#define PWR NT_SENSOR_TYPE_POWER\n+\n+#define SNA NT_SENSOR_SUBTYPE_NA\n+#define AVG NT_SENSOR_SUBTYPE_POWER_AVERAGE\n+\n+#define ENA NT_SENSOR_ENABLE_ALARM\n+#define DIA NT_SENSOR_DISABLE_ALARM\n+\n+/*\n+ * Sensors for SFP/SFP+/SFP28. The name of the level 0 temperature sensor is\n+ * empty and will then be set automatically\n+ */\n+struct nt_adapter_sensor_description sfp_sensors_level0[1] = {\n+\t{ TEMP, SNA, NT_SENSOR_SFP_TEMP, DIA, \"\" },\n+};\n+\n+struct nt_adapter_sensor_description sfp_sensors_level1[4] = {\n+\t{ VOLT, SNA, NT_SENSOR_SFP_SUPPLY, DIA, \"Supply\" },\n+\t{ CURR, SNA, NT_SENSOR_SFP_TX_BIAS, DIA, \"Tx Bias\" },\n+\t{ PWR, AVG, NT_SENSOR_SFP_TX_POWER, DIA, \"Tx\" },\n+\t{ PWR, AVG, NT_SENSOR_SFP_RX_POWER, DIA, \"Rx\" }\n+};\n+\n+struct nt_adapter_sensor_description qsfp_sensor_level0[1] = {\n+\t{ TEMP, SNA, NT_SENSOR_QSFP_TEMP, DIA, \"\" },\n+};\n+\n+struct nt_adapter_sensor_description qsfp_sensor_level1[13] = {\n+\t{ VOLT, SNA, NT_SENSOR_QSFP_SUPPLY, DIA, \"Supply\" },\n+\t{ CURR, SNA, NT_SENSOR_QSFP_TX_BIAS1, DIA, \"Tx Bias 1\" },\n+\t{ CURR, SNA, NT_SENSOR_QSFP_TX_BIAS2, DIA, \"Tx Bias 2\" },\n+\t{ CURR, SNA, NT_SENSOR_QSFP_TX_BIAS3, DIA, \"Tx Bias 3\" },\n+\t{ CURR, SNA, NT_SENSOR_QSFP_TX_BIAS4, DIA, \"Tx Bias 4\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_TX_POWER1, DIA, \"Tx 1\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_TX_POWER2, DIA, \"Tx 2\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_TX_POWER3, DIA, \"Tx 3\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_TX_POWER4, DIA, \"Tx 4\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_RX_POWER1, DIA, \"Rx 1\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_RX_POWER2, DIA, \"Rx 2\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_RX_POWER3, DIA, \"Rx 3\" },\n+\t{ PWR, AVG, NT_SENSOR_QSFP_RX_POWER4, DIA, \"Rx 4\" }\n+};\ndiff --git a/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h\nnew file mode 100644\nindex 0000000000..c68c9aa924\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/nim_sensors/nim_sensors.h\n@@ -0,0 +1,19 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NIM_SENSORS_H\n+#define _NIM_SENSORS_H\n+\n+#include <stdint.h>\n+#include <string.h>\n+#include \"sensors.h\"\n+\n+#define XFP_TEMP_LIN_ADDR 96\n+\n+extern struct nt_adapter_sensor_description sfp_sensors_level0[1];\n+extern struct nt_adapter_sensor_description sfp_sensors_level1[4];\n+extern struct nt_adapter_sensor_description qsfp_sensor_level0[1];\n+extern struct nt_adapter_sensor_description qsfp_sensor_level1[13];\n+\n+#endif /* _NIM_SENSORS_H */\ndiff --git a/drivers/net/ntnic/sensors/ntavr/avr_intf.h b/drivers/net/ntnic/sensors/ntavr/avr_intf.h\nnew file mode 100644\nindex 0000000000..feeec6e13a\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/ntavr/avr_intf.h\n@@ -0,0 +1,89 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _AVR_INTF\n+#define _AVR_INTF\n+\n+#include <stdint.h>\n+\n+#define SENSOR_MON_UINT16_NAN 0xFFFF /* Most positive number used as NaN */\n+#define SENSOR_MON_INT16_NAN \\\n+\t((int16_t)0x8000) /* Most negative number used as NaN */\n+\n+/*\n+ * Specify the nature of the raw data. AVR and ntservice must use this\n+ * information when comparing or converting to native format which is little endian\n+ */\n+enum sensor_mon_endian { SENSOR_MON_LITTLE_ENDIAN, SENSOR_MON_BIG_ENDIAN };\n+\n+enum sensor_mon_sign {\n+\tSENSOR_MON_UNSIGNED,\n+\tSENSOR_MON_SIGNED, /* 2's complement */\n+};\n+\n+/* Define sensor devices */\n+enum sensor_mon_device {\n+\tSENSOR_MON_PSU_EXAR_7724_0 = 0, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_PSU_EXAR_7724_1, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_PSU_LTM_4676_0, /* na      NT100E3, page-0 */\n+\tSENSOR_MON_PSU_LTM_4676_1, /* na      NT100E3, page-0 */\n+\tSENSOR_MON_INA219_1, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_INA219_2, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_MAX6642, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_DS1775, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_FAN, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_AVR, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_PEX8734, /* na      NT100E3 */\n+\tSENSOR_MON_RATE_COUNT, /* NT40E3, NT100E3 */\n+\tSENSOR_MON_PSU_LTM_4676_0_1, /* na      NT100E3, page-1 */\n+\tSENSOR_MON_PSU_LTM_4676_1_1, /* na      NT100E3, page-1 */\n+\tSENSOR_MON_MP2886A, /* na,     na,      NT200A02, */\n+\tSENSOR_MON_PSU_EM2260_1, /*     na,      na,      na,       na, NT200D01^M */\n+\tSENSOR_MON_PSU_EM2120_2, /*     na,      na,      na,       na, NT200D01^M */\n+\tSENSOR_MON_MP2886A_PSU_1, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_MP8869S_PSU_2, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_MP8645PGVT_PSU_3, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_MP8645PGVT_PSU_4, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_MP8869S_PSU_5, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_MP8869S_PSU_6, /*     na,      na,      na, NT200A02,        na,   NT50B01, */\n+\tSENSOR_MON_DEVICE_COUNT\n+};\n+\n+#pragma pack(1)\n+struct sensor_mon_setup_data16 {\n+\tuint8_t fpga_idx; /* Destination of results */\n+\tuint8_t device; /* Device to monitor */\n+\tuint8_t device_register; /* Sensor within device */\n+\tuint16_t mask; /* Indicates active bits */\n+\tuint8_t pos; /* Position of first active bit */\n+\tuint16_t format; /* b0,1:sensor_mon_endian_t endian */\n+\t/* b2,3:sensor_mon_sign_t   sign */\n+\tunion {\n+\t\tstruct {\n+\t\t\tint16_t limit_low; /* Signed alarm limit low */\n+\t\t\tint16_t limit_high; /* Signed alarm limit high */\n+\t\t} int16;\n+\n+\t\tstruct {\n+\t\t\tuint16_t limit_low; /* Unsigned alarm limit low */\n+\t\t\tuint16_t limit_high; /* Unsigned alarm limit high */\n+\t\t} uint16;\n+\t};\n+};\n+\n+#pragma pack()\n+struct sensor_mon_setup16 {\n+\tuint8_t setup_cnt; /* Number of entries in setup_data */\n+\tstruct sensor_mon_setup_data16 setup_data[40];\n+};\n+\n+/* Define sensor monitoring control */\n+enum sensor_mon_control {\n+\tSENSOR_MON_CTRL_STOP = 0, /* Stop sensor monitoring */\n+\tSENSOR_MON_CTRL_RUN = 1, /* Start sensor monitoring */\n+\tSENSOR_MON_CTRL_REM_ALL_SENSORS =\n+\t\t2, /* Stop and remove all sensor monitoring setup */\n+};\n+\n+#endif /* _AVR_INTF */\ndiff --git a/drivers/net/ntnic/sensors/ntavr/ntavr.c b/drivers/net/ntnic/sensors/ntavr/ntavr.c\nnew file mode 100644\nindex 0000000000..6d8c3042b1\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/ntavr/ntavr.c\n@@ -0,0 +1,78 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntavr.h\"\n+#include \"ntlog.h\"\n+\n+static int txrx(nthw_spi_v3_t *s_spi, enum avr_opcodes opcode, size_t txsz,\n+\t\tuint16_t *tx, size_t *rxsz, uint16_t *rx)\n+{\n+\tint res = 1;\n+\tstruct tx_rx_buf m_tx = { .size = (uint16_t)txsz, .p_buf = tx };\n+\tstruct tx_rx_buf m_rx = { .size = (uint16_t)*rxsz, .p_buf = rx };\n+\n+\tres = nthw_spi_v3_transfer(s_spi, opcode, &m_tx, &m_rx);\n+\tif (res) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s transfer failed - %i\", __func__, res);\n+\t\treturn res;\n+\t}\n+\n+\tif (rxsz != NULL)\n+\t\t*rxsz = m_rx.size;\n+\n+\treturn res;\n+}\n+\n+uint32_t sensor_read(nthw_spis_t *t_spi, uint8_t fpga_idx,\n+\t\t     uint32_t *p_sensor_result)\n+{\n+\treturn nthw_spis_read_sensor(t_spi, fpga_idx, p_sensor_result);\n+}\n+\n+int nt_avr_sensor_mon_setup(struct sensor_mon_setup16 *p_setup, nthw_spi_v3_t *s_spi)\n+{\n+\tint error;\n+\tsize_t tx_size;\n+\tsize_t rx_size = 0;\n+\n+\ttx_size = sizeof(struct sensor_mon_setup16) - sizeof(p_setup->setup_data);\n+\ttx_size += sizeof(p_setup->setup_data[0]) * p_setup->setup_cnt;\n+\n+\terror = txrx(s_spi, AVR_OP_SENSOR_MON_SETUP, tx_size, (uint16_t *)p_setup,\n+\t\t     &rx_size, NULL);\n+\n+\tif (error) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s failed\\n\", __func__);\n+\t\treturn error;\n+\t}\n+\n+\tif (rx_size != 0) {\n+\t\tNT_LOG(ERR, ETHDEV,\n+\t\t       \"%s: Returned data: Expected size = 0, Actual = %zu\",\n+\t\t       __func__, rx_size);\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+int nt_avr_sensor_mon_ctrl(nthw_spi_v3_t *s_spi, enum sensor_mon_control ctrl)\n+{\n+\tint error;\n+\tsize_t rx_size = 0;\n+\n+\terror = txrx(s_spi, AVR_OP_SENSOR_MON_CONTROL, sizeof(ctrl),\n+\t\t     (uint16_t *)(&ctrl), &rx_size, NULL);\n+\n+\tif (error != 0)\n+\t\treturn error;\n+\n+\tif (rx_size != 0) {\n+\t\tNT_LOG(ERR, ETHDEV,\n+\t\t       \"%s: Returned data: Expected size = 0, Actual = %zu\",\n+\t\t       __func__, rx_size);\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ntnic/sensors/ntavr/ntavr.h b/drivers/net/ntnic/sensors/ntavr/ntavr.h\nnew file mode 100644\nindex 0000000000..b7a7aeb908\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/ntavr/ntavr.h\n@@ -0,0 +1,32 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTAVR_H\n+#define _NTAVR_H\n+\n+#include <stdint.h>\n+#include <stdlib.h>\n+\n+#include \"avr_intf.h\"\n+#include \"nthw_drv.h\"\n+#include \"nthw_spi_v3.h\"\n+\n+/*\n+ * @internal\n+ * @brief AVR Device Enum\n+ *\n+ * Global names for identifying an AVR device for Generation2 adapters\n+ */\n+enum ntavr_device {\n+\tNTAVR_MAINBOARD, /* Mainboard AVR device */\n+\tNTAVR_FRONTBOARD /* Frontboard AVR device */\n+};\n+\n+int nt_avr_sensor_mon_setup(struct sensor_mon_setup16 *p_setup,\n+\t\t\tnthw_spi_v3_t *s_spi);\n+int nt_avr_sensor_mon_ctrl(nthw_spi_v3_t *s_spi, enum sensor_mon_control ctrl);\n+uint32_t sensor_read(nthw_spis_t *t_spi, uint8_t fpga_idx,\n+\t\t     uint32_t *p_sensor_result);\n+\n+#endif /* _NTAVR_H */\ndiff --git a/drivers/net/ntnic/sensors/sensor_types.h b/drivers/net/ntnic/sensors/sensor_types.h\nnew file mode 100644\nindex 0000000000..bac4e925f9\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/sensor_types.h\n@@ -0,0 +1,259 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _SENSOR_TYPES_H\n+#define _SENSOR_TYPES_H\n+\n+/*\n+ * Sensor types\n+ */\n+enum nt_sensor_type_e {\n+\tNT_SENSOR_TYPE_UNKNOWN = 0,\n+\tNT_SENSOR_TYPE_TEMPERATURE = 1, /* Unit: 0.1 degree Celsius */\n+\tNT_SENSOR_TYPE_VOLTAGE = 2, /* Unit: 1 mV */\n+\tNT_SENSOR_TYPE_CURRENT = 3, /* Unit: 1 uA */\n+\tNT_SENSOR_TYPE_POWER = 4, /* Unit: 0.1 uW */\n+\tNT_SENSOR_TYPE_FAN = 5, /* Unit: 1 RPM (Revolutions Per Minute) */\n+\tNT_SENSOR_TYPE_HIGH_POWER = 6, /* Unit: 1 mW */\n+\tNT_SENSOR_TYPE_NUMBER = 7,\n+};\n+\n+/*\n+ * Generic SFP/SFP+/SFP28 sensors\n+ *\n+ * These sensors should be used instead of all adapter specific SFP sensors\n+ * that have been deprecated..\n+ */\n+enum nt_sensors_sfp {\n+\tNT_SENSOR_SFP_TEMP,\n+\tNT_SENSOR_SFP_SUPPLY,\n+\tNT_SENSOR_SFP_TX_BIAS,\n+\tNT_SENSOR_SFP_TX_POWER,\n+\tNT_SENSOR_SFP_RX_POWER,\n+};\n+\n+/*\n+ * Generic QSFP/QSFP+/QSFP28 sensors\n+ *\n+ * These sensors should be used instead of all adapter specific QSFP sensors\n+ * that have been deprecated..\n+ */\n+enum nt_sensors_qsfp {\n+\tNT_SENSOR_QSFP_TEMP,\n+\tNT_SENSOR_QSFP_SUPPLY,\n+\tNT_SENSOR_QSFP_TX_BIAS1,\n+\tNT_SENSOR_QSFP_TX_BIAS2,\n+\tNT_SENSOR_QSFP_TX_BIAS3,\n+\tNT_SENSOR_QSFP_TX_BIAS4,\n+\tNT_SENSOR_QSFP_TX_POWER1,\n+\tNT_SENSOR_QSFP_TX_POWER2,\n+\tNT_SENSOR_QSFP_TX_POWER3,\n+\tNT_SENSOR_QSFP_TX_POWER4,\n+\tNT_SENSOR_QSFP_RX_POWER1,\n+\tNT_SENSOR_QSFP_RX_POWER2,\n+\tNT_SENSOR_QSFP_RX_POWER3,\n+\tNT_SENSOR_QSFP_RX_POWER4,\n+};\n+\n+typedef enum nt_sensor_type_e nt_sensor_type_t;\n+\n+/*\n+ * Sensor subtypes\n+ */\n+enum nt_sensor_sub_type_e {\n+\tNT_SENSOR_SUBTYPE_NA = 0,\n+\t/*\n+\t * Subtype for NT_SENSOR_TYPE_POWER type on optical modules (optical modulation\n+\t * amplitude measured)\n+\t */\n+\tNT_SENSOR_SUBTYPE_POWER_OMA,\n+\t/* Subtype for NT_SENSOR_TYPE_POWER type on optical modules (average power measured) */\n+\tNT_SENSOR_SUBTYPE_POWER_AVERAGE,\n+\t/* Subtype for NT_SENSOR_TYPE_HIGH_POWER type on adapters (total power consumption) */\n+\tNT_SENSOR_SUBTYPE_POWER_TOTAL\n+};\n+\n+typedef enum nt_sensor_sub_type_e nt_sensor_sub_type_t;\n+\n+/*\n+ * Sensor source\n+ */\n+enum nt_sensor_source_e {\n+\tNT_SENSOR_SOURCE_UNKNOWN = 0x00, /* Unknown source */\n+\t/*\n+\t * Sensors located in a port. These are primary sensors - usually NIM temperature. Presence\n+\t * depends on adapter and NIM type.\n+\t */\n+\tNT_SENSOR_SOURCE_PORT =\n+\t\t0x01,\n+\t/*\n+\t * Level 1 sensors located in a port. These are secondary sensors - usually NIM supply\n+\t * voltage, Tx bias and Rx/Tx optical power. Presence depends on adapter and NIM type.\n+\t */\n+\tNT_SENSOR_SOURCE_LEVEL1_PORT =\n+\t\t0x02,\n+#ifndef DOXYGEN_INTERNAL_ONLY\n+\tNT_SENSOR_SOURCE_LEVEL2_PORT =\n+\t\t0x04, /* Level 2 sensors located in a port */\n+#endif\n+\tNT_SENSOR_SOURCE_ADAPTER = 0x08, /* Sensors mounted on the adapter */\n+\tNT_SENSOR_SOURCE_LEVEL1_ADAPTER =\n+\t\t0x10, /* Level 1 sensors mounted on the adapter */\n+#ifndef DOXYGEN_INTERNAL_ONLY\n+\tNT_SENSOR_SOURCE_LEVEL2_ADAPTER =\n+\t\t0x20, /* Level 2 sensors mounted on the adapter */\n+#endif\n+};\n+\n+/*\n+ * Sensor state\n+ */\n+enum nt_sensor_state_e {\n+\tNT_SENSOR_STATE_UNKNOWN = 0, /* Unknown state */\n+\tNT_SENSOR_STATE_INITIALIZING = 1, /* The sensor is initializing */\n+\tNT_SENSOR_STATE_NORMAL = 2, /* Sensor values are within range */\n+\tNT_SENSOR_STATE_ALARM = 3, /* Sensor values are out of range */\n+\tNT_SENSOR_STATE_NOT_PRESENT =\n+\t\t4 /* The sensor is not present, for example, SFP without diagnostics */\n+};\n+\n+typedef enum nt_sensor_state_e nt_sensor_state_t;\n+\n+/*\n+ * Sensor value\n+ */\n+#define NT_SENSOR_NAN \\\n+\t(0x80000000) /* Indicates that sensor value or sensor limit is not valid (Not a Number) */\n+\n+/*\n+ * Master/Slave\n+ */\n+enum nt_bonding_type_e {\n+\tNT_BONDING_UNKNOWN, /* Unknown bonding type */\n+\tNT_BONDING_MASTER, /* Adapter is master in the bonding */\n+\tNT_BONDING_SLAVE, /* Adapter is slave in the bonding */\n+\tNT_BONDING_PEER /* Adapter is bonded, but relationship is symmetric */\n+};\n+\n+enum nt_sensors_e {\n+\t/* Public sensors (Level 0) */\n+\tNT_SENSOR_FPGA_TEMP, /* FPGA temperature sensor */\n+};\n+\n+/*\n+ * Adapter types\n+ */\n+enum nt_adapter_type_e {\n+\tNT_ADAPTER_TYPE_UNKNOWN = 0, /* Unknown adapter type */\n+\tNT_ADAPTER_TYPE_NT4E, /* NT4E network adapter */\n+\tNT_ADAPTER_TYPE_NT20E, /* NT20E network adapter */\n+\tNT_ADAPTER_TYPE_NT4E_STD, /* NT4E-STD network adapter */\n+\tNT_ADAPTER_TYPE_NT4E_PORT, /* NTPORT4E expansion adapter */\n+\tNT_ADAPTER_TYPE_NTBPE, /* NTBPE bypass adapter */\n+\tNT_ADAPTER_TYPE_NT20E2, /* NT20E2 network adapter */\n+\tNT_ADAPTER_TYPE_RESERVED1, /* Reserved */\n+\tNT_ADAPTER_TYPE_RESERVED2, /* Reserved */\n+\tNT_ADAPTER_TYPE_NT40E2_1, /* NT40E2-1 network adapter */\n+\tNT_ADAPTER_TYPE_NT40E2_4, /* NT40E2-4 network adapter */\n+\tNT_ADAPTER_TYPE_NT4E2_4T_BP, /* NT4E2-4T-BP bypass network adapter */\n+\tNT_ADAPTER_TYPE_NT4E2_4_PTP, /* NT4E2-4 PTP network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT20E2_PTP, /* NT20E2 PTP network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT40E3_4_PTP, /* NT40E3 network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT100E3_1_PTP, /* NT100E3 network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT20E3_2_PTP, /* NT20E3 network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT80E3_2_PTP, /* NT80E3 network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT200E3_2, /* NT200E3 network adapter */\n+\tNT_ADAPTER_TYPE_NT200A01, /* NT200A01 network adapter */\n+\tNT_ADAPTER_TYPE_NT200A01_2X100 =\n+\t\tNT_ADAPTER_TYPE_NT200A01, /* NT200A01 2 x 100 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT40A01_4X1, /* NT40A01_4X1 network adapter with IEEE1588 */\n+\tNT_ADAPTER_TYPE_NT200A01_2X40, /* NT200A01 2 x 40 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT80E3_2_PTP_8X10, /* NT80E3 8 x 10 Gbps network adapter with IEEE1588 */\n+\t/*  */\n+\tNT_ADAPTER_TYPE_INTEL_A10_4X10, /* Intel PAC A10 GX 4 x 10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_INTEL_A10_1X40, /* Intel PAC A10 GX 1 x 40 Gbps network adapter */\n+\t/*  */\n+\tNT_ADAPTER_TYPE_NT200A01_8X10, /* NT200A01 8 x 10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_2X100, /* NT200A02 2 x 100 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_2X40, /* NT200A02 2 x 40 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A01_2X25, /* Deprecated */\n+\tNT_ADAPTER_TYPE_NT200A01_2X10_25 =\n+\t\tNT_ADAPTER_TYPE_NT200A01_2X25, /* NT200A01 2 x 10/25 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_2X25, /* Deprecated */\n+\tNT_ADAPTER_TYPE_NT200A02_2X10_25 =\n+\t\tNT_ADAPTER_TYPE_NT200A02_2X25, /* NT200A02 2 x 10/25 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_4X25, /* Deprecated */\n+\tNT_ADAPTER_TYPE_NT200A02_4X10_25 =\n+\t\tNT_ADAPTER_TYPE_NT200A02_4X25, /* NT200A02 4 x 10/25 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_8X10, /* NT200A02 8 x 10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT50B01_2X25, /* Deprecated */\n+\tNT_ADAPTER_TYPE_NT50B01_2X10_25 =\n+\t\tNT_ADAPTER_TYPE_NT50B01_2X25, /* NT50B01 2 x 10/25 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT200A02_2X1_10, /* NT200A02 2 x 1/10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT100A01_4X1_10, /* NT100A01 4 x 1/10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT100A01_4X10_25, /* NT100A01 4 x 10/25 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT50B01_2X1_10, /* NT50B01 2 x 1/10 Gbps network adapter */\n+\tNT_ADAPTER_TYPE_NT40A11_4X1_10, /* NT40A11 4 x 1/10 Gbps network adapter */\n+#ifndef DOXYGEN_INTERNAL_ONLY\n+\tNT_ADAPTER_TYPE_ML605 = 10000, /* NT20E2 eval board */\n+#endif\n+\tNT_ADAPTER_TYPE_4GARCH_HAMOA =\n+\t\t(1U\n+\t\t << 29), /* Bit to mark to adapters as a 4GArch Hamoa adapter */\n+\tNT_ADAPTER_TYPE_4GARCH =\n+\t\t(1U << 30), /* Bit to mark to adapters as a 4GArch adapter */\n+\t/* NOTE: do *NOT* add normal adapters after the group bit mark enums */\n+};\n+\n+/* The NT200E3 adapter sensor id's */\n+typedef enum nt_sensors_adapter_nt200_e3_e {\n+\t/* Public sensors (Level 0) */\n+\tNT_SENSOR_NT200E3_FPGA_TEMP, /* FPGA temperature sensor */\n+\tNT_SENSOR_NT200E3_FAN_SPEED, /* FAN speed sensor */\n+\t/* MCU (Micro Controller Unit) temperature sensor located inside enclosure below FAN */\n+\tNT_SENSOR_NT200E3_MCU_TEMP,\n+\tNT_SENSOR_NT200E3_PSU0_TEMP, /* Power supply 0 temperature sensor */\n+\tNT_SENSOR_NT200E3_PSU1_TEMP, /* Power supply 1 temperature sensor */\n+\tNT_SENSOR_NT200E3_PCB_TEMP, /* PCB temperature sensor */\n+\n+\t/* Diagnostic sensors (Level 1) */\n+\t/* Total power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200E3_NT200E3_POWER,\n+\t/* FPGA power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200E3_FPGA_POWER,\n+\t/* DDR4 RAM power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200E3_DDR4_POWER,\n+\t/* NIM power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200E3_NIM_POWER,\n+\n+\tNT_SENSOR_NT200E3_L1_MAX, /* Number of NT200E3 level 0,1 board sensors */\n+} nt_sensors_adapter_nt200_e3_t;\n+\n+/*\n+ * The following sensors are deprecated - generic types should be used instead\n+ * The NIM temperature sensor must be the one with the lowest sensor_index\n+ * (enum value) in order to be shown by the monitoring tool in port mode\n+ */\n+enum nt_sensors_port_nt200_e3_2_e {\n+\t/* Public sensors */\n+\tNT_SENSOR_NT200E3_NIM, /* QSFP28 temperature sensor */\n+\n+\t/* Diagnostic sensors (Level 1) */\n+\tNT_SENSOR_NT200E3_SUPPLY, /* QSFP28 supply voltage sensor */\n+\tNT_SENSOR_NT200E3_TX_BIAS1, /* QSFP28 TX bias line 0 current sensor */\n+\tNT_SENSOR_NT200E3_TX_BIAS2, /* QSFP28 TX bias line 1 current sensor */\n+\tNT_SENSOR_NT200E3_TX_BIAS3, /* QSFP28 TX bias line 2 current sensor */\n+\tNT_SENSOR_NT200E3_TX_BIAS4, /* QSFP28 TX bias line 3 current sensor */\n+\tNT_SENSOR_NT200E3_RX1, /* QSFP28 RX line 0 power sensor */\n+\tNT_SENSOR_NT200E3_RX2, /* QSFP28 RX line 1 power sensor */\n+\tNT_SENSOR_NT200E3_RX3, /* QSFP28 RX line 2 power sensor */\n+\tNT_SENSOR_NT200E3_RX4, /* QSFP28 RX line 3 power sensor */\n+\tNT_SENSOR_NT200E3_TX1, /* QSFP28 TX line 0 power sensor */\n+\tNT_SENSOR_NT200E3_TX2, /* QSFP28 TX line 1 power sensor */\n+\tNT_SENSOR_NT200E3_TX3, /* QSFP28 TX line 2 power sensor */\n+\tNT_SENSOR_NT200E3_TX4, /* QSFP28 TX line 3 power sensor */\n+\tNT_SENSOR_NT200E3_PORT_MAX, /* Number of NT200E3 port sensors */\n+};\n+\n+#endif\ndiff --git a/drivers/net/ntnic/sensors/sensors.c b/drivers/net/ntnic/sensors/sensors.c\nnew file mode 100644\nindex 0000000000..2a85843196\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/sensors.c\n@@ -0,0 +1,273 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"sensors.h\"\n+#include \"ntlog.h\"\n+\n+void sensor_deinit(struct nt_sensor_group *sg)\n+{\n+\tif (sg) {\n+\t\tif (sg->sensor)\n+\t\t\tfree(sg->sensor);\n+\t\tif (sg->monitor)\n+\t\t\tfree(sg->monitor);\n+\t\tfree(sg);\n+\t}\n+}\n+\n+struct nt_adapter_sensor *\n+allocate_sensor(uint8_t adapter_or_port_index, const char *p_name,\n+\t\tenum nt_sensor_source_e ssrc, enum nt_sensor_type_e type,\n+\t\tunsigned int index, enum nt_sensor_event_alarm_e event_alarm,\n+\t\tenum sensor_mon_sign si)\n+{\n+\tstruct nt_adapter_sensor *sensor =\n+\t\t(struct nt_adapter_sensor *)malloc(sizeof(struct nt_adapter_sensor));\n+\tif (sensor == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor is NULL\", __func__);\n+\t\treturn NULL;\n+\t}\n+\n+\tsensor->alarm = event_alarm;\n+\tsensor->m_enable_alarm = true;\n+\tsensor->m_intf_no = 0xFF;\n+\tsensor->m_adapter_no = 0xFF;\n+\tsensor->si = si;\n+\n+\tsensor->info.source = ssrc;\n+\tsensor->info.source_index = adapter_or_port_index;\n+\tsensor->info.sensor_index = index;\n+\tsensor->info.type = type;\n+\tsensor->info.sub_type = NT_SENSOR_SUBTYPE_NA;\n+\tsensor->info.state = NT_SENSOR_STATE_INITIALIZING;\n+\tsensor->info.value = NT_SENSOR_NAN;\n+\tsensor->info.value_lowest = NT_SENSOR_NAN;\n+\tsensor->info.value_highest = NT_SENSOR_NAN;\n+\tmemset(sensor->info.name, 0, NT_INFO_SENSOR_NAME);\n+\tmemcpy(sensor->info.name, p_name,\n+\t       (strlen(p_name) > NT_INFO_SENSOR_NAME) ? NT_INFO_SENSOR_NAME :\n+\t       strlen(p_name));\n+\tsensor->info.name[NT_INFO_SENSOR_NAME] = '\\0';\n+\n+\treturn sensor;\n+}\n+\n+void update_sensor_value(struct nt_adapter_sensor *sensor, int32_t value)\n+{\n+\tif (sensor == NULL)\n+\t\treturn;\n+\tsensor->info.value = value;\n+\tif (sensor->info.value_highest < value ||\n+\t\t\t(unsigned int)sensor->info.value_highest == NT_SENSOR_NAN)\n+\t\tsensor->info.value_highest = value;\n+\tif (sensor->info.value_lowest > value ||\n+\t\t\t(unsigned int)sensor->info.value_lowest == NT_SENSOR_NAN)\n+\t\tsensor->info.value_lowest = value;\n+}\n+\n+struct nt_adapter_sensor *\n+allocate_sensor_by_description(uint8_t adapter_or_port_index,\n+\t\t\t       enum nt_sensor_source_e ssrc,\n+\t\t\t       struct nt_adapter_sensor_description *descr)\n+{\n+\tstruct nt_adapter_sensor *sensor =\n+\t\t(struct nt_adapter_sensor *)malloc(sizeof(struct nt_adapter_sensor));\n+\tif (sensor == NULL) {\n+\t\tNT_LOG(ERR, ETHDEV, \"%s: sensor is NULL\", __func__);\n+\t\treturn NULL;\n+\t}\n+\n+\tsensor->alarm = descr->event_alarm;\n+\tsensor->m_enable_alarm = true;\n+\tsensor->m_intf_no = 0xFF;\n+\tsensor->m_adapter_no = 0xFF;\n+\tsensor->si = SENSOR_MON_UNSIGNED;\n+\n+\tsensor->info.source_index = adapter_or_port_index;\n+\tsensor->info.source = ssrc;\n+\tsensor->info.type = descr->type;\n+\tsensor->info.sensor_index = descr->index;\n+\tmemset(sensor->info.name, 0, NT_INFO_SENSOR_NAME);\n+\tmemcpy(sensor->info.name, descr->name,\n+\t       (strlen(descr->name) > NT_INFO_SENSOR_NAME) ?\n+\t       NT_INFO_SENSOR_NAME :\n+\t       strlen(descr->name));\n+\tsensor->info.name[NT_INFO_SENSOR_NAME] = '\\0';\n+\n+\treturn sensor;\n+}\n+\n+void init_sensor_group(struct nt_sensor_group *sg)\n+{\n+\t/* Set all pointers to NULL */\n+\tsg->sensor = NULL;\n+\tsg->monitor = NULL;\n+\tsg->next = NULL;\n+\tsg->read = NULL;\n+\tsg->conv_func = NULL;\n+}\n+\n+/* Getters */\n+int32_t get_value(struct nt_sensor_group *sg)\n+{\n+\treturn sg->sensor->info.value;\n+};\n+\n+int32_t get_lowest(struct nt_sensor_group *sg)\n+{\n+\treturn sg->sensor->info.value_lowest;\n+};\n+\n+int32_t get_highest(struct nt_sensor_group *sg)\n+{\n+\treturn sg->sensor->info.value_highest;\n+};\n+\n+char *get_name(struct nt_sensor_group *sg)\n+{\n+\treturn sg->sensor->info.name;\n+};\n+\n+/* Conversion functions */\n+int null_signed(uint32_t p_sensor_result)\n+{\n+\treturn (int16_t)p_sensor_result;\n+}\n+\n+int null_unsigned(uint32_t p_sensor_result)\n+{\n+\treturn (uint16_t)p_sensor_result;\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For EXAR7724: Convert a read Vch value to Napatech internal representation\n+ * Doc: Vout = ReadVal * 0.015 (PRESCALE is accounted for)\n+ * ******************************************************************************\n+ */\n+int exar7724_vch(uint32_t p_sensor_result)\n+{\n+\treturn p_sensor_result * 15; /* NT unit: 1mV */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For EXAR7724: Convert a read Vin value to Napatech internal representation\n+ * Doc: Vout = ReadVal * 0.0125\n+ * ******************************************************************************\n+ */\n+int exar7724_vin(uint32_t p_sensor_result)\n+{\n+\treturn (p_sensor_result * 25) / 2; /* NT unit: 1mV */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For EXAR7724: Convert a read Tj value to Napatech internal representation\n+ * Doc: Temp (in Kelvin) = (((ReadVal * 10mV) - 600mV) / (2mV/K)) + 300K =\n+ *                      = ReadVal * 5K\n+ * ******************************************************************************\n+ */\n+int exar7724_tj(uint32_t p_sensor_result)\n+{\n+\t/*\n+\t * A value of 2730 is used instead of 2732 which is more correct but since\n+\t * the temperature step is 5 degrees it is more natural to show these steps\n+\t */\n+\treturn p_sensor_result * 50 - 2730; /* NT unit: 0.1C */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * Conversion function for Linear Tecnology Linear_5s_11s format.\n+ * The functions returns Y * 2**N, where N = b[15:11] is a 5-bit two's complement\n+ * integer and Y = b[10:0] is an 11-bit two's complement integer.\n+ * The multiplier value is used for scaling to Napatech units.\n+ * ******************************************************************************\n+ */\n+static int conv5s_11s(uint16_t value, int multiplier)\n+{\n+\tint n, y;\n+\n+\ty = value & 0x07FF;\n+\n+\tif (value & 0x0400)\n+\t\ty -= 0x0800; /* The MSBit is a sign bit */\n+\n+\tn = (value >> 11) & 0x1F;\n+\n+\tif (n & 0x10)\n+\t\tn -= 0x20; /* The MSBit is a sign bit */\n+\n+\ty *= multiplier;\n+\n+\tif (n > 0)\n+\t\ty *= (1 << n);\n+\n+\telse if (n < 0)\n+\t\ty /= (1 << (-n));\n+\n+\treturn y;\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * Temperature conversion from Linear_5s_11s format.\n+ * ******************************************************************************\n+ */\n+int ltm4676_tj(uint32_t p_sensor_result)\n+{\n+\treturn (uint16_t)conv5s_11s(p_sensor_result, 10); /* NT unit: 0.1C */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For MP2886a: Convert a read Tj value to Napatech internal representation\n+ * ******************************************************************************\n+ */\n+int mp2886a_tj(uint32_t p_sensor_result)\n+{\n+\t/*\n+\t * MPS-2886p: READ_TEMPERATURE (register 0x8Dh)\n+\t * READ_TEMPERATURE is a 2-byte, unsigned integer.\n+\t */\n+\treturn (uint16_t)p_sensor_result; /* NT unit: 0.1C */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For MAX6642: Convert a read temperature value to Napatech internal representation\n+ * ******************************************************************************\n+ */\n+int max6642_t(uint32_t p_sensor_result)\n+{\n+\tif ((p_sensor_result >> 8) == 0xFF)\n+\t\treturn NT_SENSOR_NAN;\n+\n+\t/* The six lower bits are not used */\n+\treturn (int)(((p_sensor_result >> 6) * 5) /\n+\t\t     2); /* NT unit: 0.25 deg, Native unit: 0.1C */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For DS1775: Convert a read temperature value to Napatech internal representation\n+ * ******************************************************************************\n+ */\n+int ds1775_t(uint32_t p_sensor_result)\n+{\n+\treturn (p_sensor_result * 10) /\n+\t       256; /* NT unit: 0.1 deg, Native unit: 1/256 C */\n+}\n+\n+/*\n+ * ******************************************************************************\n+ * For FAN: Convert a tick count to RPM\n+ * NT unit: RPM, Native unit: 2 ticks/revolution\n+ * ******************************************************************************\n+ */\n+int fan(uint32_t p_sensor_result)\n+{\n+\treturn (p_sensor_result * 60U / 4);\n+}\ndiff --git a/drivers/net/ntnic/sensors/sensors.h b/drivers/net/ntnic/sensors/sensors.h\nnew file mode 100644\nindex 0000000000..1424b8bc83\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/sensors.h\n@@ -0,0 +1,127 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _SENSORS_H\n+#define _SENSORS_H\n+\n+#include \"sensor_types.h\"\n+#include \"stream_info.h\"\n+#include \"nthw_platform_drv.h\"\n+#include \"nthw_drv.h\"\n+#include \"nthw_spi_v3.h\"\n+#include \"nthw_fpga_model.h\"\n+\n+#include <stdint.h>\n+#include <stdbool.h>\n+#include <stddef.h>\n+#include <stdlib.h>\n+#include <limits.h>\n+#include \"avr_intf.h\"\n+\n+enum nt_sensor_event_alarm_e {\n+\tNT_SENSOR_ENABLE_ALARM,\n+\tNT_SENSOR_LOG_ALARM,\n+\tNT_SENSOR_DISABLE_ALARM,\n+};\n+\n+/*\n+ * Sensor Class types\n+ */\n+enum nt_sensor_class_e {\n+\tNT_SENSOR_CLASS_FPGA =\n+\t\t0, /* Class for FPGA based sensors e.g FPGA temperature */\n+\tNT_SENSOR_CLASS_MCU =\n+\t\t1, /* Class for MCU based sensors e.g MCU temperature */\n+\tNT_SENSOR_CLASS_PSU =\n+\t\t2, /* Class for PSU based sensors e.g PSU temperature */\n+\tNT_SENSOR_CLASS_PCB =\n+\t\t3, /* Class for PCB based sensors e.g PCB temperature */\n+\tNT_SENSOR_CLASS_NIM =\n+\t\t4, /* Class for NIM based sensors e.g NIM temperature */\n+\tNT_SENSOR_CLASS_ANY = 5, /* Class for ANY sensors e.g any sensors */\n+};\n+\n+typedef enum nt_sensor_class_e nt_sensor_class_t;\n+\n+/*\n+ * Port of the sensor class\n+ */\n+struct nt_adapter_sensor {\n+\tuint8_t m_adapter_no;\n+\tuint8_t m_intf_no;\n+\tuint8_t fpga_idx; /* for AVR sensors */\n+\tenum sensor_mon_sign si;\n+\tstruct nt_info_sensor_s info;\n+\tenum nt_sensor_event_alarm_e alarm;\n+\tbool m_enable_alarm;\n+};\n+\n+struct nt_fpga_sensor_monitor {\n+\tnt_fpga_t *fpga;\n+\tnt_module_t *mod;\n+\n+\tnt_register_t *reg;\n+\tnt_field_t **fields;\n+\tuint8_t fields_num;\n+};\n+\n+/*\n+ * Sensor description.\n+ * Describe the static behavior of the sensor.\n+ */\n+struct nt_adapter_sensor_description {\n+\tenum nt_sensor_type_e type; /* Sensor type. */\n+\tenum nt_sensor_sub_type_e sub_type; /* Sensor subtype (if any applicable) */\n+\tunsigned int index; /* Sensor group index. */\n+\tenum nt_sensor_event_alarm_e event_alarm; /* Enable/Disable event alarm */\n+\tchar name[20]; /* Sensor name. */\n+};\n+\n+struct nt_sensor_group {\n+\tstruct nt_adapter_sensor *sensor;\n+\tstruct nt_fpga_sensor_monitor *monitor;\n+\tvoid (*read)(struct nt_sensor_group *sg, nthw_spis_t *t_spi);\n+\n+\t/* conv params are needed to call current conversion functions */\n+\tint (*conv_func)(uint32_t p_sensor_result);\n+\t/* i2c interface for NIM sensors */\n+\n+\tstruct nt_sensor_group *next;\n+};\n+\n+void init_sensor_group(struct nt_sensor_group *sg);\n+\n+void update_sensor_value(struct nt_adapter_sensor *sensor, int32_t value);\n+\n+void sensor_deinit(struct nt_sensor_group *sg);\n+\n+/* getters */\n+int32_t get_value(struct nt_sensor_group *sg);\n+int32_t get_lowest(struct nt_sensor_group *sg);\n+int32_t get_highest(struct nt_sensor_group *sg);\n+char *get_name(struct nt_sensor_group *sg);\n+\n+struct nt_adapter_sensor *\n+allocate_sensor(uint8_t adapter_or_port_index, const char *p_name,\n+\t\tenum nt_sensor_source_e ssrc, enum nt_sensor_type_e type,\n+\t\tunsigned int index, enum nt_sensor_event_alarm_e event_alarm,\n+\t\tenum sensor_mon_sign si);\n+struct nt_adapter_sensor *\n+allocate_sensor_by_description(uint8_t adapter_or_port_index,\n+\t\t\t       enum nt_sensor_source_e ssrc,\n+\t\t\t       struct nt_adapter_sensor_description *descr);\n+\n+/* conversion functions */\n+int null_signed(uint32_t p_sensor_result);\n+int null_unsigned(uint32_t p_sensor_result);\n+int exar7724_tj(uint32_t p_sensor_result);\n+int max6642_t(uint32_t p_sensor_result);\n+int ds1775_t(uint32_t p_sensor_result);\n+int ltm4676_tj(uint32_t p_sensor_result);\n+int exar7724_vch(uint32_t p_sensor_result);\n+int exar7724_vin(uint32_t p_sensor_result);\n+int mp2886a_tj(uint32_t p_sensor_result);\n+int fan(uint32_t p_sensor_result);\n+\n+#endif /* _SENSORS_H */\ndiff --git a/drivers/net/ntnic/sensors/stream_info.h b/drivers/net/ntnic/sensors/stream_info.h\nnew file mode 100644\nindex 0000000000..b94231fd8b\n--- /dev/null\n+++ b/drivers/net/ntnic/sensors/stream_info.h\n@@ -0,0 +1,86 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _STREAM_INFO_H\n+#define _STREAM_INFO_H\n+\n+#include \"sensor_types.h\"\n+\n+#include <stdint.h>\n+\n+/*\n+ * This structure will return the sensor specific information\n+ *\n+ * The units used for the fields: value, value_lowest, value_highest, limit_low and\n+ * limit_high depend on the type field. See @ref nt_sensor_type_e.\n+ *\n+ * For the limit_low and limit_high fields the following applies:\\n\n+ * If the sensor is located in a NIM (Network Interface Module), the limits are read\n+ * from the NIM module via the DMI (Diagnostic Monitoring Interface) from the alarm\n+ * and warning thresholds section, and the units are changed to internal representation.\n+ * Only the alarm thresholds are used and are read only once during initialization.\n+ * The limits cannot be changed.\n+ *\n+ * The value field is updated internally on a regular basis and is also based on a\n+ * value read from the NIM which is also changed to internal representation.\n+ *\n+ * Not all NIM types support DMI data, and its presence must be determined by reading an\n+ * option flag. In general, a NIM can read out: temperature, supply voltage,\n+ * TX bias, TX optical power and RX optical power but not all NIM types support all\n+ * 5 values.\n+ *\n+ * If external calibration is used (most NIM use internal calibration), both the\n+ * current value and the threshold values are subjected to the specified calibration\n+ * along with the change to internal calibration.\n+ */\n+#define NT_INFO_SENSOR_NAME 50\n+struct nt_info_sensor_s {\n+\tenum nt_sensor_source_e\n+\tsource; /* The source of the sensor (port or adapter on which the sensor resides) */\n+\t/*\n+\t * The source index - the adapter number for adapter sensors and port number for port\n+\t * sensors\n+\t */\n+\tuint32_t source_index;\n+\t/*\n+\t * The sensor index within the source index (sensor number on the adapter or sensor number\n+\t * on the port)\n+\t */\n+\tuint32_t sensor_index;\n+\tenum nt_sensor_type_e type; /* The sensor type */\n+\tenum nt_sensor_sub_type_e sub_type; /* The sensor subtype (if applicable) */\n+\tenum nt_sensor_state_e state; /* The current state (normal or alarm) */\n+\tint32_t value; /* The current value */\n+\tint32_t value_lowest; /* The lowest value registered */\n+\tint32_t value_highest; /* The highest value registered */\n+\tchar name[NT_INFO_SENSOR_NAME + 1]; /* The sensor name */\n+\tenum nt_adapter_type_e\n+\tadapter_type; /* The adapter type where the sensor resides */\n+};\n+\n+/* The NT200A02 adapter sensor id's */\n+enum nt_sensors_adapter_nt200a02_e {\n+\t/* Public sensors (Level 0) */\n+\tNT_SENSOR_NT200A02_FPGA_TEMP, /* FPGA temperature sensor */\n+\tNT_SENSOR_NT200A02_FAN_SPEED, /* FAN speed sensor */\n+\n+\tNT_SENSOR_NT200A02_MCU_TEMP,\n+\tNT_SENSOR_NT200A02_PSU0_TEMP, /* Power supply 0 temperature sensor */\n+\tNT_SENSOR_NT200A02_PSU1_TEMP, /* Power supply 1 temperature sensor */\n+\tNT_SENSOR_NT200A02_PCB_TEMP, /* PCB temperature sensor */\n+\n+\t/* Diagnostic sensors (Level 1) */\n+\t/* Total power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200A02_NT200A02_POWER,\n+\t/* FPGA power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200A02_FPGA_POWER,\n+\t/* DDR4 RAM power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200A02_DDR4_POWER,\n+\t/* NIM power consumption (calculated value) - does not generate alarms */\n+\tNT_SENSOR_NT200A02_NIM_POWER,\n+\n+\tNT_SENSOR_NT200A02_L1_MAX, /* Number of NT200A01 level 0,1 board sensors */\n+};\n+\n+#endif\n",
    "prefixes": [
        "v3",
        "3/8"
    ]
}