hw/arm/bcm2838.c | 3 +++ include/hw/arm/bcm2838_peripherals.h | 1 + 2 files changed, 4 insertions(+)
The I2C interrupts are only routed to the legacy interrupt controller. This means
that for modern device trees that use the GIC, the interrupts don't work. This patch
re-routes the I2C OR'd interrupt from the legacy interrupt controller to the GIC so
I2C emulation works out of the box with current kernels.
Testing
Add these lines to QEMU invocation
-drive if=none,id=i2c_storage,format=raw,file=eeprom.bin \
-device at24c-eeprom,bus=i2c-bus.1,address=0x50,drive=i2c_storage,rom-size=4096 \
note: eeprom.bin is all zeros
Before this change, running i2c get to read from EEPROM would result in this
i2cget -y 1 0x50
Error: Read failed
After this change, running i2c to read from EEPROM results in this
i2cget -y 1 0x50
0x00
The eeprom can now also be enabled in the device tree. Before the
eeprom driver load would fail due to the read failing
ls -l /sys/bus/i2c/devices/i2c-1/1-0050/ | grep -i eeprom
-rw------- 1 root root 4096 May 17 16:57 eeprom
Signed-off-by: Nicholas Righi <nicholasrighi@gmail.com>
---
hw/arm/bcm2838.c | 3 +++
include/hw/arm/bcm2838_peripherals.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c
index c14a854046..6895f70385 100644
--- a/hw/arm/bcm2838.c
+++ b/hw/arm/bcm2838.c
@@ -184,6 +184,9 @@ static void bcm2838_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0,
qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1));
+ qdev_connect_gpio_out(DEVICE(&ps_base->orgated_i2c_irq), 0,
+ qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_I2C));
+
/* Connect VC mailbox to the interrupt controller */
sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->mboxes), 0,
qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_MBOX));
diff --git a/include/hw/arm/bcm2838_peripherals.h b/include/hw/arm/bcm2838_peripherals.h
index 7ee1bd066f..0be97e67c7 100644
--- a/include/hw/arm/bcm2838_peripherals.h
+++ b/include/hw/arm/bcm2838_peripherals.h
@@ -22,6 +22,7 @@
#define GIC_SPI_INTERRUPT_DMA_7_8 87
#define GIC_SPI_INTERRUPT_DMA_9_10 88
#define GIC_SPI_INTERRUPT_AUX_UART1 93
+#define GIC_SPI_INTERRUPT_I2C 117
#define GIC_SPI_INTERRUPT_SDHOST 120
#define GIC_SPI_INTERRUPT_UART0 121
#define GIC_SPI_INTERRUPT_RNG200 125
--
2.51.2
On Mon, 25 May 2026 at 04:26, Nicholas Righi <nicholasrighi@gmail.com> wrote:
>
> The I2C interrupts are only routed to the legacy interrupt controller. This means
> that for modern device trees that use the GIC, the interrupts don't work. This patch
> re-routes the I2C OR'd interrupt from the legacy interrupt controller to the GIC so
> I2C emulation works out of the box with current kernels.
>
> diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c
> index c14a854046..6895f70385 100644
> --- a/hw/arm/bcm2838.c
> +++ b/hw/arm/bcm2838.c
> @@ -184,6 +184,9 @@ static void bcm2838_realize(DeviceState *dev, Error **errp)
> sysbus_connect_irq(SYS_BUS_DEVICE(&ps_base->aux), 0,
> qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_AUX_UART1));
>
> + qdev_connect_gpio_out(DEVICE(&ps_base->orgated_i2c_irq), 0,
> + qdev_get_gpio_in(gicdev, GIC_SPI_INTERRUPT_I2C));
We already connect this output from orgated_i2c_irq to
somewhere else, in hw/arm/bcm2835_peripherals.c:
qdev_connect_gpio_out(DEVICE(&s->orgated_i2c_irq), 0,
qdev_get_gpio_in_named(DEVICE(&s->ic),
BCM2835_IC_GPU_IRQ,
INTERRUPT_I2C));
It is not possible to connect one GPIO output to two places
like this -- the second call will silently override the first one.
You need to create a TYPE_IRQ_SPLIT object, connect the
output from the device to the input of the splitter, and
connect the outputs of the splitter to the legacy interrupt
controller and the GIC.
thanks
-- PMM
On 25/5/26 05:26, Nicholas Righi wrote: > The I2C interrupts are only routed to the legacy interrupt controller. This means > that for modern device trees that use the GIC, the interrupts don't work. This patch > re-routes the I2C OR'd interrupt from the legacy interrupt controller to the GIC so > I2C emulation works out of the box with current kernels. > > Testing > > Add these lines to QEMU invocation > > -drive if=none,id=i2c_storage,format=raw,file=eeprom.bin \ > -device at24c-eeprom,bus=i2c-bus.1,address=0x50,drive=i2c_storage,rom-size=4096 \ > > note: eeprom.bin is all zeros > > Before this change, running i2c get to read from EEPROM would result in this > > i2cget -y 1 0x50 > Error: Read failed > > After this change, running i2c to read from EEPROM results in this > > i2cget -y 1 0x50 > 0x00 > > The eeprom can now also be enabled in the device tree. Before the > eeprom driver load would fail due to the read failing > > ls -l /sys/bus/i2c/devices/i2c-1/1-0050/ | grep -i eeprom > -rw------- 1 root root 4096 May 17 16:57 eeprom > > Signed-off-by: Nicholas Righi <nicholasrighi@gmail.com> > --- > hw/arm/bcm2838.c | 3 +++ > include/hw/arm/bcm2838_peripherals.h | 1 + > 2 files changed, 4 insertions(+) Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
© 2016 - 2026 Red Hat, Inc.