From: "Cédric Le Goater" <clg@redhat.com>
To: qemu-arm@nongnu.org, qemu-devel@nongnu.org
Cc: "Emmanuel Blot" <emmanuel.blot@free.fr>,
"Cédric Le Goater" <clg@redhat.com>
Subject: [PULL 27/34] hw/i2c: pca954x: add PCA9545 4-channel mux model
Date: Sat, 31 Oct 2026 07:58:16 +0100 [thread overview]
Message-ID: <20261031065823.191966-28-clg@redhat.com> (raw)
In-Reply-To: <20261031065823.191966-1-clg@redhat.com>
From: Emmanuel Blot <emmanuel.blot@free.fr>
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 <clg@redhat.com>
Signed-off-by: Emmanuel Blot <emmanuel.blot@free.fr>
Link: https://lore.kernel.org/qemu-devel/20261015-bmc-minerva-v3-5-7d50c8a108e5@free.fr
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
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
next prev parent reply other threads:[~2026-10-31 7:06 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 6:57 [PULL 00/34] aspeed queue Cédric Le Goater
2026-10-31 6:57 ` [PULL 01/34] hw/ssi/aspeed_smc: Fix AST2700 segment end address decoding Cédric Le Goater
2026-10-31 6:57 ` [PULL 02/34] hw/ssi/aspeed_smc: Fix AST2700 DMA flash address mask Cédric Le Goater
2026-10-31 6:57 ` [PULL 03/34] hw/block/m25p80: Add Winbond W25Q33PW flash Cédric Le Goater
2026-10-31 6:57 ` [PULL 04/34] hw/misc/aspeed_scu: Correct AST1040 A0 revision ID Cédric Le Goater
2026-10-31 6:57 ` [PULL 05/34] hw/ssi/aspeed_smc: Add AST1040 FMC and SPI controllers Cédric Le Goater
2026-10-31 6:57 ` [PULL 06/34] hw/arm/aspeed_ast1040: Add " Cédric Le Goater
2026-10-31 6:57 ` [PULL 07/34] hw/arm/aspeed_ast1040: Add FMC and SPI filters as unimplemented devices Cédric Le Goater
2026-10-31 6:57 ` [PULL 08/34] hw/arm/aspeed_ast1040: Add OTP as an unimplemented device Cédric Le Goater
2026-10-31 6:57 ` [PULL 09/34] hw/arm/aspeed_ast1040: Add Anti-Tamper Controller (ATC) " Cédric Le Goater
2026-10-31 6:57 ` [PULL 10/34] hw/arm/aspeed_ast1040: Add I3C " Cédric Le Goater
2026-10-31 6:58 ` [PULL 11/34] hw/arm/aspeed_ast1040: Add Caliptra " Cédric Le Goater
2026-10-31 6:58 ` [PULL 12/34] tests/qtest/aspeed_smc: Add AST1040 SMC tests Cédric Le Goater
2026-10-31 6:58 ` [PULL 13/34] tests/functional/arm/test_aspeed_ast1040: Update AST1040 image to aspeed-mcu-fw-hub v01.00 Cédric Le Goater
2026-10-31 6:58 ` [PULL 14/34] hw/arm/aspeed_ast1040: Add common init function for AST1040/AST1080 SoCs Cédric Le Goater
2026-10-31 6:58 ` [PULL 15/34] hw/arm/aspeed_ast1040: Add common realize " Cédric Le Goater
2026-10-31 6:58 ` [PULL 16/34] hw/misc/aspeed_scu: Add AST1080 A0 silicon revision definition Cédric Le Goater
2026-10-31 6:58 ` [PULL 17/34] hw/arm/aspeed_ast1040: Add AST1080 SoC support Cédric Le Goater
2026-10-31 6:58 ` [PULL 18/34] hw/arm/aspeed_ast1040: Add AST1080 QSPI filters as unimplemented device Cédric Le Goater
2026-10-31 6:58 ` [PULL 19/34] hw/arm/aspeed_ast1040: Add AST1080 I2C filter as an " Cédric Le Goater
2026-10-31 6:58 ` [PULL 20/34] hw/arm/aspeed_ast1040_evb: Add AST1080 EVB machine support Cédric Le Goater
2026-10-31 6:58 ` [PULL 21/34] tests/functional/arm/aspeed: Add AST1080 test with ASPEED MCU FW Hub v01.00 Cédric Le Goater
2026-10-31 6:58 ` [PULL 22/34] docs/system/arm/aspeed: Add AST1080 Platform Root of Trust evaluation board Cédric Le Goater
2026-10-31 6:58 ` [PULL 23/34] docs/system/arm/aspeed: add the catalina-bmc machine Cédric Le Goater
2026-10-31 6:58 ` [PULL 24/34] docs/system/arm/aspeed: add the sanmiguel-bmc machine Cédric Le Goater
2026-10-31 6:58 ` [PULL 25/34] hw/arm: sanmiguel: remove the second SPI flash controller Cédric Le Goater
2026-10-31 6:58 ` [PULL 26/34] hw/i2c: pca954x: drop unused header inclusions Cédric Le Goater
2026-10-31 6:58 ` Cédric Le Goater [this message]
2026-10-31 6:58 ` [PULL 28/34] hw/i2c: pca954x: migrate mux state Cédric Le Goater
2026-10-31 6:58 ` [PULL 29/34] tests/qtest: pca9545: add mux test Cédric Le Goater
2026-10-31 6:58 ` [PULL 30/34] hw/rtc: ds1338: move TYPE_DS1338 to a public header Cédric Le Goater
2026-10-31 6:58 ` [PULL 31/34] hw: ds1338: use the TYPE_DS1338 constant instead of the "ds1338" string Cédric Le Goater
2026-10-31 6:58 ` [PULL 32/34] hw/rtc: ds1338: implement Resettable reset Cédric Le Goater
2026-10-31 6:58 ` [PULL 33/34] hw/rtc: ds1338: prefix the file-local helpers with ds1338_ Cédric Le Goater
2026-10-31 6:58 ` [PULL 34/34] hw/rtc: ds1338: parameterise the register map via a device class Cédric Le Goater
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261031065823.191966-28-clg@redhat.com \
--to=clg@redhat.com \
--cc=emmanuel.blot@free.fr \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.