[PATCH 2/4] [RFC] hw/i2c/designware_i2c: Switch to Fifo8

Nicholas Piggin posted 4 patches 2 months, 1 week ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Chris Rauer <crauer@google.com>, Alano Song <alanosong@163.com>, Joel Stanley <joel@jms.id.au>
There is a newer version of this series
[PATCH 2/4] [RFC] hw/i2c/designware_i2c: Switch to Fifo8
Posted by Nicholas Piggin 2 months, 1 week ago
Alistair suggested moving to Fifo8, which I think is
a good improvement.

Broken out for individual review, but IMO we should
squash before merge since it changes VMState format.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 hw/i2c/designware_i2c.c         | 37 ++++++++++++++++++++-------------
 include/hw/i2c/designware_i2c.h |  7 +++----
 2 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
index 1e5505f571..83d0968580 100644
--- a/hw/i2c/designware_i2c.c
+++ b/hw/i2c/designware_i2c.c
@@ -208,10 +208,8 @@ static void dw_i2c_update_irq(DesignWareI2CState *s)
     qemu_set_irq(s->irq, level);
 }
 
-static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
+static uint8_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
 {
-    uint32_t value = s->rx_fifo[s->rx_cur];
-
     if (s->status != DW_I2C_STATUS_RECEIVING) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: Attempted to read from RX fifo when not in receive "
@@ -223,22 +221,21 @@ static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
         return 0;
     }
 
-    s->rx_cur = (s->rx_cur + 1) % DESIGNWARE_I2C_RX_FIFO_SIZE;
+    g_assert(s->ic_rxflr == fifo8_num_used(&s->rx_fifo));
 
-    if (s->ic_rxflr > 0) {
-        s->ic_rxflr--;
-    } else {
+    if (fifo8_is_empty(&s->rx_fifo)) {
         s->ic_raw_intr_stat |= DW_IC_INTR_RX_UNDER;
         dw_i2c_update_irq(s);
         return 0;
     }
 
+    s->ic_rxflr--;
     if (s->ic_rxflr <= s->ic_rx_tl) {
         s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
         dw_i2c_update_irq(s);
     }
 
-    return value;
+    return fifo8_pop(&s->rx_fifo);
 }
 
 static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size)
@@ -445,6 +442,7 @@ static void dw_i2c_reset_to_idle(DesignWareI2CState *s)
         s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
         s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_OVER;
         s->ic_rxflr = 0;
+        fifo8_reset(&s->rx_fifo);
         s->ic_status &= ~DW_IC_STATUS_ACTIVITY;
         s->status = DW_I2C_STATUS_IDLE;
         dw_i2c_update_irq(s);
@@ -509,10 +507,10 @@ static void dw_i2c_write_ic_data_cmd(DesignWareI2CState *s, uint32_t value)
 
     /* Receive data */
     if (recv) {
-        uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE;
+        g_assert(s->ic_rxflr == fifo8_num_used(&s->rx_fifo));
 
-        if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) {
-            s->rx_fifo[pos] = i2c_recv(s->bus);
+        if (!fifo8_is_full(&s->rx_fifo)) {
+            fifo8_push(&s->rx_fifo, i2c_recv(s->bus));
             s->ic_rxflr++;
         } else {
             s->ic_raw_intr_stat |= DW_IC_INTR_RX_OVER;
@@ -738,7 +736,8 @@ static void designware_i2c_enter_reset(Object *obj, ResetType type)
     s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
     s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
 
-    s->rx_cur = 0;
+    fifo8_reset(&s->rx_fifo);
+
     s->status = DW_I2C_STATUS_IDLE;
 }
 
@@ -777,10 +776,8 @@ static const VMStateDescription vmstate_designware_i2c = {
         VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState),
         VMSTATE_UINT32(ic_comp_version, DesignWareI2CState),
         VMSTATE_UINT32(ic_comp_type, DesignWareI2CState),
+        VMSTATE_FIFO8(rx_fifo, DesignWareI2CState),
         VMSTATE_UINT32(status, DesignWareI2CState),
-        VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState,
-                        DESIGNWARE_I2C_RX_FIFO_SIZE),
-        VMSTATE_UINT8(rx_cur, DesignWareI2CState),
         VMSTATE_END_OF_LIST(),
     },
 };
@@ -790,6 +787,8 @@ static void designware_i2c_smbus_init(Object *obj)
     DesignWareI2CState *s = DESIGNWARE_I2C(obj);
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
+    fifo8_create(&s->rx_fifo, DESIGNWARE_I2C_RX_FIFO_SIZE);
+
     sysbus_init_irq(sbd, &s->irq);
 
     memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
@@ -799,6 +798,13 @@ static void designware_i2c_smbus_init(Object *obj)
     s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
 }
 
+static void designware_i2c_finalize(Object *obj)
+{
+    DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+
+    fifo8_destroy(&s->rx_fifo);
+}
+
 static void designware_i2c_class_init(ObjectClass *klass, const void *data)
 {
     ResettableClass *rc = RESETTABLE_CLASS(klass);
@@ -819,6 +825,7 @@ static const TypeInfo designware_i2c_types[] = {
         .instance_size = sizeof(DesignWareI2CState),
         .class_init = designware_i2c_class_init,
         .instance_init = designware_i2c_smbus_init,
+	.instance_finalize = designware_i2c_finalize,
     },
 };
 DEFINE_TYPES(designware_i2c_types);
diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
index 71d74c9141..affaf983a2 100644
--- a/include/hw/i2c/designware_i2c.h
+++ b/include/hw/i2c/designware_i2c.h
@@ -8,6 +8,7 @@
 #ifndef DESIGNWARE_I2C_H
 #define DESIGNWARE_I2C_H
 
+#include "qemu/fifo8.h"
 #include "hw/i2c/i2c.h"
 #include "hw/core/irq.h"
 #include "hw/core/sysbus.h"
@@ -53,8 +54,6 @@ typedef enum DesignWareI2CStatus {
  * @ic_comp_version: I2C component version register
  * @ic_comp_type: I2C component type register
  * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
- * @rx_cur: The current position of rx_fifo.
- * @status: The current status of the SMBus.
  */
 typedef struct DesignWareI2CState {
     SysBusDevice parent_obj;
@@ -88,8 +87,8 @@ typedef struct DesignWareI2CState {
     uint32_t ic_comp_version;
     uint32_t ic_comp_type;
 
-    uint8_t      rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
-    uint8_t      rx_cur;
+    /* fifo8_num_used(rx_fifo) should always equal ic_rxflr */
+    Fifo8    rx_fifo;
 
     DesignWareI2CStatus status;
 } DesignWareI2CState;
-- 
2.53.0
Re: [PATCH 2/4] [RFC] hw/i2c/designware_i2c: Switch to Fifo8
Posted by Philippe Mathieu-Daudé 2 months ago
On 7/5/26 14:05, Nicholas Piggin wrote:
> Alistair suggested moving to Fifo8, which I think is
> a good improvement.
> 
> Broken out for individual review, but IMO we should
> squash before merge since it changes VMState format.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   hw/i2c/designware_i2c.c         | 37 ++++++++++++++++++++-------------
>   include/hw/i2c/designware_i2c.h |  7 +++----
>   2 files changed, 25 insertions(+), 19 deletions(-)


> @@ -53,8 +54,6 @@ typedef enum DesignWareI2CStatus {
>    * @ic_comp_version: I2C component version register
>    * @ic_comp_type: I2C component type register
>    * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
> - * @rx_cur: The current position of rx_fifo.
> - * @status: The current status of the SMBus.
>    */
>   typedef struct DesignWareI2CState {
>       SysBusDevice parent_obj;
> @@ -88,8 +87,8 @@ typedef struct DesignWareI2CState {
>       uint32_t ic_comp_version;
>       uint32_t ic_comp_type;
>   
> -    uint8_t      rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
> -    uint8_t      rx_cur;
> +    /* fifo8_num_used(rx_fifo) should always equal ic_rxflr */

Why not remove ic_rxflr then?

> +    Fifo8    rx_fifo;
>   
>       DesignWareI2CStatus status;
>   } DesignWareI2CState;
Re: [PATCH 2/4] [RFC] hw/i2c/designware_i2c: Switch to Fifo8
Posted by Nicholas Piggin 2 months ago
On Mon, May 11, 2026 at 12:18:41PM +0200, Philippe Mathieu-Daudé wrote:
> On 7/5/26 14:05, Nicholas Piggin wrote:
> > Alistair suggested moving to Fifo8, which I think is
> > a good improvement.
> > 
> > Broken out for individual review, but IMO we should
> > squash before merge since it changes VMState format.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   hw/i2c/designware_i2c.c         | 37 ++++++++++++++++++++-------------
> >   include/hw/i2c/designware_i2c.h |  7 +++----
> >   2 files changed, 25 insertions(+), 19 deletions(-)
> 
> 
> > @@ -53,8 +54,6 @@ typedef enum DesignWareI2CStatus {
> >    * @ic_comp_version: I2C component version register
> >    * @ic_comp_type: I2C component type register
> >    * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
> > - * @rx_cur: The current position of rx_fifo.
> > - * @status: The current status of the SMBus.
> >    */
> >   typedef struct DesignWareI2CState {
> >       SysBusDevice parent_obj;
> > @@ -88,8 +87,8 @@ typedef struct DesignWareI2CState {
> >       uint32_t ic_comp_version;
> >       uint32_t ic_comp_type;
> > -    uint8_t      rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
> > -    uint8_t      rx_cur;
> > +    /* fifo8_num_used(rx_fifo) should always equal ic_rxflr */
> 
> Why not remove ic_rxflr then?

I did consider that... but the next patch to rework the registers
adds storage for them all as a block. We could still just ignore
the storage and use fifo8_num_used() instead but I was vaguely
worried about complexity of vmstate compat if we change things, but
not sure if that is a rational concern. In the end the code is
quite simple and only a couple of places where this is adjusted so
I kept it this way.

Thanks,
Nick