From nobody Mon Sep 28 02:00:33 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; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785278370; cv=none; d=zohomail.com; s=zohoarc; b=bgooLG/l1z8vm2n1aJe3AFfFjgLDB5QJFnuj9J+6xvooNs5EFO7+vvPZwEIRWJNMOKY93KOK80z/Ucilp6YUaThlomuf2sscX3P6RfBQOSoRePEbg4AB7pC2j8aEqsu3y+mvP4DD+/gH7MeMzRmsGxe7ahD/XrmTNm9Jqaf+1l4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785278370; h=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=sMriARsa9eOcDpZtXd4roMj5Gf+r4mB80SLKoT5xREA=; b=RR3RzyltREZyGzHNscSpj4lWmTWIKOGz/bt6NvZ4wfYYviGFiiiEYGGUhZSk0cY9zMBLdeTKGe1bLuA61jta6NvHcOIZy11a2lDi4meEZo0QpM6jcjRFUr6FGATyoNuTnZAJ7YAHV4cjFXc5Jjh51Qls/xmHad/z4/1FptARIk4= ARC-Authentication-Results: i=1; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785278370808224.87706345402898; Tue, 28 Jul 2026 15:39:30 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1woqRd-0007FJ-SD; Tue, 28 Jul 2026 18:38:41 -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 1woqRa-0007D3-Nw for qemu-devel@nongnu.org; Tue, 28 Jul 2026 18:38:38 -0400 Received: from mail-yw1-x112a.google.com ([2607:f8b0:4864:20::112a]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1woqRY-00027j-9R for qemu-devel@nongnu.org; Tue, 28 Jul 2026 18:38:38 -0400 Received: by mail-yw1-x112a.google.com with SMTP id 00721157ae682-81ed2a06b9eso4454487b3.3 for ; Tue, 28 Jul 2026 15:38:35 -0700 (PDT) Received: from ip-172-32-0-41.us-east-2.compute.internal (ec2-3-143-144-70.us-east-2.compute.amazonaws.com. [3.143.144.70]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fa27592e9sm7541447b3.8.2026.07.28.15.38.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Jul 2026 15:38:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785278315; x=1785883115; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=sMriARsa9eOcDpZtXd4roMj5Gf+r4mB80SLKoT5xREA=; b=FV5OqUpsVD4ysjIzrCaBfCsRnsOyiBYcKaZnbQ8tLNGoiRcsw3ocCGnuJhxensTQvE 5OFUZ4W92Rhr7XMcoIgvLMIIs4MKNZFPCC+ivK1VDqZFDvkAcfs9UaLO1yfnKHhDqKT4 wWCFTPoL+yJRrpKwoQfxIbKJN3JHVYQoB4RB9SwhYpx2XOZPGZ+xkx06R9i4AhrVfOL+ QDQe+UXpRpxa95onG46VV/zGtYUWtVFXHIlTWjfl1GvZHKTGUBliLvMnq+lajXs1+Wq/ LqboTnfQV1xt5kai32lP4rFHdR5bWc8XoJ8emy3IFrFmdjMy7hyn2s/XGkdhD1Z7X3jx kD9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785278315; x=1785883115; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=sMriARsa9eOcDpZtXd4roMj5Gf+r4mB80SLKoT5xREA=; b=Ko7QZG9STVDu1fqMpxP4f5Jrx0qoDgTyWm7EoekjzCf+Rkx6qyrJ3YIoVxXSA6pes0 xaB+2SsswzhYrdCHLgEAspiUKZd6t2IlLdjUi1XD/rgblYw3fqKnej+t9rHMsDosvcbo RpIHe+MN1rkn+YyH1UNsGOoyuDcFvuQcIyVM2AFgojBgyAnLFIN5AbNfmMNFhlwep0I+ hfgNwhBPmsufQVIxnpoLoAYOUxqe+lZxfHlbfJAM1MHK4DhmW7DpNF9gM4K/Vs7sf+i4 DkYFFvZIVwp4fNiuk9zPho6cTB7V/j3+ozvRWjvIi3cq8BQU+Rnm1RLy55xNv325E9/+ 2kiQ== X-Gm-Message-State: AOJu0Yx/FgtdBT/HSoCBDXmhLaiJzJUnf6WzjIYkDmzECZnt4fZ3Y2yl h941i+w5xZbCgoGPgrmZTWH5c9PedhyNrkhKIdQQvyURWhmhc7ivtkpje/fiEFoh X-Gm-Gg: AR+sD135vN4ZXNioQ9VhIM0V8Ucurv4kovpsiytsDoApvpelbuzmgav68ZhnEUtykPr BA4+ULwY5iTwAImI2w4MgxnLwHR0Z+m66uz9cIET/NMWnB6ybmAiEjKVof3iM9K/HjIyrlolf/H C25ktvKAe29G/xzyjTr1IM5GWjxdWhGGqFsW2pg9JslUMEn1zyenYecAd3kYVQCsWm7NiY5DJKC IQbLRVYBbXi+dr7LYWSjPpOzLYPhsiK2cquKSkkvkyhVJlY9c5MPSsCywCOuQKJRJHPvZPjeLU4 zgk12+aZMaSjJRG65SSItSZll79dVOOIcUASgyPCoYHfN+JFyg87rJN/fhVjDc44E7825Mt3EJS hYqt7IKp6x2o1dYItWQ6fk4ONy2t8XYLh65sZw29kOERnMM2mjwIX22UKj2DKJ8Z3gLH7i5AUMM VQidMRhCJ7NSUxKoKw83t/+NrhjYKvVefa0DLoAAYJgsU6t3NEdCzcYoGDiScZ/a391vxciXwTe Izn30QeAFhLcDg0oo6C6nRTRLeH0w44fh3l+/WFZN51IHSsEnL87DoGuDDk7O4qzy1UTTIPyB4o Y5FMyYno1eSudkCDrFZA X-Received: by 2002:a05:690c:4b90:b0:81e:a4cf:131b with SMTP id 00721157ae682-81f99258c81mr19974947b3.22.1785278315080; Tue, 28 Jul 2026 15:38:35 -0700 (PDT) From: Nodoka Shibasaki To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Nodoka Shibasaki Subject: [RFC PATCH v2 1/2] hw/i2c: add xlnx-axi-iic, a Xilinx AXI IIC controller Date: Tue, 28 Jul 2026 22:38:28 +0000 Message-ID: <20260728223829.2613524-2-nodokaorganized@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260728223829.2613524-1-nodokaorganized@gmail.com> References: <20260715221400.1484805-1-nodokaorganized@gmail.com> <20260728223829.2613524-1-nodokaorganized@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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: pass client-ip=2607:f8b0:4864:20::112a; envelope-from=nodokaorganized@gmail.com; helo=mail-yw1-x112a.google.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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action 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 @gmail.com) X-ZM-MESSAGEID: 1785278372611158500 Content-Type: text/plain; charset="utf-8" Add a model of the AMD/Xilinx AXI IIC controller (LogiCORE IP, PG090), an I2C bus controller. QEMU has no AXI IIC model today; this lets a guest driver for it, and the I2C slaves behind it, be exercised without real hardware. The device is a SysBus device: it owns one I2C bus, decodes the AXI IIC register map, implements the controller's dynamic transfer mode, and drives a single level-triggered interrupt line while DGIER is enabled and IISR & IIER is set. It has a reset handler and migration state, and a register-level specification in docs/specs/xlnx-axi-iic.rst. Signed-off-by: Nodoka Shibasaki --- MAINTAINERS | 10 ++ docs/specs/index.rst | 1 + docs/specs/xlnx-axi-iic.rst | 62 ++++++++ hw/i2c/Kconfig | 4 + hw/i2c/meson.build | 1 + hw/i2c/xlnx-axi-iic.c | 284 ++++++++++++++++++++++++++++++++++ include/hw/i2c/xlnx-axi-iic.h | 69 +++++++++ 7 files changed, 431 insertions(+) create mode 100644 docs/specs/xlnx-axi-iic.rst create mode 100644 hw/i2c/xlnx-axi-iic.c create mode 100644 include/hw/i2c/xlnx-axi-iic.h diff --git a/MAINTAINERS b/MAINTAINERS index a28935c898..8339c9bf83 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2158,6 +2158,16 @@ S: Maintained F: hw/misc/edu.c F: docs/specs/edu.rst =20 +<<<<<<< Updated upstream +=3D=3D=3D=3D=3D=3D=3D +xiic-fpga-i2c +M: Nodoka Shibasaki +S: Maintained +F: hw/i2c/xlnx-axi-iic.c +F: include/hw/i2c/xlnx-axi-iic.h +F: docs/specs/xlnx-axi-iic.rst + +>>>>>>> Stashed changes IDE M: John Snow L: qemu-block@nongnu.org diff --git a/docs/specs/index.rst b/docs/specs/index.rst index b7909a108a..41b45a5be4 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -40,3 +40,4 @@ guest hardware that is specific to QEMU. riscv-aia aspeed-intc iommu-testdev + xlnx-axi-iic diff --git a/docs/specs/xlnx-axi-iic.rst b/docs/specs/xlnx-axi-iic.rst new file mode 100644 index 0000000000..a19b479522 --- /dev/null +++ b/docs/specs/xlnx-axi-iic.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Xilinx AXI IIC device +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``xlnx-axi-iic`` models the AMD/Xilinx AXI IIC (LogiCORE IP, documented in +Xilinx PG090) I2C bus controller. It is a SysBus device: a board or a pare= nt +device maps its single MMIO region and connects its interrupt line, and I2C +slave models are attached to the ``i2c`` bus it creates. + +The model implements the controller's *dynamic* transfer mode, which is wh= at +a guest driver uses by default. + +Properties +---------- + +``bus-name`` + Name given to the I2C bus the controller creates (default ``i2c``). A pa= rent + device that instantiates several controllers uses this to give each bus a + unique, user-referenceable name. + +MMIO register map +----------------- + +Offsets are relative to the start of the controller's MMIO region. + + 0x1C (RW) : DGIER, global interrupt enable (bit 31) + 0x20 (RW) : IISR, interrupt status; write-1-to-clear + 0x28 (RW) : IIER, interrupt enable + 0x40 (WO) : RESETR, soft reset (write 0xA) + 0x100 (RW) : CR, control + 0x104 (RO) : SR, status (computed) + 0x108 (WO) : DTR, tx data and dynamic START (bit 8) / STOP (bit 9) + 0x10C (RO) : DRR, rx data + 0x114 (RO) : TFO, tx FIFO occupancy (always 0; the FIFO drains immediate= ly) + 0x118 (RO) : RFO, rx FIFO occupancy + 0x120 (RW) : RFD, rx FIFO programmable depth + +Status register (SR) bits: 0x04 bus busy, 0x20 rx FIFO full, 0x40 rx FIFO +empty, 0x80 tx FIFO empty. Interrupt (IISR/IIER) bits: 0x01 arbitration lo= st, +0x02 tx error / NACK, 0x04 tx FIFO empty, 0x08 rx FIFO full, 0x10 bus-not-= busy. + +Dynamic-mode transfers +---------------------- + +The low 8 bits of a DTR write are the data byte; bit 8 (START) frames the +8-bit address (bit 0 is the read/write flag) that opens a transfer, and bi= t 9 +(STOP) ends it. + +- Write: DTR <- addr|START, then each data byte, the last with STOP. +- Read: DTR <- addr|START (read flag set), then DTR <- count|STOP. The + controller clocks ``count`` bytes from the slave into the rx FIFO, raises + IISR.RX_FULL, and raises IISR.BNB once the FIFO has been drained through= DRR. + +A slave that does not acknowledge sets IISR.TX_ERROR and releases the bus. + +Interrupt +--------- + +The controller drives a single level-triggered output line, asserted while +DGIER is enabled and ``IISR & IIER`` is non-zero, and deasserted when the = guest +clears the pending, enabled causes. diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 0766130b59..4faf2bda99 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -43,6 +43,10 @@ config ALLWINNER_I2C bool select I2C =20 +config XLNX_AXI_IIC + bool + select I2C + config PCA954X bool select I2C diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index 88aea35662..939f02f03b 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -9,6 +9,7 @@ i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos42= 10_i2c.c')) i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c')) i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c')) i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c')) +i2c_ss.add(when: 'CONFIG_XLNX_AXI_IIC', if_true: files('xlnx-axi-iic.c')) i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c')) i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c')) i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c= ')) diff --git a/hw/i2c/xlnx-axi-iic.c b/hw/i2c/xlnx-axi-iic.c new file mode 100644 index 0000000000..c27398c319 --- /dev/null +++ b/hw/i2c/xlnx-axi-iic.c @@ -0,0 +1,284 @@ +/* + * xlnx-axi-iic.c - QEMU model of the Xilinx AXI IIC (LogiCORE IP) control= ler. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "hw/core/irq.h" +#include "hw/core/sysbus.h" +#include "hw/core/qdev-properties.h" +#include "hw/i2c/xlnx-axi-iic.h" + +static bool xlnx_axi_iic_pending(XlnxAxiIicState *s) +{ + if (!(s->dgier & XLNX_AXI_IIC_GINTR_ENABLE_MASK)) { + return false; + } + return (s->isr & s->ier) !=3D 0; +} + +static void xlnx_axi_iic_update_irq(XlnxAxiIicState *s) +{ + qemu_set_irq(s->irq, xlnx_axi_iic_pending(s)); +} + +static void xlnx_axi_iic_reset_regs(XlnxAxiIicState *s) +{ + if (s->in_xfer) { + i2c_end_transfer(s->bus); + } + s->cr =3D 0; + s->isr =3D 0; + s->ier =3D 0; + s->dgier =3D 0; + s->rfd =3D 0; + s->in_xfer =3D false; + s->is_recv =3D false; + s->stop_pending =3D false; + s->rx_len =3D 0; + s->rx_pos =3D 0; + memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); +} + +static uint32_t xlnx_axi_iic_status(XlnxAxiIicState *s) +{ + uint32_t sr =3D XLNX_AXI_IIC_SR_TX_FIFO_EMPTY_MASK; + + sr |=3D (s->rx_pos >=3D s->rx_len) ? XLNX_AXI_IIC_SR_RX_FIFO_EMPTY_MASK + : XLNX_AXI_IIC_SR_RX_FIFO_FULL_MASK; + if (s->in_xfer) { + sr |=3D XLNX_AXI_IIC_SR_BUS_BUSY_MASK; + } + return sr; +} + +static void xlnx_axi_iic_fail(XlnxAxiIicState *s) +{ + i2c_end_transfer(s->bus); + s->in_xfer =3D false; + s->isr |=3D XLNX_AXI_IIC_INTR_TX_ERROR_MASK | XLNX_AXI_IIC_INTR_BNB_MA= SK; + xlnx_axi_iic_update_irq(s); +} + +static uint64_t xlnx_axi_iic_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxAxiIicState *s =3D opaque; + bool rx_empty =3D s->rx_pos >=3D s->rx_len; + uint64_t val =3D 0; + + switch (addr) { + case XLNX_AXI_IIC_SR: + val =3D xlnx_axi_iic_status(s); + break; + case XLNX_AXI_IIC_IISR: + val =3D s->isr; + break; + case XLNX_AXI_IIC_IIER: + val =3D s->ier; + break; + case XLNX_AXI_IIC_DGIER: + val =3D s->dgier; + break; + case XLNX_AXI_IIC_CR: + val =3D s->cr; + break; + case XLNX_AXI_IIC_RFD: + val =3D s->rfd; + break; + case XLNX_AXI_IIC_RFO: + val =3D rx_empty ? 0 : (s->rx_len - s->rx_pos - 1); + break; + case XLNX_AXI_IIC_DRR: + if (!rx_empty) { + val =3D s->rx_fifo[s->rx_pos++]; + if (s->rx_pos >=3D s->rx_len) { + s->isr &=3D ~(uint32_t)XLNX_AXI_IIC_INTR_RX_FULL_MASK; + if (s->stop_pending) { + s->stop_pending =3D false; + s->in_xfer =3D false; + s->isr |=3D XLNX_AXI_IIC_INTR_BNB_MASK; + } + xlnx_axi_iic_update_irq(s); + } + } + break; + default: + break; + } + return val; +} + +static void xlnx_axi_iic_dtr_write(XlnxAxiIicState *s, uint64_t val) +{ + uint16_t word =3D val & 0xFFFF; + bool stop =3D word & XLNX_AXI_IIC_TX_DYN_STOP_MASK; + + if (word & XLNX_AXI_IIC_TX_DYN_START_MASK) { + uint8_t addr8 =3D word & 0xFF; + s->is_recv =3D addr8 & 1; + s->rx_len =3D 0; + s->rx_pos =3D 0; + + int nack =3D s->is_recv ? i2c_start_recv(s->bus, addr8 >> 1) + : i2c_start_send(s->bus, addr8 >> 1); + s->in_xfer =3D true; + if (nack) { + xlnx_axi_iic_fail(s); + } + return; + } + + if (!s->in_xfer) { + return; + } + + if (s->is_recv) { + unsigned room =3D XLNX_AXI_IIC_RX_FIFO_MAX - s->rx_len; + unsigned n =3D MIN((unsigned)(word & 0xFF), room); + + for (unsigned i =3D 0; i < n; i++) { + s->rx_fifo[s->rx_len++] =3D i2c_recv(s->bus); + } + if (stop) { + i2c_end_transfer(s->bus); + s->stop_pending =3D true; + } + if (s->rx_len > 0) { + s->isr |=3D XLNX_AXI_IIC_INTR_RX_FULL_MASK; + } + xlnx_axi_iic_update_irq(s); + return; + } + + if (i2c_send(s->bus, word & 0xFF)) { + xlnx_axi_iic_fail(s); + return; + } + if (stop) { + i2c_end_transfer(s->bus); + s->in_xfer =3D false; + s->isr |=3D XLNX_AXI_IIC_INTR_TX_EMPTY_MASK | XLNX_AXI_IIC_INTR_BN= B_MASK; + } else { + s->isr |=3D XLNX_AXI_IIC_INTR_TX_EMPTY_MASK; + } + xlnx_axi_iic_update_irq(s); +} + +static void xlnx_axi_iic_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + XlnxAxiIicState *s =3D opaque; + + switch (addr) { + case XLNX_AXI_IIC_RESETR: + if ((val & 0xf) =3D=3D XLNX_AXI_IIC_RESET_MASK) { + device_cold_reset(DEVICE(s)); + } + break; + case XLNX_AXI_IIC_CR: + s->cr =3D val; + break; + case XLNX_AXI_IIC_DGIER: + s->dgier =3D val; + xlnx_axi_iic_update_irq(s); + break; + case XLNX_AXI_IIC_IIER: + s->ier =3D val; + xlnx_axi_iic_update_irq(s); + break; + case XLNX_AXI_IIC_IISR: + s->isr &=3D ~(uint32_t)val; + xlnx_axi_iic_update_irq(s); + break; + case XLNX_AXI_IIC_RFD: + s->rfd =3D val; + break; + case XLNX_AXI_IIC_DTR: + xlnx_axi_iic_dtr_write(s, val); + break; + default: + break; + } +} + +static const MemoryRegionOps xlnx_axi_iic_ops =3D { + .read =3D xlnx_axi_iic_read, + .write =3D xlnx_axi_iic_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { .min_access_size =3D 1, .max_access_size =3D 4 }, + .valid =3D { .min_access_size =3D 1, .max_access_size =3D 4 }, +}; + +static void xlnx_axi_iic_realize(DeviceState *dev, Error **errp) +{ + XlnxAxiIicState *s =3D XLNX_AXI_IIC(dev); + + memory_region_init_io(&s->mmio, OBJECT(s), &xlnx_axi_iic_ops, s, + TYPE_XLNX_AXI_IIC, XLNX_AXI_IIC_REGS_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + s->bus =3D i2c_init_bus(dev, s->bus_name ? s->bus_name : "i2c"); +} + +static void xlnx_axi_iic_reset_hold(Object *obj, ResetType type) +{ + XlnxAxiIicState *s =3D XLNX_AXI_IIC(obj); + + xlnx_axi_iic_reset_regs(s); + xlnx_axi_iic_update_irq(s); +} + +static const VMStateDescription vmstate_xlnx_axi_iic =3D { + .name =3D TYPE_XLNX_AXI_IIC, + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(cr, XlnxAxiIicState), + VMSTATE_UINT32(isr, XlnxAxiIicState), + VMSTATE_UINT32(ier, XlnxAxiIicState), + VMSTATE_UINT32(dgier, XlnxAxiIicState), + VMSTATE_UINT32(rfd, XlnxAxiIicState), + VMSTATE_BOOL(in_xfer, XlnxAxiIicState), + VMSTATE_BOOL(is_recv, XlnxAxiIicState), + VMSTATE_BOOL(stop_pending, XlnxAxiIicState), + VMSTATE_UINT8_ARRAY(rx_fifo, XlnxAxiIicState, XLNX_AXI_IIC_RX_FIFO= _MAX), + VMSTATE_INT32(rx_len, XlnxAxiIicState), + VMSTATE_INT32(rx_pos, XlnxAxiIicState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property xlnx_axi_iic_props[] =3D { + DEFINE_PROP_STRING("bus-name", XlnxAxiIicState, bus_name), +}; + +static void xlnx_axi_iic_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D xlnx_axi_iic_realize; + dc->vmsd =3D &vmstate_xlnx_axi_iic; + rc->phases.hold =3D xlnx_axi_iic_reset_hold; + dc->desc =3D "Xilinx AXI IIC controller"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, xlnx_axi_iic_props); +} + +static const TypeInfo xlnx_axi_iic_info =3D { + .name =3D TYPE_XLNX_AXI_IIC, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(XlnxAxiIicState), + .class_init =3D xlnx_axi_iic_class_init, +}; + +static void xlnx_axi_iic_register_types(void) +{ + type_register_static(&xlnx_axi_iic_info); +} + +type_init(xlnx_axi_iic_register_types) diff --git a/include/hw/i2c/xlnx-axi-iic.h b/include/hw/i2c/xlnx-axi-iic.h new file mode 100644 index 0000000000..daa65c4140 --- /dev/null +++ b/include/hw/i2c/xlnx-axi-iic.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef HW_I2C_XLNX_AXI_IIC_H +#define HW_I2C_XLNX_AXI_IIC_H + +#include "hw/core/sysbus.h" +#include "hw/i2c/i2c.h" +#include "qom/object.h" + +#define TYPE_XLNX_AXI_IIC "xlnx-axi-iic" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxAxiIicState, XLNX_AXI_IIC) + +#define XLNX_AXI_IIC_REGS_SIZE 0x1000 +#define XLNX_AXI_IIC_RX_FIFO_MAX 256 + +#define XLNX_AXI_IIC_DGIER 0x1C +#define XLNX_AXI_IIC_IISR 0x20 +#define XLNX_AXI_IIC_IIER 0x28 +#define XLNX_AXI_IIC_RESETR 0x40 +#define XLNX_AXI_IIC_CR 0x100 +#define XLNX_AXI_IIC_SR 0x104 +#define XLNX_AXI_IIC_DTR 0x108 +#define XLNX_AXI_IIC_DRR 0x10C +#define XLNX_AXI_IIC_TFO 0x114 +#define XLNX_AXI_IIC_RFO 0x118 +#define XLNX_AXI_IIC_RFD 0x120 + +#define XLNX_AXI_IIC_RESET_MASK 0xA + +#define XLNX_AXI_IIC_SR_BUS_BUSY_MASK 0x04 +#define XLNX_AXI_IIC_SR_RX_FIFO_FULL_MASK 0x20 +#define XLNX_AXI_IIC_SR_RX_FIFO_EMPTY_MASK 0x40 +#define XLNX_AXI_IIC_SR_TX_FIFO_EMPTY_MASK 0x80 + +#define XLNX_AXI_IIC_INTR_ARB_LOST_MASK 0x01 +#define XLNX_AXI_IIC_INTR_TX_ERROR_MASK 0x02 +#define XLNX_AXI_IIC_INTR_TX_EMPTY_MASK 0x04 +#define XLNX_AXI_IIC_INTR_RX_FULL_MASK 0x08 +#define XLNX_AXI_IIC_INTR_BNB_MASK 0x10 + +#define XLNX_AXI_IIC_GINTR_ENABLE_MASK 0x80000000UL + +#define XLNX_AXI_IIC_TX_DYN_START_MASK 0x0100 +#define XLNX_AXI_IIC_TX_DYN_STOP_MASK 0x0200 + +struct XlnxAxiIicState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + I2CBus *bus; + char *bus_name; + + uint32_t cr; + uint32_t isr; + uint32_t ier; + uint32_t dgier; + uint32_t rfd; + + bool in_xfer; + bool is_recv; + bool stop_pending; + + uint8_t rx_fifo[XLNX_AXI_IIC_RX_FIFO_MAX]; + int rx_len; + int rx_pos; +}; + +#endif --=20 2.50.1 From nobody Mon Sep 28 02:00:33 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; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785278370; cv=none; d=zohomail.com; s=zohoarc; b=CHBN8fFZEOZtJF1juojLO7844xQ8TNQmpKeAhG5F/JZiYXooiXf4cgNwAjuBD2ko+hy8+VeIN7iRDEZu7ITI1cmepkrKMTDbhbDkI8aiPVgKbtQypkLR+SWvqCMtQl6rcpKqf7l3Se/juzTzlM53MmhfQM9jA8NgEhgqsC4OqBw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785278370; h=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=JC70vhGM2mm4Eqnz6rd7PaOFmvD+mZG2HzV8c+5437k=; b=IyyQupqotrTIQ+IsGKUn8doxsq2WJyERN5wDCBb5Ok1UgTngZT+FqL084pT+7EUsf5wg/kqftyOZ5udjET+nX8GPcKA0CW/XsoI0qFXXNuQOmNK3y81FV84meA92uvFNFKrC7XX/K0vooBvd0tcFOHodVK3U/dpBr4MsXWTuBKU= ARC-Authentication-Results: i=1; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785278370807650.876249039006; Tue, 28 Jul 2026 15:39:30 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1woqRf-0007Fc-IJ; Tue, 28 Jul 2026 18:38:43 -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 1woqRd-0007FH-Qs for qemu-devel@nongnu.org; Tue, 28 Jul 2026 18:38:41 -0400 Received: from mail-yw1-x1136.google.com ([2607:f8b0:4864:20::1136]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1woqRb-00027w-B6 for qemu-devel@nongnu.org; Tue, 28 Jul 2026 18:38:41 -0400 Received: by mail-yw1-x1136.google.com with SMTP id 00721157ae682-81dfdbd86d1so4423507b3.1 for ; Tue, 28 Jul 2026 15:38:38 -0700 (PDT) Received: from ip-172-32-0-41.us-east-2.compute.internal (ec2-3-143-144-70.us-east-2.compute.amazonaws.com. [3.143.144.70]) by smtp.gmail.com with ESMTPSA id 00721157ae682-81fa27592e9sm7541447b3.8.2026.07.28.15.38.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 28 Jul 2026 15:38:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785278318; x=1785883118; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=JC70vhGM2mm4Eqnz6rd7PaOFmvD+mZG2HzV8c+5437k=; b=H8aZ0zOUMr2acK1nXmZItO3D3qbC8qWdJ0gyJywyRWYX7KvRjLahPNTPGJl537f8W5 DFh7YC+GT2orGUyKs6aTUD3lnEvGpil7Od8Xfk9c+C/UiDKGehNahDSHPdw+u07XpNve srke/QDVmj1BPuEpC0CD5K+zwLJJX42VsGSb7oLaopR9g+MJM44MFSq+f92WEOZBfUBs H/NotL2xCjqH4IsRLqDMupE4C/Yunizaj+cObYIRd3WIYA/R6P4B+xClkVae692PHXWx JkjMZn2m1AX7GIyN+4lcMqy+w4JCl9pkFMKKbKl2Qicb6ZBzL4S2Shmh898hg2A+zF+e JDQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785278318; x=1785883118; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=JC70vhGM2mm4Eqnz6rd7PaOFmvD+mZG2HzV8c+5437k=; b=O0Q0i7UKLOfpK3bjL4nWWiOz0AioYQe+mCofpve5IYp6CkuWnv6UMIZFVnEEIGo6Li VWPq6M0sSPzJqECkjnSQcD2rxg6ZVlxOMH3iaw4kv/e005sYYf4DCSJTj2I9XoJmUGXH iyuYJfEdnozN4AgASZjnH65qh2VuDlroUSJGAPGI4TfQkMd1p4BHJKW3UbXsu4lS0hk1 mdG2nw11hMx0opEm1U/8CKmsJhYLLvAnDDg1O2siQoT2x+yiDqYs+CTj8y5P4e62g32U 41VjD9dutnDb29dkWpFAiHx8gT+yeiV/SajRwW+DPzmhf7wWHTx1MkPJMXlWVk1q45bn d1Mg== X-Gm-Message-State: AOJu0YxwTusBneiz9N4MsX+fVjWLfx1yt6xU3OLmNmzN1zgD8HsfYTCP 1Xa1GDAhkzkuvJeKbzDnv6Ba9KHmA3E3eoKkqy5mVGSNmP2GhQBJzmv6ls/SzEDM X-Gm-Gg: AR+sD11emtUuhyX65cv/FDlLiYuUa6pd0ECjOnxNEDmMqAjGXPQ0t2AIh2SqjouC7Hl G0vvoMOxA2OEvgr7pORisMA2dQNa/C4nqBmR5Hha31gu/GFpaD0uybP1mCDiTh87NjaIkEmlRQm hskc2Rd7y54dO+REAZg8LuZdN19PpyRDR+hlTSB1Xykvr8H4ylRKNWY9LVcMxhLVQg5bU0CJa6p 1VJUNj+B4J0Bc2xvIjVJqzc4ZrghmqHe4BjWsmcpwq6Hdzw12R6A8yfsJr2PXARSBhFXIxhc382 Xokzi8++qrso18LUrGC9FhP7OzLz4JDMyVe/86DTLalrpdxoouXMMbyxkksx5GEVkegntxNo2ip 4VlSTDm7k65s76B/6DHT2GVL5d1kzzGm5n5KN9szdegMNyYLzh6b+cGDrM3Vi0qooXAyygHWctI bOZXMN+49GT3VTpYa9J+seDUhhkPyEsg72lYdbSunzDG7hVBuvV5vH1MMhFpn5/X8w0c1U2bJZi +dSZp4ZceKvbniSzW8KQ8AOVOCTRwuvhX54vjOdaHOIq2f8E28rqQsTnJn8BQscAUIg/czbwGtT HnzV/4XckPt0Ap9ZCu0iKoAIzUUu2Ug= X-Received: by 2002:a05:690c:6513:b0:81e:6fd:93b4 with SMTP id 00721157ae682-81f990e37e0mr23498787b3.19.1785278317664; Tue, 28 Jul 2026 15:38:37 -0700 (PDT) From: Nodoka Shibasaki To: qemu-devel@nongnu.org Cc: Paolo Bonzini , Nodoka Shibasaki Subject: [RFC PATCH v2 2/2] hw/misc: add xiic-fpga-i2c, a PCIe FPGA embedding xlnx-axi-iic cores Date: Tue, 28 Jul 2026 22:38:29 +0000 Message-ID: <20260728223829.2613524-3-nodokaorganized@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260728223829.2613524-1-nodokaorganized@gmail.com> References: <20260715221400.1484805-1-nodokaorganized@gmail.com> <20260728223829.2613524-1-nodokaorganized@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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: pass client-ip=2607:f8b0:4864:20::1136; envelope-from=nodokaorganized@gmail.com; helo=mail-yw1-x1136.google.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, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action 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 @gmail.com) X-ZM-MESSAGEID: 1785278372666158500 Content-Type: text/plain; charset="utf-8" Add a PCIe function that embeds N xlnx-axi-iic controllers, maps each core's register window into BAR0, and adds a top-level interrupt block that aggregates the per-channel level lines into a bit-per-channel status register delivered on a single MSI vector (with a legacy INTx fallback), performing the level-to-edge translation a guest driver expects. The aggregator's register layout models a specific FPGA carrier rather than a standardized controller, so the device is gated behind CONFIG_XIIC_FPGA_I2C (default y if TEST_DEVICES). A qtest drives a transfer through BAR0 to a tmp105 slave and checks register, transfer and NACK behavior; the test slaves it needs are enabled in the x86_64-softmmu test config. Signed-off-by: Nodoka Shibasaki --- MAINTAINERS | 8 +- configs/devices/x86_64-softmmu/default.mak | 5 + hw/misc/Kconfig | 6 + hw/misc/meson.build | 1 + hw/misc/xiic_fpga_i2c.c | 261 +++++++++++++++++++++ tests/qtest/meson.build | 2 + tests/qtest/xiic-fpga-i2c-test.c | 159 +++++++++++++ 7 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 hw/misc/xiic_fpga_i2c.c create mode 100644 tests/qtest/xiic-fpga-i2c-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 8339c9bf83..85f03cf4b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2158,8 +2158,6 @@ S: Maintained F: hw/misc/edu.c F: docs/specs/edu.rst =20 -<<<<<<< Updated upstream -=3D=3D=3D=3D=3D=3D=3D xiic-fpga-i2c M: Nodoka Shibasaki S: Maintained @@ -2167,7 +2165,11 @@ F: hw/i2c/xlnx-axi-iic.c F: include/hw/i2c/xlnx-axi-iic.h F: docs/specs/xlnx-axi-iic.rst =20 ->>>>>>> Stashed changes +xiic-fpga-i2c +M: Nodoka Shibasaki +S: Maintained +F: hw/misc/xiic_fpga_i2c.c + IDE M: John Snow L: qemu-block@nongnu.org diff --git a/configs/devices/x86_64-softmmu/default.mak b/configs/devices/x= 86_64-softmmu/default.mak index ddfc2ea626..d7a88deccc 100644 --- a/configs/devices/x86_64-softmmu/default.mak +++ b/configs/devices/x86_64-softmmu/default.mak @@ -1,3 +1,8 @@ # Default configuration for x86_64-softmmu =20 include ../i386-softmmu/default.mak + +# Enable I2C slave models used by the xiic-fpga-i2c qtest. +CONFIG_AT24C=3Dy +CONFIG_TMP105=3Dy +CONFIG_TMP421=3Dy diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1543ee6653..12acca66bb 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -25,6 +25,12 @@ config PCI_TESTDEV default y if TEST_DEVICES depends on PCI =20 +config XIIC_FPGA_I2C + bool + default y if TEST_DEVICES + depends on PCI && MSI_NONBROKEN + select XLNX_AXI_IIC + config IOMMU_TESTDEV bool default y if TEST_DEVICES diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 23265f6035..69d816cad9 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vm= coreinfo.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) +system_ss.add(when: 'CONFIG_XIIC_FPGA_I2C', if_true: files('xiic_fpga_i2c.= c')) system_ss.add(when: 'CONFIG_IOMMU_TESTDEV', if_true: files('iommu-testdev.= c')) system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) diff --git a/hw/misc/xiic_fpga_i2c.c b/hw/misc/xiic_fpga_i2c.c new file mode 100644 index 0000000000..c4425fa84d --- /dev/null +++ b/hw/misc/xiic_fpga_i2c.c @@ -0,0 +1,261 @@ +/* + * xiic_fpga_i2c.c - QEMU model of a PCIe FPGA that embeds Xilinx AXI-IIC + * controllers behind a shared MSI interrupt aggregator. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/host-utils.h" +#include "migration/vmstate.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/msi.h" +#include "hw/core/sysbus.h" +#include "hw/core/qdev-properties.h" +#include "hw/i2c/xlnx-axi-iic.h" + +#define TYPE_XIIC_FPGA_I2C "xiic-fpga-i2c" +OBJECT_DECLARE_SIMPLE_TYPE(XiicFpgaI2cState, XIIC_FPGA_I2C) + +#define XIIC_FPGA_MAX_CHANNELS 32 + +#define XIIC_FPGA_IRQ_STATUS_REG 0 +#define XIIC_FPGA_IRQ_UNMASK_REG 4 +#define XIIC_FPGA_IRQ_REGION_SIZE 8 + +struct XiicFpgaI2cState { + PCIDevice parent_obj; + + MemoryRegion bar0; + MemoryRegion irq_mmio; + + uint32_t num_channels; + uint32_t ch_base_offset; + uint32_t ch_stride; + uint32_t bar_size; + uint32_t num_msi_vectors; + + uint32_t irq_status_offset; + uint32_t irq_unmask_offset; + uint32_t irq_msi_vector; + + uint32_t irq_status; + uint32_t irq_unmask; + bool msi_asserted; + + XlnxAxiIicState chan[XIIC_FPGA_MAX_CHANNELS]; +}; + +static void xiic_fpga_update_msi(XiicFpgaI2cState *s) +{ + PCIDevice *pci_dev =3D PCI_DEVICE(s); + uint32_t active =3D s->irq_status & s->irq_unmask; + + if (msi_enabled(pci_dev)) { + if (active && !s->msi_asserted) { + msi_notify(pci_dev, s->irq_msi_vector); + s->msi_asserted =3D true; + } else if (!active) { + s->msi_asserted =3D false; + } + return; + } + + pci_set_irq(pci_dev, active !=3D 0); +} + +static void xiic_fpga_irq_set(void *opaque, int n, int level) +{ + XiicFpgaI2cState *s =3D opaque; + + if (level) { + s->irq_status |=3D (1u << n); + } else { + s->irq_status &=3D ~(1u << n); + } + xiic_fpga_update_msi(s); +} + +static uint64_t xiic_fpga_irq_read(void *opaque, hwaddr addr, unsigned siz= e) +{ + XiicFpgaI2cState *s =3D opaque; + + switch (addr) { + case XIIC_FPGA_IRQ_STATUS_REG: + return s->irq_status; + case XIIC_FPGA_IRQ_UNMASK_REG: + return s->irq_unmask; + default: + return 0; + } +} + +static void xiic_fpga_irq_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + XiicFpgaI2cState *s =3D opaque; + + switch (addr) { + case XIIC_FPGA_IRQ_STATUS_REG: + s->msi_asserted =3D false; + xiic_fpga_update_msi(s); + break; + case XIIC_FPGA_IRQ_UNMASK_REG: + s->irq_unmask =3D val; + xiic_fpga_update_msi(s); + break; + default: + break; + } +} + +static const MemoryRegionOps xiic_fpga_irq_ops =3D { + .read =3D xiic_fpga_irq_read, + .write =3D xiic_fpga_irq_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { .min_access_size =3D 1, .max_access_size =3D 4 }, + .valid =3D { .min_access_size =3D 1, .max_access_size =3D 4 }, +}; + +static void xiic_fpga_i2c_realize(PCIDevice *pci_dev, Error **errp) +{ + XiicFpgaI2cState *s =3D XIIC_FPGA_I2C(pci_dev); + + if (s->num_channels < 1 || s->num_channels > XIIC_FPGA_MAX_CHANNELS) { + error_setg(errp, "num-channels must be between 1 and %d", + XIIC_FPGA_MAX_CHANNELS); + return; + } + if (s->irq_unmask_offset !=3D s->irq_status_offset + 4) { + error_setg(errp, "irq-unmask-offset must be irq-status-offset + 4"= ); + return; + } + + if (s->bar_size < s->ch_base_offset + s->num_channels * s->ch_stride) { + s->bar_size =3D s->ch_base_offset + s->num_channels * s->ch_stride; + } + if (s->bar_size < s->irq_status_offset + XIIC_FPGA_IRQ_REGION_SIZE) { + s->bar_size =3D s->irq_status_offset + XIIC_FPGA_IRQ_REGION_SIZE; + } + s->bar_size =3D pow2ceil(s->bar_size); + + s->num_msi_vectors =3D pow2ceil(s->num_channels); + if (s->num_msi_vectors > 32) { + s->num_msi_vectors =3D 32; + } + if (s->irq_msi_vector >=3D s->num_msi_vectors) { + error_setg(errp, "irq-msi-vector %u out of range (0..%u)", + s->irq_msi_vector, s->num_msi_vectors - 1); + return; + } + + memory_region_init(&s->bar0, OBJECT(s), "xiic-fpga-i2c-bar0", s->bar_s= ize); + + qdev_init_gpio_in(DEVICE(s), xiic_fpga_irq_set, s->num_channels); + + for (unsigned i =3D 0; i < s->num_channels; i++) { + g_autofree char *name =3D g_strdup_printf("channel[%u]", i); + g_autofree char *bus_name =3D g_strdup_printf("xiic-fpga-i2c.%u", = i); + object_initialize_child(OBJECT(s), name, &s->chan[i], + TYPE_XLNX_AXI_IIC); + qdev_prop_set_string(DEVICE(&s->chan[i]), "bus-name", bus_name); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->chan[i]), errp)) { + return; + } + memory_region_add_subregion(&s->bar0, + s->ch_base_offset + i * s->ch_stride, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->chan[i]), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->chan[i]), 0, + qdev_get_gpio_in(DEVICE(s), i)); + } + + memory_region_init_io(&s->irq_mmio, OBJECT(s), &xiic_fpga_irq_ops, s, + "xiic-fpga-i2c-irq", XIIC_FPGA_IRQ_REGION_SIZE); + memory_region_add_subregion(&s->bar0, s->irq_status_offset, &s->irq_mm= io); + + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); + pci_config_set_interrupt_pin(pci_dev->config, 1); + + if (msi_init(pci_dev, 0, s->num_msi_vectors, true, false, errp) < 0) { + error_prepend(errp, "xiic-fpga-i2c: failed to init MSI: "); + return; + } +} + +static void xiic_fpga_i2c_exit(PCIDevice *pci_dev) +{ + msi_uninit(pci_dev); +} + +static void xiic_fpga_i2c_reset_hold(Object *obj, ResetType type) +{ + XiicFpgaI2cState *s =3D XIIC_FPGA_I2C(obj); + + s->irq_status =3D 0; + s->irq_unmask =3D 0; + s->msi_asserted =3D false; +} + +static const VMStateDescription vmstate_xiic_fpga_i2c =3D { + .name =3D "xiic-fpga-i2c", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (const VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, XiicFpgaI2cState), + VMSTATE_UINT32(irq_status, XiicFpgaI2cState), + VMSTATE_UINT32(irq_unmask, XiicFpgaI2cState), + VMSTATE_BOOL(msi_asserted, XiicFpgaI2cState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property xiic_fpga_i2c_props[] =3D { + DEFINE_PROP_UINT32("num-channels", XiicFpgaI2cState, num_channels, 4), + DEFINE_PROP_UINT32("ch-base-offset", XiicFpgaI2cState, ch_base_offset,= 0x0), + DEFINE_PROP_UINT32("ch-stride", XiicFpgaI2cState, ch_stride, 0x1000), + DEFINE_PROP_UINT32("bar-size", XiicFpgaI2cState, bar_size, 0x8000), + DEFINE_PROP_UINT32("irq-status-offset", XiicFpgaI2cState, + irq_status_offset, 0x6000), + DEFINE_PROP_UINT32("irq-unmask-offset", XiicFpgaI2cState, + irq_unmask_offset, 0x6004), + DEFINE_PROP_UINT32("irq-msi-vector", XiicFpgaI2cState, irq_msi_vector,= 0), +}; + +static void xiic_fpga_i2c_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + PCIDeviceClass *k =3D PCI_DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + k->realize =3D xiic_fpga_i2c_realize; + k->exit =3D xiic_fpga_i2c_exit; + k->vendor_id =3D 0x10ee; + k->device_id =3D 0x7021; + k->revision =3D 0x01; + k->class_id =3D PCI_CLASS_OTHERS; + + rc->phases.hold =3D xiic_fpga_i2c_reset_hold; + dc->vmsd =3D &vmstate_xiic_fpga_i2c; + dc->desc =3D "FPGA I2C (Xilinx AXI-IIC) emulation"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, xiic_fpga_i2c_props); +} + +static const TypeInfo xiic_fpga_i2c_info =3D { + .name =3D TYPE_XIIC_FPGA_I2C, + .parent =3D TYPE_PCI_DEVICE, + .instance_size =3D sizeof(XiicFpgaI2cState), + .class_init =3D xiic_fpga_i2c_class_init, + .interfaces =3D (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void xiic_fpga_i2c_register_types(void) +{ + type_register_static(&xiic_fpga_i2c_info); +} + +type_init(xiic_fpga_i2c_register_types) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..7abd2c7520 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -74,6 +74,8 @@ qtests_i386 =3D \ (config_all_devices.has_key('CONFIG_WDT_IB700') ? ['wdt_ib700-test'] : [= ]) + \ (config_all_devices.has_key('CONFIG_PVPANIC_ISA') ? ['pvpanic-test'] : [= ]) + \ (config_all_devices.has_key('CONFIG_PVPANIC_PCI') ? ['pvpanic-pci-test']= : []) + \ + (config_all_devices.has_key('CONFIG_XIIC_FPGA_I2C') and + config_all_devices.has_key('CONFIG_TMP105') ? ['xiic-fpga-i2c-test'] : = []) + \ (config_all_devices.has_key('CONFIG_HDA') ? ['intel-hda-test'] : []) + = \ (config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : [= ]) + \ (config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) += \ diff --git a/tests/qtest/xiic-fpga-i2c-test.c b/tests/qtest/xiic-fpga-i2c-t= est.c new file mode 100644 index 0000000000..8c5f862d7c --- /dev/null +++ b/tests/qtest/xiic-fpga-i2c-test.c @@ -0,0 +1,159 @@ +/* + * QTest for the xiic-fpga-i2c PCIe FPGA and its embedded xlnx-axi-iic cor= es. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/pci.h" +#include "libqos/pci-pc.h" + +#define XIIC_IISR 0x20 +#define XIIC_IIER 0x28 +#define XIIC_DGIER 0x1C +#define XIIC_SR 0x104 +#define XIIC_DTR 0x108 +#define XIIC_DRR 0x10C + +#define XIIC_SR_RX_FIFO_EMPTY 0x40 +#define XIIC_SR_TX_FIFO_EMPTY 0x80 +#define XIIC_SR_BUS_BUSY 0x04 + +#define XIIC_INTR_TX_ERROR 0x02 +#define XIIC_INTR_BNB 0x10 + +#define XIIC_DYN_START 0x100 +#define XIIC_DYN_STOP 0x200 + +#define TMP105_ADDR 0x4c +#define TMP105_REG_CONFIG 0x01 + +typedef struct { + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; +} XiicFixture; + +static void save_dev(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **out =3D data; + + if (*out) { + g_free(dev); + } else { + *out =3D dev; + } +} + +static void fixture_setup(XiicFixture *f) +{ + f->qts =3D qtest_init("-device xiic-fpga-i2c,num-channels=3D1 " + "-device tmp105,id=3Dtemp,bus=3Dxiic-fpga-i2c.0,ad= dress=3D0x4c"); + f->pcibus =3D qpci_new_pc(f->qts, NULL); + f->dev =3D NULL; + qpci_device_foreach(f->pcibus, 0x10ee, 0x7021, save_dev, &f->dev); + g_assert(f->dev !=3D NULL); + qpci_device_enable(f->dev); + f->bar =3D qpci_iomap(f->dev, 0, NULL); +} + +static void fixture_teardown(XiicFixture *f) +{ + qpci_iounmap(f->dev, f->bar); + g_free(f->dev); + qpci_free_pc(f->pcibus); + qtest_quit(f->qts); +} + +static void wr(XiicFixture *f, uint64_t off, uint32_t val) +{ + qpci_io_writel(f->dev, f->bar, off, val); +} + +static uint32_t rd(XiicFixture *f, uint64_t off) +{ + return qpci_io_readl(f->dev, f->bar, off); +} + +static void test_registers(void) +{ + XiicFixture f; + uint32_t sr; + + fixture_setup(&f); + + sr =3D rd(&f, XIIC_SR); + g_assert_cmphex(sr & XIIC_SR_TX_FIFO_EMPTY, =3D=3D, XIIC_SR_TX_FIFO_EM= PTY); + g_assert_cmphex(sr & XIIC_SR_RX_FIFO_EMPTY, =3D=3D, XIIC_SR_RX_FIFO_EM= PTY); + g_assert_cmphex(sr & XIIC_SR_BUS_BUSY, =3D=3D, 0); + + wr(&f, XIIC_IIER, 0x08); + g_assert_cmphex(rd(&f, XIIC_IIER), =3D=3D, 0x08); + wr(&f, XIIC_DGIER, 0x80000000); + g_assert_cmphex(rd(&f, XIIC_DGIER), =3D=3D, 0x80000000); + + fixture_teardown(&f); +} + +static void test_read(void) +{ + XiicFixture f; + + fixture_setup(&f); + + qtest_qmp_assert_success(f.qts, + "{ 'execute': 'qom-set', 'arguments':" + " { 'path': '/machine/peripheral/temp'," + " 'property': 'temperature', 'value': 21000 } }"); + + wr(&f, XIIC_DTR, XIIC_DYN_START | (TMP105_ADDR << 1) | 1); + wr(&f, XIIC_DTR, XIIC_DYN_STOP | 2); + + g_assert_cmphex(rd(&f, XIIC_DRR) & 0xff, =3D=3D, 0x15); + + fixture_teardown(&f); +} + +static void test_write(void) +{ + XiicFixture f; + uint32_t isr; + + fixture_setup(&f); + + wr(&f, XIIC_DTR, XIIC_DYN_START | (TMP105_ADDR << 1) | 0); + wr(&f, XIIC_DTR, TMP105_REG_CONFIG); + wr(&f, XIIC_DTR, XIIC_DYN_STOP | 0x00); + + isr =3D rd(&f, XIIC_IISR); + g_assert_cmphex(isr & XIIC_INTR_TX_ERROR, =3D=3D, 0); + g_assert_cmphex(isr & XIIC_INTR_BNB, =3D=3D, XIIC_INTR_BNB); + g_assert_cmphex(rd(&f, XIIC_SR) & XIIC_SR_BUS_BUSY, =3D=3D, 0); + + fixture_teardown(&f); +} + +static void test_nack(void) +{ + XiicFixture f; + + fixture_setup(&f); + + wr(&f, XIIC_DTR, XIIC_DYN_START | (0x20 << 1) | 1); + g_assert_cmphex(rd(&f, XIIC_IISR) & XIIC_INTR_TX_ERROR, =3D=3D, + XIIC_INTR_TX_ERROR); + + fixture_teardown(&f); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/xiic-fpga-i2c/registers", test_registers); + qtest_add_func("/xiic-fpga-i2c/read", test_read); + qtest_add_func("/xiic-fpga-i2c/write", test_write); + qtest_add_func("/xiic-fpga-i2c/nack", test_nack); + return g_test_run(); +} --=20 2.50.1