From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 84683606A31A for ; Sat, 31 Oct 2026 07:06:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=cfjX7GxM Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1xN35f-0007GS-Is; Sat, 31 Oct 2026 03:01:23 -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 1xN344-0003G4-VJ for qemu-devel@nongnu.org; Sat, 31 Oct 2026 02:59:46 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1xN341-0002w5-2L for qemu-devel@nongnu.org; Sat, 31 Oct 2026 02:59:44 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1793429979; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rrWVCT21fs1dAgW1s2laTiLVPp6mV2mqmhhRWhdix7U=; b=cfjX7GxM0vxyJAOXNBeV06wTB99A1bWvn6PZrWflZ1RYUq0ispxD9l0VkZeL01bhriWFQs a+l2ZQsA0NMYrtEj2hOLeAwx65TQAPBDqaEcLHG2h7sutmSuwGdnZwSFkYutZxcirDYU/S BM1iH23p4EYfJOnvctLSwkq56Yeu18s= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-444-MSbNfN9cPsGB4MG_MAcNYA-1; Sat, 31 Oct 2026 02:59:30 -0400 X-MC-Unique: MSbNfN9cPsGB4MG_MAcNYA-1 X-Mimecast-MFC-AGG-ID: MSbNfN9cPsGB4MG_MAcNYA_1793429969 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A1EEF1800590; Sat, 31 Oct 2026 06:59:29 +0000 (UTC) Received: from circe.redhat.com (unknown [10.44.32.2]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3DFBB1800677; Sat, 31 Oct 2026 06:59:28 +0000 (UTC) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: qemu-arm@nongnu.org, qemu-devel@nongnu.org Cc: Emmanuel Blot , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Subject: [PULL 27/34] hw/i2c: pca954x: add PCA9545 4-channel mux model Date: Sat, 31 Oct 2026 07:58:16 +0100 Message-ID: <20261031065823.191966-28-clg@redhat.com> In-Reply-To: <20261031065823.191966-1-clg@redhat.com> References: <20261031065823.191966-1-clg@redhat.com> X-Archived-By: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Received-SPF: pass client-ip=170.10.133.124; envelope-from=clg@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=unavailable 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-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_PASS, SPF_PASS autolearn=unavailable autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 From: Emmanuel Blot The PCA9545 is a 4-channel I2C mux/switch. What sets it apart from the plain PCA9546 is its interrupt logic: one INTn input per channel and a shared INT output, both active low, asserted whenever any channel interrupt is active. Model this behind a new has_irq class flag. The PCA9545 gains four named "interrupt" GPIO inputs and an "interrupt-out" output, and its control read-back merges the latched status nibble; the plain PCA9546/PCA9548 switches keep their previous behaviour. Reviewed-by: Cédric Le Goater Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20261015-bmc-minerva-v3-5-7d50c8a108e5@free.fr Signed-off-by: Cédric Le Goater --- include/hw/i2c/i2c_mux_pca954x.h | 1 + hw/i2c/i2c_mux_pca954x.c | 71 +++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/hw/i2c/i2c_mux_pca954x.h b/include/hw/i2c/i2c_mux_pca954x.h index 3dd25ec98309..19a105050565 100644 --- a/include/hw/i2c/i2c_mux_pca954x.h +++ b/include/hw/i2c/i2c_mux_pca954x.h @@ -3,6 +3,7 @@ #include "hw/i2c/i2c.h" +#define TYPE_PCA9545 "pca9545" #define TYPE_PCA9546 "pca9546" #define TYPE_PCA9548 "pca9548" diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index c8e75da8b2ed..4bb078a03c4c 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -12,12 +12,20 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. + * + * Limitations: + * - A control write longer than one byte is rejected rather than retaining the + * last received byte as the datasheet specifies. + * - On reset the cached per-channel interrupt state is cleared; a downstream + * INT input that stays asserted across the reset is not re-sampled until it + * next toggles. */ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_slave.h" +#include "hw/core/irq.h" #include "hw/core/qdev.h" #include "hw/core/qdev-properties.h" #include "qemu/log.h" @@ -26,11 +34,15 @@ #include "trace.h" #define PCA9548_CHANNEL_COUNT 8 +#define PCA9545_CHANNEL_COUNT 4 #define PCA9546_CHANNEL_COUNT 4 /* * struct Pca954xState - The pca954x state object. * @control: The value written to the mux control. + * @int_status: Per-channel interrupt status, for parts with interrupt logic. + * @int_out: The mux INT output, driven low while any channel interrupt is + * active. * @channel: The set of i2c channel buses that act as channels which own the * i2c children. */ @@ -38,6 +50,8 @@ typedef struct Pca954xState { SMBusDevice parent; uint8_t control; + uint8_t int_status; + qemu_irq int_out; bool enabled[PCA9548_CHANNEL_COUNT]; I2CBus *bus[PCA9548_CHANNEL_COUNT]; @@ -48,11 +62,13 @@ typedef struct Pca954xState { /* * struct Pca954xClass - The pca954x class object. * @nchans: The number of i2c channels this device has. + * @has_irq: Whether the part implements interrupt logic. */ typedef struct Pca954xClass { SMBusDeviceClass parent; uint8_t nchans; + bool has_irq; } Pca954xClass; #define TYPE_PCA954X "pca954x" @@ -116,8 +132,10 @@ static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask) static void pca954x_write(Pca954xState *s, uint8_t data) { - s->control = data; - pca954x_enable_channel(s, data); + Pca954xClass *c = PCA954X_GET_CLASS(s); + + s->control = c->has_irq ? data & ((1 << c->nchans) - 1) : data; + pca954x_enable_channel(s, s->control); trace_pca954x_write_bytes(data); } @@ -148,16 +166,45 @@ static int pca954x_write_data(SMBusDevice *d, uint8_t *buf, uint8_t len) static uint8_t pca954x_read_byte(SMBusDevice *d) { Pca954xState *s = PCA954X(d); + Pca954xClass *c = PCA954X_GET_CLASS(s); uint8_t data = s->control; + + /* + * On parts with interrupt logic the read-back byte carries the per-channel + * interrupt status in the upper nibble. + */ + if (c->has_irq) { + data = (s->int_status << 4) | (s->control & 0x0f); + } + trace_pca954x_read_data(data); return data; } +/* + * A downstream device drives one of the INTn inputs. INTn and INT are active + * low, and both carry the physical level: 0 is asserted, 1 is idle. + */ +static void pca954x_irq_handler(void *opaque, int n, int level) +{ + Pca954xState *s = PCA954X(opaque); + + if (level) { + s->int_status &= ~(1 << n); + } else { + s->int_status |= (1 << n); + } + + qemu_set_irq(s->int_out, s->int_status == 0); +} + static void pca954x_enter_reset(Object *obj, ResetType type) { Pca954xState *s = PCA954X(obj); /* Reset will disable all channels. */ pca954x_write(s, 0); + s->int_status = 0; + qemu_set_irq(s->int_out, 1); /* INT released */ } I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) @@ -169,6 +216,13 @@ I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) return pca954x->bus[channel]; } +static void pca9545_class_init(ObjectClass *klass, const void *data) +{ + Pca954xClass *s = PCA954X_CLASS(klass); + s->nchans = PCA9545_CHANNEL_COUNT; + s->has_irq = true; +} + static void pca9546_class_init(ObjectClass *klass, const void *data) { Pca954xClass *s = PCA954X_CLASS(klass); @@ -184,12 +238,20 @@ static void pca9548_class_init(ObjectClass *klass, const void *data) static void pca954x_realize(DeviceState *dev, Error **errp) { Pca954xState *s = PCA954X(dev); + Pca954xClass *c = PCA954X_GET_CLASS(dev); DeviceState *d = DEVICE(s); if (s->name) { d->id = g_strdup(s->name); } else { d->id = g_strdup_printf("pca954x[%x]", s->parent.i2c.address); } + + if (c->has_irq) { + /* One INTn input per channel, plus the shared INT output. */ + qdev_init_gpio_in_named(dev, pca954x_irq_handler, "interrupt", + c->nchans); + qdev_init_gpio_out_named(dev, &s->int_out, "interrupt-out", 1); + } } static void pca954x_init(Object *obj) @@ -242,6 +304,11 @@ static const TypeInfo pca954x_info[] = { .class_init = pca954x_class_init, .abstract = true, }, + { + .name = TYPE_PCA9545, + .parent = TYPE_PCA954X, + .class_init = pca9545_class_init, + }, { .name = TYPE_PCA9546, .parent = TYPE_PCA954X, -- 2.56.0