From nobody Sat Sep 26 20:50:59 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=axiado.com) ARC-Seal: i=2; a=rsa-sha256; t=1789540185; cv=pass; d=zohomail.com; s=zohoarc; b=C9t5rp58UzHS5d+cnHQmE7NKd3wk8cBL6zza16a/TJcdc1PJKhBngWPOpp6iQ8sBTUbrQDgvEp6UunW4SuWl15P6y4WmUGopWjWmhPOR2hEau8rkvsAUM7SQcdzo/XC28hCsCYP3ikHvUvWVyTn+OWv35TGKWImxKLJCOn5X/vw= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1789540185; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=xiDhdmbaDFIlrc94+300DmNttsQiYDDWCC7hduCoYBo=; b=Tfq3aEw/JLOZTEmQKeIKMk9kNdJPzBUHb661kxOoPY4M7yZGg9JdUJ5AtlHfDiOyavObVCHMnDPivmFM5Fo/vNMk/1guSmwoL/cYtnwzEcQ0yfYaEZNh6YWsnQVoMKlJ12mv62yF8d8K1Hpjrb3Ww4RX4fzLUWf0uSMC1KzFTQQ= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=axiado.com) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1789540185416418.9388271992568; Tue, 15 Sep 2026 23:29:45 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x6j9B-0006cN-DR; Wed, 16 Sep 2026 02:29:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x6hcw-0002Eh-GM; Wed, 16 Sep 2026 00:52:12 -0400 Received: from mail-westusazlp170100001.outbound.protection.outlook.com ([2a01:111:f403:c000::1] helo=BYAPR05CU005.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x6hcr-0001X7-OB; Wed, 16 Sep 2026 00:52:10 -0400 Received: from BY1P220CA0049.NAMP220.PROD.OUTLOOK.COM (2603:10b6:a03:59e::15) by MN0PR18MB5920.namprd18.prod.outlook.com (2603:10b6:208:4a7::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.428.9; Wed, 16 Sep 2026 04:51:51 +0000 Received: from CO1PEPF00012E65.namprd05.prod.outlook.com (2603:10b6:a03:59e:cafe::8c) by BY1P220CA0049.outlook.office365.com (2603:10b6:a03:59e::15) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.21.406.12 via Frontend Transport; Wed, 16 Sep 2026 04:51:51 +0000 Received: from smtp.corp.axiado.com (64.62.143.114) by CO1PEPF00012E65.mail.protection.outlook.com (10.167.249.74) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.21.428.7 via Frontend Transport; Wed, 16 Sep 2026 04:51:51 +0000 Received: from axiado.com (unknown [10.4.1.32]) by smtp.corp.axiado.com (Postfix) with ESMTP id B5F7A4186B53; Tue, 15 Sep 2026 21:51:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=RjSAx+qx2mFziHUFIRkHFiH1weMwA8koNnHKiREOTu1et+czpCebjkz8jV4gd8wCZdHWtfixIkW2yJQPQhYPB5KfIqiP7WvF/h3oYa45dPr4UQs8TPMcnt4/W50Wv7junV/fyy+TuXVIQ/261msUyFWOR+2sRkWmAZrT7WwCNfFOETQbIWOFj7DS1vpJ/Q+9G6aKBjFygGAXeOkRAY8o9FVo4OpfY6pXn4KH7gydxTGCw54Jy0qCGRM01T8KWPzccn6FnYbVvKQ9+PG+10JDKO18+96LRSY8S2Jgi7uWJ3hu3QFthanm0/M8adVdMR+QumNiOT7aucwq9VySpqxw4w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=xiDhdmbaDFIlrc94+300DmNttsQiYDDWCC7hduCoYBo=; b=c4thx4RgI/DVafWQH7wxAJt53sdX0LY9aTjhL/OmvxX9/xQAJQmF3fgPi0ksjYFAkzqEv8VcEFo1Zi53PVysOssKZiIA1F2yGASNdkN+lLkGop/0SQ7Svx+8SU0V1j/mt4/WUVSVzRMbVs8MfkrIwid7Q+IviVO47ha40NrTs263OhZ8rST8um7/v5/8ihlo4qUfyQJ5HW6Wt1JTi+ZP4g1+LgHACunF0IQ0Y0cjThL78JLq09YQ7kNKU4ld2+uencDrSsKXosQuDqmb0ydXo3ccxtUgDW+GVxgnfSZhvaWa//8nmPFAQ+HrxTJPD6J3cTecD8A/NGOEcXkWbF4p1Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 64.62.143.114) smtp.rcpttodomain=axiado.com smtp.mailfrom=axiado.com; dmarc=none action=none header.from=axiado.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=axiado.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=xiDhdmbaDFIlrc94+300DmNttsQiYDDWCC7hduCoYBo=; b=nMZBqVRoN0k6YxKuOJ30k2ZF4uAGcpd/a2x+6k7xSXnfDgysVa09pSCe5+riR++8Sj3Om27cj92AQEqbzMUBlU3kzZxSpmgJM70PlBSLrY3omrNUCf7ugf91b8uf1go7kZQTpUWSo0gl6q+S7gaZC8rww76rLSw//ZYOGNmX14sMeI02il5gJwt8VEbEql0VGPNlyoAtcG6oJNrObceKkkZmo6CrLuDRKNAmD7YlWw1ZLUKLoHfTCr4eBNWrSFAwdx75ZL5TlEF0MOQ/ggiUPenPqEBD3tiDJWOtnvINxh/EAzl0QcGkh0cQhGTSjrnUo8zjGSIzrJTHGZ+Ah277zA== X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 64.62.143.114) smtp.mailfrom=axiado.com; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=axiado.com; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: Fail (protection.outlook.com: domain of axiado.com does not designate 64.62.143.114 as permitted sender) receiver=protection.outlook.com; client-ip=64.62.143.114; helo=smtp.corp.axiado.com; From: Ratan Lal Dondi To: qemu-devel@nongnu.org, qemu-arm@nongnu.org Cc: pbonzini@redhat.com, peter.maydell@linaro.org, jasowangio@gmail.com, kchiu@axiado.com, Ratan Lal Dondi Subject: [PATCH 1/2] hw/net: Add Axiado HCP network device model Date: Tue, 15 Sep 2026 21:51:33 -0700 Message-Id: <20260916045134.1687145-2-rdondi@axiado.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260916045134.1687145-1-rdondi@axiado.com> References: <20260916045134.1687145-1-rdondi@axiado.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CO1PEPF00012E65:EE_|MN0PR18MB5920:EE_ X-MS-Office365-Filtering-Correlation-Id: ecadc97e-d90b-41cd-c9a1-08df13ae3915 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|82310400026|36860700016|376014|23010399003|1800799024|6133799003|22082099003|18002099003|56012099006|5023799004|10067099003|3023799007; X-Microsoft-Antispam-Message-Info: SYRt5HNVPpBARk55u8SeY8QdVg9l2uS4aWh9gxzXUyAufSdsnqm7FUACuXWVUHc7HQctyTIUn5RZskx5MdPsb9ARIxu/RdNVlj/0O6qIdHIpCDrtk7rHQwg0tq1fwS1pKrCG5RgPIat/L/qvvtHIS1OIzoGypGGYWeFMlnRY5bScSLae39Nj8WH/jqtcC/ihMu/UzN+hMsWx6dicJ6Id0BPdPlztc/azkOc+84RGxMwGJ9Wc8w5qoo0NNt2CDJrn7JA7Zp7Czl3zQ0fm2yM5O2lJcL7c1Nt+V2oh8qDvYKfxk62ekFrvK71vXpMc0Ex2OESkb9WKl9+vK6gYtCPVOUkPifNqb0R8itpzZdO6/LhfDElNb8AopXr/gnfhAhvHwLOsVp4aDRaXzcnjuZdp3zbZz+qjAYCyFVneoTIkfws3MFECGIZNel0RhwgDimxqyzducFA/mI/sjOk6K1so25nBgeifUOncJVZQgLgWBrMFrx1QZ5fIAh4lK1NkDRkXANMAnWAsVfQHsjxQ/FrNfYijn9XqKppvrMQ/ZV8ceq27zwZTXlu3otZaMUSMQoy8ur940/nXri61NMIbX5tnaPwIpDllZWTjh2hd0XswaLGdbxZC7648afBjLNTyzgQx99PBPuzCtFt6lvpWuTDuLKF5ZulB2rnFOXDPDsAQ2kVdAKnJf4LZvhc+icwAl2r+GIiYgAugeHMz0No9HkVoFg== X-Forefront-Antispam-Report: CIP:64.62.143.114; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:smtp.corp.axiado.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(82310400026)(36860700016)(376014)(23010399003)(1800799024)(6133799003)(22082099003)(18002099003)(56012099006)(5023799004)(10067099003)(3023799007); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: zzxN8TeCbfW7iXjn0132ohtwuAracPgKy+eTO6cnWxD11DQr9fFQibFLzgrYJ3863IHDAFWD8acXgb3acvdYNVmVTmuEHk80slmD9KDBTN1qDnauQl6QOzYXMSi9z58voE1OvEA+2kk9IMUa7qdQ1TSDBUar9E7QT40snqrfTsCXiGIOVEQSVNhWFB05GF6rY/KKJlzOsUCp+EdAzJNh1FHHUrsN4KbouEmMWRTvxLokta7P9/9Oe+u8poAxIeaA0gVkryujUmCDCFGR+68hFFDfetqSn78+l66TDnhOepXMyRLrrNCmhDVufxPiqxQvbtbkzM3UIg60KC+mLxLqp7mIw2X05eov9OWla0fgOuKZxuDV2qCjAzrMMphjqMJQUm9pJQRYeD7p/4397Taqmoj33yV7Ua3Uc4Um9COcOZvv04rJbHZo51x0Hn2Mlkud X-OriginatorOrg: axiado.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Sep 2026 04:51:51.1987 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: ecadc97e-d90b-41cd-c9a1-08df13ae3915 X-MS-Exchange-CrossTenant-Id: ff2db17c-4338-408e-9036-2dee8e3e17d7 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=ff2db17c-4338-408e-9036-2dee8e3e17d7; Ip=[64.62.143.114]; Helo=[smtp.corp.axiado.com] X-MS-Exchange-CrossTenant-AuthSource: CO1PEPF00012E65.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN0PR18MB5920 Received-SPF: pass client-ip=2a01:111:f403:c000::1; envelope-from=rdondi@axiado.com; helo=BYAPR05CU005.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 16 Sep 2026 02:28:02 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @axiado.com) X-ZM-MESSAGEID: 1789540186417158500 The HCP is the network complex of the Axiado AX3000 SoC. It exposes three MMIO regions: - EIP-197: an Inside Secure SafeXcel packet engine. It is the bus master for all packet I/O, driven by four command/result descriptor ring pairs (CDR/RDR). Packets are routed to and from one of five MACs by an "application ID" tag carried in the EIP-96 input and output tokens. - SHIM: MAC configuration, per-MAC MDIO and statistics. Configuration only; no packet data flows through it. - PHY CSR: PCS/SerDes configuration. The EIP-197 cryptographic transforms are not modelled; descriptors are treated as pass-through, which is what the guest asks for when the block is used purely as a network interface. The MACs are exposed as "netdev0" to "netdev4" and "mac0" to "mac4" properties, each with its own virtual PHY on that MAC's MDIO bus. Signed-off-by: Ratan Lal Dondi --- hw/net/Kconfig | 3 + hw/net/axiado_hcp.c | 1372 +++++++++++++++++++++++++++++++++++ hw/net/meson.build | 2 + hw/net/trace-events | 6 + include/hw/net/axiado_hcp.h | 172 +++++ 5 files changed, 1555 insertions(+) create mode 100644 hw/net/axiado_hcp.c create mode 100644 include/hw/net/axiado_hcp.h diff --git a/hw/net/Kconfig b/hw/net/Kconfig index b56a173eed..a0b91bea03 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -162,3 +162,6 @@ config CAN_FLEXCAN bool depends on IMX select CAN_BUS + +config AXIADO_HCP + bool diff --git a/hw/net/axiado_hcp.c b/hw/net/axiado_hcp.c new file mode 100644 index 0000000000..566e4ca780 --- /dev/null +++ b/hw/net/axiado_hcp.c @@ -0,0 +1,1372 @@ +/* + * Axiado HCP (Header & Crypto Processing) block - QEMU device model + * + * The HCP is the network complex of the Axiado AX3000 SoC. It exposes thr= ee + * MMIO regions: + * + * - EIP-197: an Inside Secure SafeXcel packet engine. It is the bus mas= ter + * for all packet I/O, driven by four command/result descriptor ring p= airs + * (CDR/RDR). Packets are routed to and from one of five MACs by an + * "application ID" tag carried in the EIP-96 input/output tokens. + * - SHIM: MAC configuration, per-MAC MDIO and statistics. Config only; = no + * packet data flows through it. + * - PHY CSR: PCS/SerDes configuration. + * + * Only the packet datapath is modelled. The EIP-197's cryptographic + * transforms are not implemented: descriptors are treated as plain + * pass-through, which is what the guest driver requests for network traff= ic. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2026 Ratan Lal Dondi + */ + +#include "qemu/osdep.h" +#include "hw/net/axiado_hcp.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev-properties-system.h" +#include "hw/core/irq.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "system/dma.h" +#include "system/address-spaces.h" +#include "trace.h" + +/* + * MAC configuration region, within the SHIM MMIO area + */ + +#define MAC_R_REVISION 0x000 +#define MAC_R_SCRATCH 0x004 +#define MAC_R_COMMAND_CONFIG 0x008 +#define MAC_R_MAC_0 0x00c +#define MAC_R_MAC_1 0x010 +#define MAC_R_FRM_LENGTH 0x014 +#define MAC_R_RX_FIFO_SECTIONS 0x01c +#define MAC_R_TX_FIFO_SECTIONS 0x020 +#define MAC_R_MDIO_CFG_STATUS 0x030 +#define MAC_R_MDIO_COMMAND 0x034 +#define MAC_R_MDIO_DATA 0x038 +#define MAC_R_MDIO_REGADDR 0x03c +#define MAC_R_STATUS 0x040 +#define MAC_R_IF_MODE 0x300 +#define MAC_R_PCS_STATUS 0x304 + +#define MAC_REVISION_VAL 0x10136 + +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 + +#define MDIO_COMMAND_READ_FLAG BIT(15) + +#define SHIM_REG_FIFO_BASE 0x040 +#define SHIM_REG_STATS_BASE 0x060 + +#define FIFO_BIT_RX_RST 0 +#define FIFO_BIT_RX_EMPTY 2 +#define FIFO_BIT_RX_OVFLOW 3 +#define FIFO_BIT_TX_RST 4 +#define FIFO_BIT_TX_EMPTY 6 +#define FIFO_BIT_TX_OVFLOW 7 + +/* + * EIP-207 input classification engine, at offsets from the packet engine = base + * within the EIP-197 region. + */ +#define PE_ICE_SCRATCH_RAM 0x00800 +#define PE_ICE_PUE_CTRL 0x00c80 +#define PE_ICE_FPP_CTRL 0x00d80 + +/* + * Administration RAM, at byte offsets from the ICE scratch RAM base. The + * classification firmware publishes its version here and sets bit 0 of the + * matching control word to say it has done so. + */ +#define ICE_ADMIN_IPUE_VERSION 0x00 +#define ICE_ADMIN_IFPP_VERSION 0x08 +#define ICE_ADMIN_IPUE_CTRL 0x14 +#define ICE_ADMIN_IFPP_CTRL 0x18 +#define ICE_ADMIN_VERSION_UPDATED (1u << 0) + +/* Administration RAM byte offset to an index into the packet engine windo= w. */ +#define ICE_ADMIN_WORD(off) ((PE_ICE_SCRATCH_RAM + (off)) / 4) + +/* Version word: major in bits 11..8, minor in 7..4, patch level in 3..0. = */ +#define ICE_FW_VERSION_FIELD 0xf +#define ICE_FW_VERSION_MAJOR_SH 8 +#define ICE_FW_VERSION_MINOR_SH 4 +#define ICE_FW_VERSION_PATCH_SH 0 + +#define ICE_FW_VERSION(maj, min, patch) = \ + ((((maj) & ICE_FW_VERSION_FIELD) << ICE_FW_VERSION_MAJOR_SH) | = \ + (((min) & ICE_FW_VERSION_FIELD) << ICE_FW_VERSION_MINOR_SH) | = \ + (((patch) & ICE_FW_VERSION_FIELD) << ICE_FW_VERSION_PATCH_SH)) + +/* + * The version reported to the guest, which compares it against the versio= n of + * the classification firmware image it has just downloaded. The two have = to + * agree: report anything else and classification init fails outright. Upd= ate + * this when the guest ships a different classification firmware. + */ +#define ICE_FIRMWARE_VERSION ICE_FW_VERSION(3, 3, 1) + +static void mac_mdio_execute(HCPMac *m) +{ + uint32_t cmd =3D m->mdio_command; + uint8_t reg =3D m->mdio_regaddr ? (m->mdio_regaddr & 0x1f) + : (cmd & 0x1f); + + if (cmd & MDIO_COMMAND_READ_FLAG) { + m->mdio_data =3D m->mii_regs[reg]; + } else { + m->mii_regs[reg] =3D m->mdio_data & 0xffff; + } +} + +static uint64_t mac_block_read(HCPMac *m, hwaddr off) +{ + switch (off) { + case MAC_R_REVISION: return MAC_REVISION_VAL; + case MAC_R_COMMAND_CONFIG: return m->command_config; + case MAC_R_MAC_0: return m->mac_addr_lo; + case MAC_R_MAC_1: return m->mac_addr_hi; + case MAC_R_FRM_LENGTH: return m->frm_length; + case MAC_R_RX_FIFO_SECTIONS: return m->rx_fifo_sections; + case MAC_R_TX_FIFO_SECTIONS: return m->tx_fifo_sections; + case MAC_R_MDIO_CFG_STATUS: return 0; + case MAC_R_MDIO_COMMAND: return m->mdio_command; + case MAC_R_MDIO_DATA: return m->mdio_data; + case MAC_R_MDIO_REGADDR: return m->mdio_regaddr; + case MAC_R_STATUS: return 0; + case MAC_R_IF_MODE: return m->if_mode; + case MAC_R_PCS_STATUS: return 0x0084; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: MAC reg read offset 0x%" HWADDR_PRIx "\= n", + off); + return 0; + } +} + +static void mac_block_write(HCPMac *m, hwaddr off, uint64_t val) +{ + switch (off) { + case MAC_R_COMMAND_CONFIG: + m->command_config =3D val; + break; + case MAC_R_MAC_0: + m->mac_addr_lo =3D val; + break; + case MAC_R_MAC_1: + m->mac_addr_hi =3D val; + break; + case MAC_R_FRM_LENGTH: + m->frm_length =3D val; + break; + case MAC_R_RX_FIFO_SECTIONS: + m->rx_fifo_sections =3D val; + break; + case MAC_R_TX_FIFO_SECTIONS: + m->tx_fifo_sections =3D val; + break; + case MAC_R_IF_MODE: + m->if_mode =3D val; + break; + case MAC_R_MDIO_COMMAND: + m->mdio_command =3D val; + mac_mdio_execute(m); + break; + case MAC_R_MDIO_DATA: + m->mdio_data =3D val & 0xffff; + break; + case MAC_R_MDIO_REGADDR: + m->mdio_regaddr =3D val; + break; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: MAC reg write offset 0x%" HWADDR_PRIx + " =3D 0x%" PRIx64 "\n", off, val); + break; + } +} + +static uint64_t shim_global_read(AXIADOHCPState *s, hwaddr addr) +{ + if (addr >=3D SHIM_REG_FIFO_BASE && + addr < SHIM_REG_FIFO_BASE + HCP_NUM_MACS * 4) { + return (1U << FIFO_BIT_TX_EMPTY) | (1U << FIFO_BIT_RX_EMPTY); + } + if (addr >=3D SHIM_REG_STATS_BASE && + addr < SHIM_REG_STATS_BASE + HCP_NUM_MACS * 4) { + return 0; + } + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: shim global read 0x%" HWADDR_PRIx "\n", add= r); + return 0; +} + +static void shim_global_write(AXIADOHCPState *s, hwaddr addr, uint64_t val) +{ + if (addr >=3D SHIM_REG_FIFO_BASE && + addr < SHIM_REG_FIFO_BASE + HCP_NUM_MACS * 4) { + return; + } + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: shim global write 0x%" HWADDR_PRIx + " =3D 0x%" PRIx64 "\n", addr, val); +} + +static int shim_mac_index(hwaddr addr, hwaddr *off_out) +{ + if (addr < HCP_SHIM_MAC_BASE_OFFSET) { + return -1; + } + hwaddr rel =3D addr - HCP_SHIM_MAC_BASE_OFFSET; + int idx =3D rel / HCP_SHIM_MAC_STRIDE; + if (idx >=3D HCP_NUM_MACS) { + return -1; + } + *off_out =3D rel % HCP_SHIM_MAC_STRIDE; + return idx; +} + +static uint64_t axiado_shim_read(void *opaque, hwaddr addr, unsigned size) +{ + AXIADOHCPState *s =3D AXIADO_HCP(opaque); + hwaddr off; + int idx =3D shim_mac_index(addr, &off); + + if (idx >=3D 0) { + return mac_block_read(&s->macs[idx], off); + } + return shim_global_read(s, addr); +} + +static void axiado_shim_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + AXIADOHCPState *s =3D AXIADO_HCP(opaque); + hwaddr off; + int idx =3D shim_mac_index(addr, &off); + + if (idx >=3D 0) { + mac_block_write(&s->macs[idx], off, val); + return; + } + shim_global_write(s, addr, val); +} + +static const MemoryRegionOps shim_ops =3D { + .read =3D axiado_shim_read, + .write =3D axiado_shim_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .impl.min_access_size =3D 4, + .impl.max_access_size =3D 4, +}; + +/* + * EIP-197 region, SafeXcel register layout + */ + +#define XDR_BASE_LO 0x00 +#define XDR_BASE_HI 0x04 +#define XDR_SIZE 0x18 +#define XDR_DESC_SIZE 0x1c +#define XDR_CFG 0x20 +#define XDR_DMA_CFG 0x24 +#define XDR_THRESH 0x28 +#define XDR_PREP_COUNT 0x2c +#define XDR_PROC_COUNT 0x30 +#define XDR_PREP_PNTR 0x34 +#define XDR_PROC_PNTR 0x38 +#define XDR_STAT 0x3c + +#define XDR_STAT_PROC_THRESH_IRQ 0x10 + +/* + * Bits 16..27 of xDR_STAT report the descriptor FIFO size in 32-bit words. + * The guest reads it at ring init to check that the descriptor size, fetch + * size and threshold it wants to program all fit. 256 words leaves headro= om + * well past any combination the guest asks for. + */ +#define XDR_STAT_FIFO_SIZE_WORDS 256u +#define XDR_STAT_FIFO_SIZE_BITS ((XDR_STAT_FIFO_SIZE_WORDS & 0xFFFu) << = 16) + +#define EIP_HIA_XDR_BASE 0x80000 +#define EIP_HIA_XDR_STRIDE 0x1000 +#define EIP_HIA_RDR_OFF 0x800 + +#define EIP_HIA_AIC_R_RING0_BASE 0x9E000 +#define EIP_AIC_R_CDR_ENABLE_CTRL 0x008 +#define EIP_AIC_R_CDR_ENABLED 0x010 +#define EIP_AIC_R_CDR_ENABLE_CLR 0x014 +#define EIP_AIC_R_RDR_ENABLE_CTRL 0x808 +#define EIP_AIC_R_RDR_ENABLED 0x810 +#define EIP_AIC_R_RDR_ENABLE_CLR 0x814 + +#define EIP_HIA_AIC_G_BASE 0x9F000 +#define EIP_AIC_G_ENABLE_CTRL 0x808 +#define EIP_AIC_G_ENABLED 0x810 +#define EIP_AIC_G_ENABLE_CLR 0x814 + +/* SafeXcel command descriptor - field offsets used here (little-endian). = */ +#define CMD_DESC_FLAGS_OFF 0 /* particle_size:17, ..., flags = */ +#define CMD_DESC_DATA_LO_OFF 8 +#define CMD_DESC_DATA_HI_OFF 12 +#define CMD_DESC_APP_ID_OFF 28 /* EIP-96 input token, app_id <<= 9 */ + +/* SafeXcel result descriptor word-0 bit layout */ +#define RES_DESC_FLAGS_OFF 0 +#define RES_DESC_PARTICLE_MASK 0x1FFFFu +#define RES_DESC_LAST_SEG_BIT (1u << 22) +#define RES_DESC_FIRST_SEG_BIT (1u << 23) +#define RES_DESC_DATA_LO_OFF 8 +#define RES_DESC_DATA_HI_OFF 12 + +#define MAX_PKT_LEN 9600 + +/* + * DESC_SIZE register encoding: + * bits 0..7 =3D dscr_word_count (data payload size, words) + * bits 16..23 =3D dscr_offs_word_count (per-slot stride, words) + * bits 29..31 =3D flags (atp_to_token / atp / 64-bit) + * The stride between descriptor slots in ring memory is the OFFSET field,= not + * the payload size. Returns 0 if the guest has not programmed the registe= r, + * which every caller checks before using it as a divisor. + */ +static uint32_t xdr_stride_bytes(HCPxDR *x) +{ + uint32_t offs_words =3D (x->desc_size >> 16) & 0xFFu; + return offs_words * 4u; +} + +/* + * Drive the per-ring IRQ line from (STAT & AIC mask). The line is + * level-triggered, so it must be recomputed - and deasserted - as soon as= the + * guest acks a STAT bit by writing it back, otherwise the interrupt + * controller re-fires forever. + */ +static void hcp_update_ring_irq(AXIADOHCPState *s, int r) +{ + bool level =3D false; + + if (s->rings[r].aic_rdr_mask && + (s->rings[r].rdr.stat & XDR_STAT_PROC_THRESH_IRQ)) { + level =3D true; + } + if (s->rings[r].aic_cdr_mask && + (s->rings[r].cdr.stat & XDR_STAT_PROC_THRESH_IRQ)) { + level =3D true; + } + qemu_set_irq(s->irqs[HCP_IRQ_RING_BASE + r], level); +} + +static int app_id_to_mac_index(AXIADOHCPState *s, uint16_t app_id) +{ + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + if (s->macs[i].app_id =3D=3D app_id) { + return i; + } + } + return -1; +} + +/* + * Process all newly-prepared CDR descriptors for ring r. For each: DMA-re= ad + * the command descriptor, extract the routing app_id, DMA-read the packet, + * and emit it on the matching MAC's NIC backend. + * + * No result descriptor is posted for a transmit. The engine and the guest + * share one RDR per ring for both transmit completions and receive result= s; + * the guest tracks transmit buffer reuse from the CDR consumed count alon= e, + * so writing transmit completions would only corrupt receive slots. + */ +static void hcp_process_tx_ring(AXIADOHCPState *s, int r) +{ + HCPxDR *cdr =3D &s->rings[r].cdr; + uint32_t stride =3D xdr_stride_bytes(cdr); + + if (stride =3D=3D 0 || cdr->size =3D=3D 0 || cdr->base =3D=3D 0) { + return; + } + uint32_t ring_descs =3D cdr->size / stride; + if (ring_descs =3D=3D 0) { + return; + } + + while (cdr->proc_count < cdr->prep_count) { + uint32_t idx =3D (cdr->proc_count / stride) % ring_descs; + hwaddr desc_addr =3D cdr->base + (hwaddr)idx * stride; + + uint8_t desc[64]; + size_t read_bytes =3D MIN(stride, (uint32_t)sizeof(desc)); + if (dma_memory_read(&address_space_memory, desc_addr, desc, read_b= ytes, + MEMTXATTRS_UNSPECIFIED) !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "axiado-hcp: CDR DMA read failed at 0x%" HWADDR_= PRIx + "\n", desc_addr); + break; + } + + uint32_t flags =3D ldl_le_p(&desc[CMD_DESC_FLAGS_OFF]); + uint32_t pkt_len =3D flags & 0x1FFFFu; + uint64_t data_addr =3D + (uint64_t)ldl_le_p(&desc[CMD_DESC_DATA_LO_OFF]) + | ((uint64_t)ldl_le_p(&desc[CMD_DESC_DATA_HI_OFF]) << 32); + /* + * The routing app_id is not a plain descriptor field: it lives in= the + * EIP-96 input token as (app_id << 9). + */ + uint16_t raw_token =3D lduw_le_p(&desc[CMD_DESC_APP_ID_OFF]); + uint16_t app_id =3D (raw_token >> 9) & 0x7Fu; + + int mac_idx =3D app_id_to_mac_index(s, app_id); + if (mac_idx < 0 || pkt_len =3D=3D 0 || pkt_len > MAX_PKT_LEN) { + /* + * An all-zero descriptor is an unused slot, not an error. Only + * report slots that carry data we could not route. + */ + if (pkt_len !=3D 0 || raw_token !=3D 0) { + trace_axiado_hcp_tx_unroutable(r, app_id, pkt_len, data_ad= dr); + } + } else { + g_autofree uint8_t *pkt =3D g_malloc(pkt_len); + if (dma_memory_read(&address_space_memory, data_addr, pkt, pkt= _len, + MEMTXATTRS_UNSPECIFIED) =3D=3D MEMTX_OK) { + HCPMac *mac =3D &s->macs[mac_idx]; + + trace_axiado_hcp_tx_packet(r, app_id, mac_idx, pkt_len); + if (mac->nic) { + qemu_send_packet(qemu_get_queue(mac->nic), pkt, pkt_le= n); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "axiado-hcp: packet DMA read failed at 0x%" + PRIx64 "\n", data_addr); + } + } + + cdr->proc_count +=3D stride; + cdr->proc_pntr =3D (cdr->proc_pntr + stride) % cdr->size; + } +} + +static uint64_t eip_xdr_read(HCPxDR *x, hwaddr off) +{ + switch (off) { + case XDR_BASE_LO: return x->base & 0xFFFFFFFFu; + case XDR_BASE_HI: return (x->base >> 32) & 0xFFFFFFFFu; + case XDR_SIZE: return x->size; + case XDR_DESC_SIZE: return x->desc_size; + case XDR_CFG: return x->cfg; + case XDR_DMA_CFG: return x->dma_cfg; + case XDR_THRESH: return x->thresh; + case XDR_PREP_COUNT: + /* + * This register is asymmetric. A write means "N more descriptors = have + * been prepared", but a read returns the ring's current fill leve= l: + * the guest recovers a descriptor count as + * + * ((reg >> 2) & 0x3fffff) / dscr_offs_word_count + * + * and uses it to decide how much room is left in the ring. Report= ing + * the cumulative prepared count instead would make that fill level + * grow without bound, and the guest would eventually see the ring= as + * permanently full and stop transmitting for good. + * + * Both counters are byte cursors and the guest's >> 2 undoes the = << 2 + * applied on write, so the outstanding byte count is the register + * value directly. Transmit descriptors are consumed synchronously= , so + * this reads back as empty once a transmit has been processed. + */ + return x->prep_count > x->proc_count + ? x->prep_count - x->proc_count : 0; + case XDR_PROC_COUNT: return x->proc_count; + case XDR_PREP_PNTR: return x->prep_pntr; + case XDR_PROC_PNTR: return x->proc_pntr; + case XDR_STAT: return x->stat | XDR_STAT_FIFO_SIZE_BITS; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip xdr read offset 0x%" HWADDR_PRIx "\= n", + off); + return 0; + } +} + +static int eip_xdr_ring(hwaddr addr, hwaddr *off_out, bool *is_rdr_out) +{ + if (addr < EIP_HIA_XDR_BASE || + addr >=3D EIP_HIA_XDR_BASE + HCP_NUM_RINGS * EIP_HIA_XDR_STRIDE) { + return -1; + } + hwaddr rel =3D addr - EIP_HIA_XDR_BASE; + int r =3D rel / EIP_HIA_XDR_STRIDE; + hwaddr intra =3D rel % EIP_HIA_XDR_STRIDE; + *is_rdr_out =3D (intra >=3D EIP_HIA_RDR_OFF); + *off_out =3D (*is_rdr_out ? (intra - EIP_HIA_RDR_OFF) : intra) & 0x3F; + return r; +} + +/* + * xDR write. Returns true if this write was a CDR PREP_COUNT bump that ne= eds + * follow-up TX processing (caller invokes hcp_process_tx_ring afterward). + */ +static bool eip_xdr_write(HCPxDR *x, hwaddr off, uint64_t val) +{ + switch (off) { + case XDR_BASE_LO: + x->base =3D (x->base & ~0xFFFFFFFFULL) | (val & 0xFFFFFFFFu); + return false; + case XDR_BASE_HI: + x->base =3D (x->base & 0xFFFFFFFFULL) | ((val & 0xFFFFFFFFu) << 32= ); + return false; + case XDR_SIZE: + x->size =3D val; + return false; + case XDR_DESC_SIZE: + x->desc_size =3D val; + return false; + case XDR_CFG: + x->cfg =3D val; + return false; + case XDR_DMA_CFG: + x->dma_cfg =3D val; + return false; + case XDR_THRESH: + x->thresh =3D val; + return false; + case XDR_PREP_COUNT: { + /* + * Write layout: + * bit 31 =3D clear-count flag, reset rather than add + * bits 2.. =3D added descriptor word count, i.e. a byte count, = since + * the per-slot stride is dscr_offs_word_count * 4 + * The guest issues a clear at ring init; treating that as an incr= ement + * would leave the ring permanently reporting millions of pending + * descriptors. + */ + uint32_t add =3D val & 0x7FFFFFFFu; + if (val & 0x80000000u) { + x->prep_count =3D add; + x->prep_pntr =3D (x->size > 0) ? (add % x->size) : 0; + } else { + x->prep_count +=3D add; + if (x->size > 0) { + x->prep_pntr =3D (x->prep_pntr + add) % x->size; + } + } + return true; + } + case XDR_PROC_COUNT: + /* + * In ownership-word mode this write only acknowledges results the + * guest has already read; the ownership word is the real handshak= e. + * It must not clobber proc_count, which is the engine-side slot + * cursor and only advances when a slot is actually consumed or + * produced. + */ + return false; + case XDR_PREP_PNTR: + x->prep_pntr =3D val; + return false; + case XDR_PROC_PNTR: + x->proc_pntr =3D val; + return false; + case XDR_STAT: + if (val & XDR_STAT_PROC_THRESH_IRQ) { + x->stat &=3D ~XDR_STAT_PROC_THRESH_IRQ; + } + return false; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip xdr write offset 0x%" HWADDR_PRIx + " =3D 0x%" PRIx64 "\n", off, val); + return false; + } +} + +/* + * Complete the classification firmware version handshake. + * + * Having downloaded the firmware, the guest starts a micro-engine in debug + * mode at a fixed program counter and polls the administration RAM for a + * version word that the running microcode would have written. No microcode + * executes here, so the model publishes the version itself: a write to ei= ther + * engine's control register makes that engine's version word and its upda= ted + * flag appear, and the guest's poll succeeds on its first read. + * + * Without this the guest cannot complete classification init, and the only + * other way past it is to disable the check in the guest - which a device + * model should never require. + */ +static void hcp_ice_publish_fw_version(AXIADOHCPState *s, hwaddr ctrl_off) +{ + unsigned version, ctrl; + + QEMU_BUILD_BUG_ON(ICE_ADMIN_WORD(ICE_ADMIN_IFPP_CTRL) >=3D HCP_EIP_PE_= REGS); + + if (ctrl_off =3D=3D PE_ICE_FPP_CTRL) { + version =3D ICE_ADMIN_WORD(ICE_ADMIN_IFPP_VERSION); + ctrl =3D ICE_ADMIN_WORD(ICE_ADMIN_IFPP_CTRL); + } else { + version =3D ICE_ADMIN_WORD(ICE_ADMIN_IPUE_VERSION); + ctrl =3D ICE_ADMIN_WORD(ICE_ADMIN_IPUE_CTRL); + } + + s->pe_regs[version] =3D ICE_FIRMWARE_VERSION; + s->pe_regs[ctrl] |=3D ICE_ADMIN_VERSION_UPDATED; +} + +/* + * Locate the backing word for a register inside one of the windows the gu= est + * writes and reads back, or NULL if this address is not in any of them. + * Registers outside the windows are either write-only or read-only status, + * and need no storage. + */ +static uint32_t *eip_reg_slot(AXIADOHCPState *s, hwaddr addr) +{ + struct { + hwaddr base; + unsigned regs; + uint32_t *store; + } const windows[] =3D { + { HCP_EIP_FLUE_FHT_BASE, HCP_EIP_FLUE_FHT_REGS, s->flue_fht }, + { HCP_EIP_PE_BASE, HCP_EIP_PE_REGS, s->pe_regs }, + { HCP_EIP_TRC_RAM_BASE, HCP_EIP_TRC_RAM_REGS, s->trc_ram }, + { HCP_EIP_CLS_BASE, HCP_EIP_CLS_REGS, s->cls_regs }, + { HCP_EIP_FLOW_BASE, HCP_EIP_FLOW_REGS, s->flow_regs }, + { HCP_EIP_CACHE_BASE, HCP_EIP_CACHE_REGS, s->cache_regs }, + }; + + for (int i =3D 0; i < ARRAY_SIZE(windows); i++) { + hwaddr base =3D windows[i].base; + + if (addr >=3D base && addr < base + windows[i].regs * 4) { + return &windows[i].store[(addr - base) / 4]; + } + } + return NULL; +} + +static int eip_aic_r_ring(hwaddr addr, hwaddr *off_out) +{ + hwaddr lo =3D EIP_HIA_AIC_R_RING0_BASE - + (HCP_NUM_RINGS - 1) * EIP_HIA_XDR_STRIDE; + if (addr < lo || addr > EIP_HIA_AIC_R_RING0_BASE + 0xFFF) { + return -1; + } + hwaddr block_base =3D addr & ~0xFFFULL; + int r =3D (EIP_HIA_AIC_R_RING0_BASE - block_base) / EIP_HIA_XDR_STRIDE; + if (r < 0 || r >=3D HCP_NUM_RINGS) { + return -1; + } + *off_out =3D addr & 0xFFF; + return r; +} + +static uint64_t axiado_eip197_read(void *opaque, hwaddr addr, unsigned siz= e) +{ + AXIADOHCPState *s =3D AXIADO_HCP(opaque); + uint32_t *slot; + hwaddr off; + bool is_rdr; + int r; + + /* + * EIP-2xx identification registers, matched before the AIC range disp= atch + * so they take precedence over it. Each sub-block has a version regis= ter + * whose low 16 bits hold a fixed signature and whose high 16 bits enc= ode + * the revision, plus one or two options registers describing the + * capabilities the guest may use. + */ + switch (addr) { + /* + * EIP-202 host interface adapter, signature 0x35ca. + * 0x9fff0 =3D OPTIONS2: lookaside interfaces (0..3), inline interfa= ces + * (4..7), AXI write channels (16..19), read clusters (20.= .27) + * 0x9fff4 =3D MST_CTRL, answered from the scratchpad + * 0x9fff8 =3D OPTIONS: rings (0..3), packet engines (4..8), command= and + * result FIFO sizes (9..14), host interface (16..19), DMA + * length (20..24), host data width (25..27), target align= ment + * (28..30), 64-bit addressing (31) + * 0x9fffc =3D VERSION + */ + case 0x9FFF0: + return 0x000400CAu; + case 0x9FFF8: + /* Four rings, one packet engine, 64-bit addressing. */ + return 0x80000014u; + case 0x9FFFC: + return 0x010135CAu; + + /* + * EIP-207 classification sub-block, signature 0x30cf. + * 0xf7ff0 =3D OPTIONS2, unused by the minimum init path + * 0xf7ff8 =3D OPTIONS: bits 1..2 number of cache sets, bits 28..30 + * number of lookup tables. One of each is the minimum the + * guest accepts; all other capability bits stay clear. + * 0xf7ffc =3D VERSION + */ + case 0xF7FF0: + return 0; + case 0xF7FF8: + return (1u << 28) | (1u << 1); + case 0xF7FFC: + return 0x010130CFu; + + /* EIP-207s flow look-up engine, same 0x30cf signature. */ + case 0x01FF8: + return 0; + case 0x01FFC: + return 0x010130CFu; + + /* + * EIP-96 packet engine, signature 0x9f60. Both the PE + 0x13fc and the + * PE + 0xfffc placements are answered, as the guest probes either + * depending on the variant it believes it is talking to. + */ + case 0xA13FC: + case 0xA0FFC: + return 0x01019F60u; + case 0xA13F8: + case 0xA0FF0: + case 0xA0FF8: + return 0; + + /* EIP-201 interrupt controller / HIA xDR block, signature 0x36c9. */ + case 0x8FFF0: + case 0x8FFFC: + return 0x010136C9u; + case 0x8FFF8: + return 0; + + /* EIP-197 global, signature 0x3ac5. */ + case 0xFFFF0: + case 0xFFFFC: + return 0x01013AC5u; + case 0xFFFF8: + return 0; + } + + r =3D eip_xdr_ring(addr, &off, &is_rdr); + if (r >=3D 0) { + HCPxDR *x =3D is_rdr ? &s->rings[r].rdr : &s->rings[r].cdr; + return eip_xdr_read(x, off); + } + + r =3D eip_aic_r_ring(addr, &off); + if (r >=3D 0) { + HCPRing *ring =3D &s->rings[r]; + switch (off) { + case EIP_AIC_R_CDR_ENABLE_CTRL: return ring->aic_cdr_mask; + case EIP_AIC_R_CDR_ENABLE_CLR: return ring->aic_cdr_mask; + case EIP_AIC_R_CDR_ENABLED: return 0; + case EIP_AIC_R_RDR_ENABLE_CTRL: return ring->aic_rdr_mask; + case EIP_AIC_R_RDR_ENABLE_CLR: return ring->aic_rdr_mask; + case EIP_AIC_R_RDR_ENABLED: return 0; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip aic_r[%d] read 0x%" HWADDR_PRIx= "\n", + r, off); + return 0; + } + } + + if (addr >=3D EIP_HIA_AIC_G_BASE && addr < EIP_HIA_AIC_G_BASE + 0x1000= ) { + hwaddr g_off =3D addr - EIP_HIA_AIC_G_BASE; + switch (g_off) { + case EIP_AIC_G_ENABLE_CTRL: return s->aic_g_mask; + case EIP_AIC_G_ENABLE_CLR: return s->aic_g_mask; + case EIP_AIC_G_ENABLED: return 0; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip aic_g read 0x%" HWADDR_PRIx "\n= ", + g_off); + return 0; + } + } + + slot =3D eip_reg_slot(s, addr); + if (slot) { + return *slot; + } + + /* Write-only or read-only-status register: reads as zero. */ + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip197 read 0x%" HWADDR_PRIx "\n", addr); + return 0; +} + +static void axiado_eip197_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + AXIADOHCPState *s =3D AXIADO_HCP(opaque); + uint32_t *slot; + hwaddr off; + bool is_rdr; + int r; + + r =3D eip_xdr_ring(addr, &off, &is_rdr); + if (r >=3D 0) { + HCPxDR *x =3D is_rdr ? &s->rings[r].rdr : &s->rings[r].cdr; + bool kick =3D eip_xdr_write(x, off, val); + + /* + * A STAT write that cleared the threshold bit must immediately + * deassert the level-triggered ring interrupt, so the line is + * reconciled after any write to that register. + */ + if (off =3D=3D XDR_STAT) { + hcp_update_ring_irq(s, r); + } + if (kick) { + if (!is_rdr) { + /* Transmit command descriptors were posted on this ring. = */ + hcp_process_tx_ring(s, r); + } else { + /* + * Fresh receive buffers were posted, so retry anything th= at + * was back-pressured earlier. + */ + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + NICState *nic =3D s->macs[i].nic; + + if (nic) { + qemu_flush_queued_packets(qemu_get_queue(nic)); + } + } + } + } + return; + } + + r =3D eip_aic_r_ring(addr, &off); + if (r >=3D 0) { + HCPRing *ring =3D &s->rings[r]; + switch (off) { + case EIP_AIC_R_CDR_ENABLE_CTRL: + ring->aic_cdr_mask |=3D val; + hcp_update_ring_irq(s, r); + return; + case EIP_AIC_R_CDR_ENABLE_CLR: + ring->aic_cdr_mask &=3D ~val; + hcp_update_ring_irq(s, r); + return; + case EIP_AIC_R_RDR_ENABLE_CTRL: + ring->aic_rdr_mask |=3D val; + hcp_update_ring_irq(s, r); + return; + case EIP_AIC_R_RDR_ENABLE_CLR: + ring->aic_rdr_mask &=3D ~val; + hcp_update_ring_irq(s, r); + return; + case EIP_AIC_R_CDR_ENABLED: + case EIP_AIC_R_RDR_ENABLED: + return; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip aic_r[%d] write 0x%" HWADDR_PRIx + " =3D 0x%" PRIx64 "\n", r, off, val); + return; + } + } + + if (addr >=3D EIP_HIA_AIC_G_BASE && addr < EIP_HIA_AIC_G_BASE + 0x1000= ) { + hwaddr g_off =3D addr - EIP_HIA_AIC_G_BASE; + switch (g_off) { + case EIP_AIC_G_ENABLE_CTRL: + s->aic_g_mask |=3D val; + return; + case EIP_AIC_G_ENABLE_CLR: + s->aic_g_mask &=3D ~val; + return; + case EIP_AIC_G_ENABLED: + return; + default: + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip aic_g write 0x%" HWADDR_PRIx + " =3D 0x%" PRIx64 "\n", g_off, val); + return; + } + } + + slot =3D eip_reg_slot(s, addr); + if (slot) { + *slot =3D val; + + if (addr =3D=3D HCP_EIP_PE_BASE + PE_ICE_FPP_CTRL || + addr =3D=3D HCP_EIP_PE_BASE + PE_ICE_PUE_CTRL) { + hcp_ice_publish_fw_version(s, addr - HCP_EIP_PE_BASE); + } + return; + } + + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: eip197 write 0x%" HWADDR_PRIx " =3D 0x%" PR= Ix64 + "\n", addr, val); +} + +static const MemoryRegionOps eip197_ops =3D { + .read =3D axiado_eip197_read, + .write =3D axiado_eip197_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .impl.min_access_size =3D 4, + .impl.max_access_size =3D 4, +}; + +/* + * PHY CSR region + */ + +/* + * PCS/SerDes configuration. The guest programs two lane blocks here, at + * 0x30000 and 0x34000, and never reads any of it back: the link is always= up + * in this model, so none of the calibration or lock status it would poll = for + * is needed. Writes are therefore accepted and discarded, and reads return + * zero. Both are logged, so a guest that does start reading shows up as + * LOG_UNIMP output rather than as silent wrong behaviour. + */ +static uint64_t axiado_phy_csr_read(void *opaque, hwaddr addr, unsigned si= ze) +{ + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: phy csr read 0x%" HWADDR_PRIx "\n", addr); + return 0; +} + +static void axiado_phy_csr_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, + "axiado-hcp: phy csr write 0x%" HWADDR_PRIx " =3D 0x%" P= RIx64 + "\n", addr, val); +} + +static const MemoryRegionOps phy_csr_ops =3D { + .read =3D axiado_phy_csr_read, + .write =3D axiado_phy_csr_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid.min_access_size =3D 4, + .valid.max_access_size =3D 4, + .impl.min_access_size =3D 4, + .impl.max_access_size =3D 4, +}; + +/* + * NIC backends and receive datapath + */ + +/* + * 64-bit result descriptor plus EIP-96 output token, as the guest reads t= hem + * back with ownership words enabled and a host data width of zero: + * + * word 0 : control word (segment byte count | FIRST_SEG | LAST_SEG) + * word 1 : extended length (not read by the driver) + * word 2/3: destination packet address (lo/hi) -> RES_DESC_DATA_LO/HI_= OFF + * word 4..: EIP-96 output token + * token word 0, byte 16: packet byte count and error bits + * token word 2, byte 24: app_id << 9 + * last word of the slot (stride-4): ownership word =3D 0xAAAAAAAA + * + * A packet is ready purely by virtue of the ownership word; the guest tak= es + * the length and app_id from the output token, not from a fixed result + * descriptor field. + */ +#define RES_TOKEN_WORD_OFF 4 +/* Byte offsets of the length/error word and the app_id word in the token.= */ +#define RES_TOKEN_HDR_OFF (RES_TOKEN_WORD_OFF * 4) +#define RES_TOKEN_APP_ID_OFF ((RES_TOKEN_WORD_OFF + 2) * 4) +#define RES_TOKEN_LEN_MASK 0x1FFFFu +#define RES_OWNERSHIP_PATTERN 0xAAAAAAAAu + +/* + * Ring that carries received packets. Ring 0 is the default interface and + * posts no receive buffers; rings 1 to 3 run in continuous-scatter mode, + * where each result descriptor slot is pre-loaded with the address of a + * guest receive buffer at offsets +8 and +12. Any of them will do, and all + * five MACs are demultiplexed from the app_id, so one ring serves them al= l. + */ +#define HCP_RX_RING 1 + +/* Map a NetClientState back to the MAC index that owns it. */ +static int hcp_find_mac_for_nc(AXIADOHCPState *s, NetClientState *nc) +{ + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + if (s->macs[i].nic && qemu_get_queue(s->macs[i].nic) =3D=3D nc) { + return i; + } + } + return -1; +} + +static bool hcp_rdr_has_slot(HCPxDR *rdr) +{ + uint32_t stride =3D xdr_stride_bytes(rdr); + if (stride =3D=3D 0 || rdr->size =3D=3D 0 || rdr->base =3D=3D 0) { + return false; + } + if (rdr->size / stride =3D=3D 0) { + return false; + } + return rdr->prep_count > rdr->proc_count; +} + +/* + * Deliver one inbound packet through ring r's result descriptor ring. + * + * 1. Read the current slot. The guest pre-loaded it with the address of= a + * receive buffer at +8 and +12. + * 2. DMA-write the packet into that buffer. The buffer lives elsewhere = in + * guest RAM, not in the descriptor. + * 3. Rebuild the slot as a result descriptor plus output token, and sta= mp + * the ownership word last. + * 4. Advance the receive cursor and latch the threshold interrupt. + * + * Returns false without consuming the packet if no slot is available, so = the + * caller can apply back-pressure. + */ +static bool hcp_rdr_deliver(AXIADOHCPState *s, int r, int mac_idx, + const uint8_t *buf, size_t len) +{ + HCPxDR *rdr =3D &s->rings[r].rdr; + + uint32_t rdr_stride =3D xdr_stride_bytes(rdr); + if (rdr_stride =3D=3D 0 || rdr->size =3D=3D 0 || rdr->base =3D=3D 0) { + return false; + } + uint32_t rdr_descs =3D rdr->size / rdr_stride; + if (rdr_descs =3D=3D 0) { + return false; + } + + /* + * Step 1: locate the slot and read the receive buffer the guest poste= d in + * it. The command ring is the transmit ring and takes no part in rece= ive. + */ + uint32_t rdr_idx =3D s->rings[r].rx_idx % rdr_descs; + hwaddr rdr_desc_addr =3D rdr->base + (hwaddr)rdr_idx * rdr_stride; + + uint8_t res_desc[256]; + size_t rdr_io =3D MIN(rdr_stride, (uint32_t)sizeof(res_desc)); + if (dma_memory_read(&address_space_memory, rdr_desc_addr, res_desc, rd= r_io, + MEMTXATTRS_UNSPECIFIED) !=3D MEMTX_OK) { + return false; + } + + uint32_t orig_own =3D (rdr_io >=3D rdr_stride && rdr_stride >=3D 4) + ? ldl_le_p(&res_desc[rdr_stride - 4]) : 0; + uint64_t buf_addr =3D + (uint64_t)ldl_le_p(&res_desc[RES_DESC_DATA_LO_OFF]) + | ((uint64_t)ldl_le_p(&res_desc[RES_DESC_DATA_HI_OFF]) << 32); + + if (buf_addr =3D=3D 0) { + /* No buffer posted for this slot yet. */ + trace_axiado_hcp_rx_backpressure(r, rdr_idx); + return false; + } + + /* + * Only fill a slot the guest has freshly prepared. An ownership word = still + * holding the pattern means this slot was filled earlier and has not = been + * consumed, so the ring is full: leave the packet queued rather than + * overwrite a buffer that is still in use. + */ + if (orig_own =3D=3D RES_OWNERSHIP_PATTERN) { + trace_axiado_hcp_rx_backpressure(r, rdr_idx); + return false; + } + + /* Step 2: DMA-write the packet into the guest's receive buffer. */ + if (dma_memory_write(&address_space_memory, buf_addr, buf, len, + MEMTXATTRS_UNSPECIFIED) !=3D MEMTX_OK) { + return false; + } + + /* + * Step 3: build the result descriptor and output token in this slot. = The + * slot is rebuilt from zero because the guest clears only the ownersh= ip + * word, so stale token bytes would otherwise survive. + */ + memset(res_desc, 0, rdr_io); + + /* word 0: control word =E2=80=94 segment byte count + first/last segm= ent */ + uint32_t ctrl =3D ((uint32_t)len & RES_DESC_PARTICLE_MASK) + | RES_DESC_FIRST_SEG_BIT + | RES_DESC_LAST_SEG_BIT; + stl_le_p(&res_desc[RES_DESC_FLAGS_OFF], ctrl); + + /* words 2/3: destination packet address (64-bit device) */ + stl_le_p(&res_desc[RES_DESC_DATA_LO_OFF], (uint32_t)buf_addr); + stl_le_p(&res_desc[RES_DESC_DATA_HI_OFF], (uint32_t)(buf_addr >> 32)); + + /* EIP-96 output token: length with a zero error code, then app_id << = 9. */ + if (rdr_io >=3D RES_TOKEN_APP_ID_OFF + 4) { + stl_le_p(&res_desc[RES_TOKEN_HDR_OFF], + (uint32_t)len & RES_TOKEN_LEN_MASK); + stl_le_p(&res_desc[RES_TOKEN_APP_ID_OFF], + ((uint32_t)s->macs[mac_idx].app_id & 0x7Fu) << 9); + } + + /* + * The ownership word in the last word of the slot is the packet-ready + * handshake, so it is written after everything else the guest will re= ad. + */ + if (rdr_io >=3D rdr_stride && rdr_stride >=3D 4) { + stl_le_p(&res_desc[rdr_stride - 4], RES_OWNERSHIP_PATTERN); + } + + dma_memory_write(&address_space_memory, rdr_desc_addr, res_desc, rdr_i= o, + MEMTXATTRS_UNSPECIFIED); + + /* + * Step 4: advance the receive cursor. The command ring counters belon= g to + * the transmit path and must not move here. + */ + s->rings[r].rx_idx++; + rdr->proc_count +=3D rdr_stride; + rdr->proc_pntr =3D (rdr->proc_pntr + rdr_stride) % rdr->size; + + trace_axiado_hcp_rx_packet(r, mac_idx, len); + + rdr->stat |=3D XDR_STAT_PROC_THRESH_IRQ; + hcp_update_ring_irq(s, r); + + return true; +} + +static bool axiado_nic_can_receive(NetClientState *nc) +{ + AXIADOHCPState *s =3D qemu_get_nic_opaque(nc); + + return hcp_rdr_has_slot(&s->rings[HCP_RX_RING].rdr); +} + +static ssize_t axiado_nic_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + AXIADOHCPState *s =3D qemu_get_nic_opaque(nc); + int mac_idx =3D hcp_find_mac_for_nc(s, nc); + + if (mac_idx < 0 || size =3D=3D 0 || size > MAX_PKT_LEN) { + return size; + } + + if (!hcp_rdr_deliver(s, HCP_RX_RING, mac_idx, buf, size)) { + /* + * No slot is free. Returning zero keeps the packet queued so it is + * redelivered from qemu_flush_queued_packets() once the guest pos= ts + * more buffers; returning size would silently drop it and stall t= he + * guest's transport layer under load. + */ + return 0; + } + return size; +} + +static NetClientInfo axiado_nic_info =3D { + .type =3D NET_CLIENT_DRIVER_NIC, + .size =3D sizeof(NICState), + .can_receive =3D axiado_nic_can_receive, + .receive =3D axiado_nic_receive, +}; + +/* + * QOM lifecycle + */ + +static void axiado_hcp_realize(DeviceState *dev, Error **errp) +{ + AXIADOHCPState *s =3D AXIADO_HCP(dev); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->mmio_eip197, OBJECT(s), &eip197_ops, s, + "axiado-hcp.eip197", HCP_EIP197_SIZE); + sysbus_init_mmio(sbd, &s->mmio_eip197); + + memory_region_init_io(&s->mmio_shim, OBJECT(s), &shim_ops, s, + "axiado-hcp.shim", HCP_SHIM_SIZE); + sysbus_init_mmio(sbd, &s->mmio_shim); + + memory_region_init_io(&s->mmio_phy_csr, OBJECT(s), &phy_csr_ops, s, + "axiado-hcp.phy-csr", HCP_PHY_CSR_SIZE); + sysbus_init_mmio(sbd, &s->mmio_phy_csr); + + for (int i =3D 0; i < HCP_NUM_IRQS; i++) { + sysbus_init_irq(sbd, &s->irqs[i]); + } + + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + HCPMac *m =3D &s->macs[i]; + char nic_name[16]; + + snprintf(nic_name, sizeof(nic_name), "axiado-mac%d", i); + qemu_macaddr_default_if_unset(&m->conf.macaddr); + m->nic =3D qemu_new_nic(&axiado_nic_info, &m->conf, + object_get_typename(OBJECT(dev)), + nic_name, &dev->mem_reentrancy_guard, s); + qemu_format_nic_info_str(qemu_get_queue(m->nic), m->conf.macaddr.a= ); + } +} + +static void axiado_hcp_unrealize(DeviceState *dev) +{ + AXIADOHCPState *s =3D AXIADO_HCP(dev); + + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + if (s->macs[i].nic) { + qemu_del_nic(s->macs[i].nic); + s->macs[i].nic =3D NULL; + } + } +} + +static void axiado_hcp_reset(DeviceState *dev) +{ + AXIADOHCPState *s =3D AXIADO_HCP(dev); + static const uint8_t mac_app_ids[HCP_NUM_MACS] =3D { 5, 1, 2, 3, 4 }; + + for (int i =3D 0; i < HCP_NUM_MACS; i++) { + HCPMac *m =3D &s->macs[i]; + /* Preserve nic + conf across reset */ + NICState *saved_nic =3D m->nic; + NICConf saved_conf =3D m->conf; + + memset(m, 0, sizeof(*m)); + m->nic =3D saved_nic; + m->conf =3D saved_conf; + m->app_id =3D mac_app_ids[i]; + m->mii_regs[MII_BMCR] =3D 0x1140; + m->mii_regs[MII_BMSR] =3D 0x002d; + } + + for (int i =3D 0; i < HCP_NUM_RINGS; i++) { + memset(&s->rings[i], 0, sizeof(s->rings[i])); + } + s->aic_g_mask =3D 0; + + memset(s->flue_fht, 0, sizeof(s->flue_fht)); + memset(s->pe_regs, 0, sizeof(s->pe_regs)); + memset(s->cls_regs, 0, sizeof(s->cls_regs)); + memset(s->flow_regs, 0, sizeof(s->flow_regs)); + memset(s->cache_regs, 0, sizeof(s->cache_regs)); + memset(s->trc_ram, 0, sizeof(s->trc_ram)); +} + +static const VMStateDescription vmstate_axiado_hcp_mac =3D { + .name =3D "axiado-hcp/mac", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(command_config, HCPMac), + VMSTATE_UINT32(mac_addr_lo, HCPMac), + VMSTATE_UINT32(mac_addr_hi, HCPMac), + VMSTATE_UINT32(frm_length, HCPMac), + VMSTATE_UINT32(rx_fifo_sections, HCPMac), + VMSTATE_UINT32(tx_fifo_sections, HCPMac), + VMSTATE_UINT32(if_mode, HCPMac), + VMSTATE_UINT32(mdio_command, HCPMac), + VMSTATE_UINT32(mdio_data, HCPMac), + VMSTATE_UINT32(mdio_regaddr, HCPMac), + VMSTATE_UINT16_ARRAY(mii_regs, HCPMac, 32), + VMSTATE_UINT8(app_id, HCPMac), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_axiado_hcp_xdr =3D { + .name =3D "axiado-hcp/xdr", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT64(base, HCPxDR), + VMSTATE_UINT32(size, HCPxDR), + VMSTATE_UINT32(desc_size, HCPxDR), + VMSTATE_UINT32(cfg, HCPxDR), + VMSTATE_UINT32(dma_cfg, HCPxDR), + VMSTATE_UINT32(thresh, HCPxDR), + VMSTATE_UINT32(prep_count, HCPxDR), + VMSTATE_UINT32(proc_count, HCPxDR), + VMSTATE_UINT32(prep_pntr, HCPxDR), + VMSTATE_UINT32(proc_pntr, HCPxDR), + VMSTATE_UINT32(stat, HCPxDR), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_axiado_hcp_ring =3D { + .name =3D "axiado-hcp/ring", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_STRUCT(cdr, HCPRing, 1, vmstate_axiado_hcp_xdr, HCPxDR), + VMSTATE_STRUCT(rdr, HCPRing, 1, vmstate_axiado_hcp_xdr, HCPxDR), + VMSTATE_UINT32(aic_cdr_mask, HCPRing), + VMSTATE_UINT32(aic_rdr_mask, HCPRing), + VMSTATE_UINT32(rx_idx, HCPRing), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_axiado_hcp =3D { + .name =3D TYPE_AXIADO_HCP, + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(macs, AXIADOHCPState, HCP_NUM_MACS, 1, + vmstate_axiado_hcp_mac, HCPMac), + VMSTATE_STRUCT_ARRAY(rings, AXIADOHCPState, HCP_NUM_RINGS, 1, + vmstate_axiado_hcp_ring, HCPRing), + VMSTATE_UINT32(aic_g_mask, AXIADOHCPState), + VMSTATE_UINT32_ARRAY(flue_fht, AXIADOHCPState, + HCP_EIP_FLUE_FHT_REGS), + VMSTATE_UINT32_ARRAY(pe_regs, AXIADOHCPState, HCP_EIP_PE_REGS), + VMSTATE_UINT32_ARRAY(cls_regs, AXIADOHCPState, HCP_EIP_CLS_REGS), + VMSTATE_UINT32_ARRAY(flow_regs, AXIADOHCPState, HCP_EIP_FLOW_REGS), + VMSTATE_UINT32_ARRAY(cache_regs, AXIADOHCPState, HCP_EIP_CACHE_REG= S), + VMSTATE_UINT32_ARRAY(trc_ram, AXIADOHCPState, HCP_EIP_TRC_RAM_REGS= ), + VMSTATE_END_OF_LIST() + } +}; + +static const Property axiado_hcp_properties[] =3D { + DEFINE_PROP_MACADDR("mac0", AXIADOHCPState, macs[0].conf.macaddr), + DEFINE_PROP_MACADDR("mac1", AXIADOHCPState, macs[1].conf.macaddr), + DEFINE_PROP_MACADDR("mac2", AXIADOHCPState, macs[2].conf.macaddr), + DEFINE_PROP_MACADDR("mac3", AXIADOHCPState, macs[3].conf.macaddr), + DEFINE_PROP_MACADDR("mac4", AXIADOHCPState, macs[4].conf.macaddr), + DEFINE_PROP_NETDEV("netdev0", AXIADOHCPState, macs[0].conf.peers), + DEFINE_PROP_NETDEV("netdev1", AXIADOHCPState, macs[1].conf.peers), + DEFINE_PROP_NETDEV("netdev2", AXIADOHCPState, macs[2].conf.peers), + DEFINE_PROP_NETDEV("netdev3", AXIADOHCPState, macs[3].conf.peers), + DEFINE_PROP_NETDEV("netdev4", AXIADOHCPState, macs[4].conf.peers), +}; + +static void axiado_hcp_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "Axiado HCP network complex"; + dc->realize =3D axiado_hcp_realize; + dc->unrealize =3D axiado_hcp_unrealize; + dc->vmsd =3D &vmstate_axiado_hcp; + device_class_set_legacy_reset(dc, axiado_hcp_reset); + device_class_set_props(dc, axiado_hcp_properties); + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static const TypeInfo axiado_hcp_info =3D { + .name =3D TYPE_AXIADO_HCP, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(AXIADOHCPState), + .class_init =3D axiado_hcp_class_init, +}; + +static void axiado_hcp_register_types(void) +{ + type_register_static(&axiado_hcp_info); +} +type_init(axiado_hcp_register_types) diff --git a/hw/net/meson.build b/hw/net/meson.build index 63b0e96245..56dae1266e 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -70,4 +70,6 @@ if have_hmp system_ss.add(files('rocker/rocker-hmp-cmds.c')) endif =20 +system_ss.add(when: 'CONFIG_AXIADO_HCP', if_true: files('axiado_hcp.c')) + subdir('can') diff --git a/hw/net/trace-events b/hw/net/trace-events index 001a20b0e2..db9815931a 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -528,3 +528,9 @@ xen_netdev_rx(int dev, int idx, int status, int flags) = "vif%u idx %d status %d f ethlite_pkt_lost(uint32_t rx_ctrl) "rx_ctrl:0x%" PRIx32 ethlite_pkt_size_too_big(uint64_t size) "size:0x%" PRIx64 ethlite_pkt_tx_size_too_big(uint64_t size) "size:0x%" PRIx64 + +# axiado_hcp.c +axiado_hcp_tx_packet(int ring, unsigned app_id, int mac, unsigned len) "ri= ng %d app_id %u mac %d len %u" +axiado_hcp_tx_unroutable(int ring, unsigned app_id, unsigned len, uint64_t= addr) "ring %d app_id %u len %u data 0x%" PRIx64 +axiado_hcp_rx_packet(int ring, int mac, size_t len) "ring %d mac %d len %z= u" +axiado_hcp_rx_backpressure(int ring, unsigned slot) "ring %d slot %u" diff --git a/include/hw/net/axiado_hcp.h b/include/hw/net/axiado_hcp.h new file mode 100644 index 0000000000..89069ea456 --- /dev/null +++ b/include/hw/net/axiado_hcp.h @@ -0,0 +1,172 @@ +/* + * Axiado HCP (Header & Crypto Processing) block - QEMU device model + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright (C) 2026 Ratan Lal Dondi + */ + +#ifndef HW_NET_AXIADO_HCP_H +#define HW_NET_AXIADO_HCP_H + +#include "hw/core/sysbus.h" +#include "net/net.h" + +#define TYPE_AXIADO_HCP "axiado-hcp" +OBJECT_DECLARE_SIMPLE_TYPE(AXIADOHCPState, AXIADO_HCP) + +/* MMIO region sizes, matching the SoC base map */ +#define HCP_EIP197_SIZE 0x100000 /* EIP-197 registers and ring contro= l */ +#define HCP_SHIM_SIZE 0x4000 /* MAC config, MDIO and statistics */ +#define HCP_PHY_CSR_SIZE 0x50000 /* PCS / PHY CSR */ + +/* + * IRQ topology: + * index 0 =3D EIP-197 global + * indices 1..4 =3D ring interfaces + * indices 5..9 =3D per-MAC IRQs, PHY and link events only + */ +#define HCP_NUM_RINGS 4 +#define HCP_NUM_MACS 5 +#define HCP_IRQ_EIP197 0 +#define HCP_IRQ_RING_BASE 1 +#define HCP_IRQ_MAC_BASE (HCP_IRQ_RING_BASE + HCP_NUM_RINGS) +#define HCP_NUM_IRQS (HCP_IRQ_MAC_BASE + HCP_NUM_MACS) + +/* MAC block layout within the SHIM region */ +#define HCP_SHIM_MAC_BASE_OFFSET 0x400 +#define HCP_SHIM_MAC_STRIDE 0x400 + +/* Per-MAC state. Five instances per device: one 10G XGMII and four 1G SGM= II. */ +typedef struct HCPMac { + /* MAC configuration registers */ + uint32_t command_config; /* R_COMMAND_CONFIG 0x008 */ + uint32_t mac_addr_lo; /* R_MAC_0 0x00c */ + uint32_t mac_addr_hi; /* R_MAC_1 0x010 */ + uint32_t frm_length; /* R_FRM_LENGTH 0x014 */ + uint32_t rx_fifo_sections; /* R_RX_FIFO_SECT 0x01c */ + uint32_t tx_fifo_sections; /* R_TX_FIFO_SECT 0x020 */ + uint32_t if_mode; /* R_IF_MODE 0x300 */ + + /* Per-MAC MDIO controller (talks to this MAC's virtual PHY) */ + uint32_t mdio_command; /* R_MDIO_COMMAND 0x034 */ + uint32_t mdio_data; /* R_MDIO_DATA 0x038 */ + uint32_t mdio_regaddr; /* R_MDIO_REGADDR 0x03c (Clause 45) */ + + /* Virtual PHY: Clause 22 register file accessed via per-MAC MDIO */ + uint16_t mii_regs[32]; + + /* Axiado IOTOKEN routing tag. MAC 0 (10G) =3D 5; MAC 1..4 (1G) =3D 1.= .4. */ + uint8_t app_id; + + /* QEMU NIC backend for this MAC */ + NICConf conf; + NICState *nic; +} HCPMac; + +/* + * SafeXcel xDR =E2=80=94 Command/Result Descriptor Ring control registers. + * One instance is a CDR; another (same layout) is the paired RDR. + */ +typedef struct HCPxDR { + uint64_t base; /* +0x00 BASE_LO / +0x04 BASE_HI */ + uint32_t size; /* +0x18 RING_SIZE (bytes) */ + uint32_t desc_size; /* +0x1c DESC_SIZE */ + uint32_t cfg; /* +0x20 CFG */ + uint32_t dma_cfg; /* +0x24 DMA_CFG */ + uint32_t thresh; /* +0x28 THRESH */ + /* + * Byte cursors, not register images. Descriptors prepared by the guest + * and descriptors consumed by the engine; the difference is the fill + * level reported when +0x2c is read. + */ + uint32_t prep_count; /* +0x2c PREP_COUNT */ + uint32_t proc_count; /* +0x30 PROC_COUNT */ + uint32_t prep_pntr; /* +0x34 PREP_PNTR */ + uint32_t proc_pntr; /* +0x38 PROC_PNTR */ + uint32_t stat; /* +0x3c STAT (latched IRQ; bit 4 =3D THRESH IRQ= ) */ +} HCPxDR; + +/* + * Backed register windows inside the EIP-197 region, as base address and + * count of 32-bit registers. Each covers a block the guest writes during + * setup and reads back later: + * + * FLUE_FHT flow look-up engine, flow hash table configuration + * PE packet engine: buffer thresholds, ICE scratch RAM and contr= ol, + * and the EIP-96 token, context and seed registers + * CLS classification engines, three instances at a 0x800 stride + * FLOW flow control + * CACHE record cache control + * TRC_RAM classification record cache, actual on-chip RAM + * + * The ring status window at 0xffb00 is deliberately absent: the guest only + * ever reads it, so it needs no storage and reads as zero. + */ +#define HCP_EIP_FLUE_FHT_BASE 0x00000 +#define HCP_EIP_FLUE_FHT_REGS 8 +#define HCP_EIP_PE_BASE 0xa0000 +#define HCP_EIP_PE_REGS 2048 +#define HCP_EIP_TRC_RAM_BASE 0xe0000 +#define HCP_EIP_TRC_RAM_REGS 16384 +#define HCP_EIP_CLS_BASE 0xf0000 +#define HCP_EIP_CLS_REGS 2048 +#define HCP_EIP_FLOW_BASE 0xf6000 +#define HCP_EIP_FLOW_REGS 1024 +#define HCP_EIP_CACHE_BASE 0xf7000 +#define HCP_EIP_CACHE_REGS 1024 + +/* Per-ring state: a CDR/RDR pair plus its interrupt enable masks. */ +typedef struct HCPRing { + HCPxDR cdr; + HCPxDR rdr; + uint32_t aic_cdr_mask; + uint32_t aic_rdr_mask; + + /* + * Receive slot cursor, in descriptors. The guest's own read pointer i= s a + * plain counter starting at zero and advancing one descriptor per pac= ket, + * so the byte counters above cannot drive the slot index: they carry + * clear-bit and word-shift encodings from the register writes. Keepin= g a + * separate cursor lines slot zero up with the guest's first read. + */ + uint32_t rx_idx; +} HCPRing; + +struct AXIADOHCPState { + SysBusDevice parent_obj; + + MemoryRegion mmio_eip197; + MemoryRegion mmio_shim; + MemoryRegion mmio_phy_csr; + + qemu_irq irqs[HCP_NUM_IRQS]; + + HCPMac macs[HCP_NUM_MACS]; + HCPRing rings[HCP_NUM_RINGS]; + + /* Global AIC enable mask (per-ring bits) */ + uint32_t aic_g_mask; + + /* + * Register windows within the EIP-197 region that hold guest-written + * values. The engine's behaviour does not depend on them - the datapa= th + * is driven entirely by the descriptor rings - but the guest writes t= hem + * during setup and reads them back, so the values have to persist. + * Anything outside these windows reads as zero. + */ + uint32_t flue_fht[HCP_EIP_FLUE_FHT_REGS]; + uint32_t pe_regs[HCP_EIP_PE_REGS]; + uint32_t cls_regs[HCP_EIP_CLS_REGS]; + uint32_t flow_regs[HCP_EIP_FLOW_REGS]; + uint32_t cache_regs[HCP_EIP_CACHE_REGS]; + + /* + * Classification record cache. Real on-chip RAM: at init the guest wa= lks + * it writing each record's own index, then reads the records back to + * discover how large the cache is. + */ + uint32_t trc_ram[HCP_EIP_TRC_RAM_REGS]; +}; + +#endif /* HW_NET_AXIADO_HCP_H */ --=20 2.34.1 From nobody Sat Sep 26 20:50:59 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=axiado.com) ARC-Seal: i=2; a=rsa-sha256; t=1789540120; cv=pass; d=zohomail.com; s=zohoarc; b=gApqQhqVK3m3UdJf9GTnyqrwaoSO/5SvKDzlf9jpRHdhbs++eCv62ZUx1tULdx4FHEUk4l6eevxwIwPQFK+4NAyLfh8NrUyPKmEXtTHoq2lwSGpKEdu92+uyZ4QOQnEtyLAVqxX+2wrvJfoMI8QuMNgW6eoODqFIuf16Vexkw24= ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1789540120; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=yKb4EafBysRSKmV477/Xy1Ah8Q06+juLK9rG8DOlISc=; b=abOeTbrl0CMMFGzlYpqMdnh1zP++jnAWh1ALt55TQ9HZTVaN4wjTE/WZQgLvQeAjpQvl/R2ob5tLDJzsXrahM8ih/RGz0r0q/7Y7uwpPCbhbcqIF/JBwdjNCSsx4o1tz4k8daEGAWq/UZR6wC54dak4mSBXVJIQQu3iJTvJ7MoA= ARC-Authentication-Results: i=2; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; arc=pass (i=1 dmarc=pass fromdomain=axiado.com) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 178954012096276.86920655834149; Tue, 15 Sep 2026 23:28:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x6j84-0005vu-Tm; Wed, 16 Sep 2026 02:28:28 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x6hcu-0002Ef-Rs; Wed, 16 Sep 2026 00:52:10 -0400 Received: from mail-westus2azlp170120002.outbound.protection.outlook.com ([2a01:111:f403:c007::2] helo=MW6PR02CU001.outbound.protection.outlook.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x6hcr-0001XO-Td; Wed, 16 Sep 2026 00:52:07 -0400 Received: from BL1P221CA0043.NAMP221.PROD.OUTLOOK.COM (2603:10b6:208:5b5::13) by PH0PR18MB4426.namprd18.prod.outlook.com (2603:10b6:510:e9::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.428.9; Wed, 16 Sep 2026 04:52:00 +0000 Received: from BN2PEPF000044AB.namprd04.prod.outlook.com (2603:10b6:208:5b5:cafe::62) by BL1P221CA0043.outlook.office365.com (2603:10b6:208:5b5::13) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.21.428.11 via Frontend Transport; Wed, 16 Sep 2026 04:52:00 +0000 Received: from smtp.corp.axiado.com (64.62.143.114) by BN2PEPF000044AB.mail.protection.outlook.com (10.167.243.106) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.21.428.7 via Frontend Transport; Wed, 16 Sep 2026 04:52:00 +0000 Received: from axiado.com (unknown [10.4.1.32]) by smtp.corp.axiado.com (Postfix) with ESMTP id 79C9F4186B53; Tue, 15 Sep 2026 21:51:59 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=xRsGVlcHuF2VYMbDhGQvQSwgex9Kw4/AZkW7if4jZ3QcxlMOntupHQoUqqj1yydpisLTIiShz7ETMa7MZMgfNEOtjoyBXl/xbW6WYm1k8m1diVIVYWV6s+QoBy8pR4Fx//fnIjJSO+OS8GjEDjCsrow3eTwfKZdrmilQEqQuNx3wtmyXazP7o5p9eDUqCjPslNO/QUu6LDRx3nVHPp+ymjxFCoqufSxCEpQ7mh53tn0sK5KVeVBEOE52HW2IMC2OjS0PKxG0Qp99ANbDujIkZllRy5LLAt9odNZwCwin0H1aLeCuf9ar5VtH+PX4xL6iAT2gZJIPfi2zE/1sM2NRGA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=yKb4EafBysRSKmV477/Xy1Ah8Q06+juLK9rG8DOlISc=; b=rC9RC6nW7rwFxbeHvtlZJciSXE9tlvlZxgpknq8zI9dFdk8oPn0jJrjHUUBLy0CAoyDcdwdPAT8YgFQ7hxCSKP1VYgK44drQAKObIFjeNJn2w1lY1DRc8uha+lrY6Ph9hu039eyg2UG77w9dUhrpg8uTl9GsWEoXyT22YnkHip40HqqXyfKSs/Rh6VpRX76AmcXBkqaq2qwsliQzXCsDBflFW1UBHILKjfXXFjD4QUs4oOYOtcts3N0KXosyCRHGv3D4UXmKejIyPsHBnQIPABft4htQf0ZxUd20/Nm/fosbIA6z9FkK9bUAaBV1ZWxAr2zoJ+Lh2k6CoLV8H1xtog== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is 64.62.143.114) smtp.rcpttodomain=axiado.com smtp.mailfrom=axiado.com; dmarc=none action=none header.from=axiado.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=axiado.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=yKb4EafBysRSKmV477/Xy1Ah8Q06+juLK9rG8DOlISc=; b=W5dw7qGzGIkhYQtY7RvxXdmosnh+KZURDn6R4tbnQQE9v0ZiYK2BMlf9PbgHMqNxYEqBH0rup7+sPWBonK8dXYfrrsIPm3dgQ9TGkNRycvTSwhcBkSuPpmAb7ABKdoUMoM2RMm5WEktMIZPAaoPtpfZdk0yt4ZeigIvv2oZo31/OWgIgF/e9mBYlpmitr7jhrUKT6w+VWRP99p8mQjW1qVEb+C3cvMOefcbGNlcqZlZg81n6tq82TIq4Cpg/DBAvPJ1VTt1OKhSgOSwGznsqv/+KHd4x09x4YjjZ4wCzAAXTXK5Xjrab8NADlUzMhheevnuW94GxgY1gK+V6w5V1eQ== X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 64.62.143.114) smtp.mailfrom=axiado.com; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=axiado.com; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: Fail (protection.outlook.com: domain of axiado.com does not designate 64.62.143.114 as permitted sender) receiver=protection.outlook.com; client-ip=64.62.143.114; helo=smtp.corp.axiado.com; From: Ratan Lal Dondi To: qemu-devel@nongnu.org, qemu-arm@nongnu.org Cc: pbonzini@redhat.com, peter.maydell@linaro.org, jasowangio@gmail.com, kchiu@axiado.com, Ratan Lal Dondi Subject: [PATCH 2/2] hw/arm: Wire up the HCP network complex in the AX3000 SoC Date: Tue, 15 Sep 2026 21:51:34 -0700 Message-Id: <20260916045134.1687145-3-rdondi@axiado.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260916045134.1687145-1-rdondi@axiado.com> References: <20260916045134.1687145-1-rdondi@axiado.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: BN2PEPF000044AB:EE_|PH0PR18MB4426:EE_ X-MS-Office365-Filtering-Correlation-Id: d9d8b88b-c235-4450-3853-08df13ae3e81 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230040|30052699003|36860700016|23010399003|376014|1800799024|82310400026|56012099006|10067099003|18002099003|22082099003|6133799003; X-Microsoft-Antispam-Message-Info: 4ItoEYvArvUmcx7muplvB10RSE0Sj/SveIGyfOwC9eT0WpjojXFe0xmzWHhl2ak9CF/YKl7pZTj5/seedE5gLp/waiIuvffNLH3xNJvKyLaXZmhOhdJM4a9GhMnOZvErVs+ECbddboC7tIns5MfsCIF1kSkes2LgkfgWH/kqP6nkuT0uhjH5jYCWbWeGPoFMF+uQq9LMlUgNiM5UZb+9LSjzXbIGuYEzHQvO69r2QmlMJ60e0mYBwmgtjmBjAO6v0NrhBqI6R2dO7+HnQymRQEjR+su04AVviOGA0w6fuyucJm4RXnGwMNNaJJVMoopMvujfDHy/yak/6aDd0o9Shs8+7xyNrR/mtNmd16Yew26AGmCIgdDOjJujJ9P202bBD0Nik0SRZjU2O/eoP/l83BFFMG4aY/bOKHhq998CbAiPJGurd20JprNjPdH4JlD2eV7bRADLx08d3zOl+2aFTfEaj4PEZj1VgfS8FsZk+z5+zbT2TCMJEdXIgvgnBgv28kUL/tMJKAMM34F1PPVJEH2LE/1IOhu0uvtBxym8D2utBNFm6XlVcX+vTO0ga2f9OkRk7m1KUoDnukEktaUghP8Y3YyK6080AWFjxSCPACyNutVYIoxxsqJBYt1E5j9Va0THj1yz4LifH3KU8bP4Pmjh3W5QffTH/2oVblo2dYhR5cc3lsieEPo3OPC8W28iEd6KFy8UeVmWZURIZx0DHw== X-Forefront-Antispam-Report: CIP:64.62.143.114; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:smtp.corp.axiado.com; PTR:InfoDomainNonexistent; CAT:NONE; SFS:(13230040)(30052699003)(36860700016)(23010399003)(376014)(1800799024)(82310400026)(56012099006)(10067099003)(18002099003)(22082099003)(6133799003); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: UWKCUiZeMZ+zrBuGamBDbFqbz+jpGaMZ5mlqMRecwPYGTfHC10QjDNukfJzF+Pu2nZFNruq4G09r16mhRI0yX2JDSe+QpGatsSXo9DVNSWWG5WAl7uqqvpAWy7+bpRc4CWAjBBBXmJf4B0qq6oLuIy1YStlGqjA69Q4nfLqvbgvnjFHjttz/GAkzz4aSLCNNLSHhCGoNPZdDGRqOTaIBBKefL7yUbtOTeg8VkR90LHPMVQ8Yp1YP6bxB47oL3I/3TxS3XGH9f9mf7cyOJwi7CAKeA/Io5byBrGVasB319GKjM35Ld1C6O/FO3AksQQoK/Y37oNTochoxK3ufxIz6WMcvXrQsfv/T0r6XMjl4mJL88KYK9EtGseTdjdDahY9pho4g9QFlv8ugxKa5CZQejTRuZJrZhdAPbXSW10+2nFP4kL/tZKx+Wxi9CqeiGxGc X-OriginatorOrg: axiado.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 16 Sep 2026 04:52:00.2123 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: d9d8b88b-c235-4450-3853-08df13ae3e81 X-MS-Exchange-CrossTenant-Id: ff2db17c-4338-408e-9036-2dee8e3e17d7 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=ff2db17c-4338-408e-9036-2dee8e3e17d7; Ip=[64.62.143.114]; Helo=[smtp.corp.axiado.com] X-MS-Exchange-CrossTenant-AuthSource: BN2PEPF000044AB.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR18MB4426 Received-SPF: pass client-ip=2a01:111:f403:c007::2; envelope-from=rdondi@axiado.com; helo=MW6PR02CU001.outbound.protection.outlook.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 16 Sep 2026 02:28:02 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @axiado.com) X-ZM-MESSAGEID: 1789540122817158500 Instantiate the Axiado HCP and map its three MMIO regions - the EIP-197 packet engine, the SHIM MAC configuration block and the PCS/SerDes CSR region - at the addresses the SoC uses. Its ten interrupt lines are connected to the GIC in the order the guest expects: one for the EIP-197 itself, four for the descriptor ring interfaces and five for the MACs. Signed-off-by: Ratan Lal Dondi --- hw/arm/Kconfig | 1 + hw/arm/ax3000-soc.c | 23 +++++++++++++++++++++++ include/hw/arm/ax3000-soc.h | 14 ++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index eae06369b0..5545f4846d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -723,6 +723,7 @@ config AXIADO_SOC select AXIADO_CLK select CADENCE_GPIO select AXIADO_SDHCI + select AXIADO_HCP select UNIMP =20 config AXIADO_EVK diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c index ebe174fb97..9c3137af2a 100644 --- a/hw/arm/ax3000-soc.c +++ b/hw/arm/ax3000-soc.c @@ -41,6 +41,10 @@ static void ax3000_init(Object *obj) g_autofree char *name =3D g_strdup_printf("gpio%d", i); object_initialize_child(obj, name, &s->gpio[i], TYPE_CADENCE_GPIO); } + + + object_initialize_child(obj, "hcp", &s->hcp, TYPE_AXIADO_HCP); + } =20 static void ax3000_realize(DeviceState *dev, Error **errp) @@ -49,6 +53,7 @@ static void ax3000_realize(DeviceState *dev, Error **errp) Ax3000SoCClass *sc =3D AX3000_SOC_GET_CLASS(s); SysBusDevice *gic_sbd =3D SYS_BUS_DEVICE(&s->gic); DeviceState *gic_dev =3D DEVICE(&s->gic); + SysBusDevice *hcp_sbd =3D SYS_BUS_DEVICE(&s->hcp); QList *redist_region_count; SysBusDevice *sdhci0_sbd; DeviceState *card; @@ -217,6 +222,24 @@ static void ax3000_realize(DeviceState *dev, Error **e= rrp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, qdev_get_gpio_in(gic_dev, gpio_table[i].irq)); } + + /* HCP */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->hcp), errp)) { + return; + } + + sysbus_mmio_map(hcp_sbd, 0, AX3000_HCP_EIP197_BASE); + + sysbus_mmio_map(hcp_sbd, 1, AX3000_HCP_SHIM_BASE); + + sysbus_mmio_map(hcp_sbd, 2, AX3000_HCP_PHY_CSR_BASE); + + for (int i =3D 0; i < HCP_NUM_IRQS; i++) { + sysbus_connect_irq(hcp_sbd, i, + qdev_get_gpio_in(gic_dev, + AX3000_HCP_IRQ_BASE + i)); + } + } =20 static void ax3000_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h index 344fcb6f3c..187c1e02ad 100644 --- a/include/hw/arm/ax3000-soc.h +++ b/include/hw/arm/ax3000-soc.h @@ -15,6 +15,7 @@ #include "hw/misc/axiado_clk.h" #include "hw/gpio/cadence_gpio.h" #include "hw/sd/axiado_sdhci.h" +#include "hw/net/axiado_hcp.h" #include "hw/core/sysbus.h" #include "qemu/units.h" =20 @@ -48,6 +49,10 @@ OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX30= 00_SOC) #define AX3000_GPIO6_BASE 0x80800000 #define AX3000_GPIO7_BASE 0x80880000 =20 +#define AX3000_HCP_EIP197_BASE 0x83000000 +#define AX3000_HCP_SHIM_BASE 0x83100000 +#define AX3000_HCP_PHY_CSR_BASE 0x87000000 + #define AX3000_TIMER_CTRL 0x8A020000 #define AX3000_PLL_BASE 0x80000000 =20 @@ -69,6 +74,7 @@ typedef struct Ax3000SoCState { CadenceUARTState uart[AX3000_NUM_UARTS]; CadenceGPIOState gpio[AX3000_NUM_GPIOS]; AxiadoSDHCIState sdhci0; + AXIADOHCPState hcp; } Ax3000SoCState; =20 typedef struct Ax3000SoCClass { @@ -93,6 +99,14 @@ enum Ax3000Irqs { AX3000_GPIO5_IRQ =3D 188, AX3000_GPIO6_IRQ =3D 189, AX3000_GPIO7_IRQ =3D 190, + + /* + * HCP IRQ base =E2=80=94 10 contiguous SPIs : + * +0 =3D EIP-197 global + * +1..+4 =3D ring interfaces (4) + * +5..+9 =3D MAC IRQs (5) + */ + AX3000_HCP_IRQ_BASE =3D 0, }; =20 #endif /* AXIADO_AX3000_H */ --=20 2.34.1