Message ID | 20240126005541.1839038-2-komlodi@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | hw/i2c: smbus: Reset fixes | expand |
On Fri, 26 Jan 2024 at 00:56, Joe Komlodi <komlodi@google.com> wrote: > > It's possible for a reset to come in the middle of a transaction, which > causes the bus to be in an old state when a new transaction comes in. > > Signed-off-by: Joe Komlodi <komlodi@google.com> > --- > hw/i2c/core.c | 30 +++++++++++++++++++++++++----- > include/hw/i2c/i2c.h | 6 +++++- > 2 files changed, 30 insertions(+), 6 deletions(-) > > diff --git a/hw/i2c/core.c b/hw/i2c/core.c > index 4cf30b2c86..def4f134d0 100644 > --- a/hw/i2c/core.c > +++ b/hw/i2c/core.c > @@ -23,11 +23,31 @@ static Property i2c_props[] = { > DEFINE_PROP_END_OF_LIST(), > }; > > -static const TypeInfo i2c_bus_info = { > - .name = TYPE_I2C_BUS, > - .parent = TYPE_BUS, > - .instance_size = sizeof(I2CBus), > -}; > +static void i2c_bus_enter_reset(Object *obj, ResetType type) > +{ > + I2CBus *bus = I2C_BUS(obj); > + I2CNode *node, *next; > + > + bus->broadcast = false; > + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { > + QLIST_REMOVE(node, next); > + g_free(node); > + } Doesn't it confuse the device that's partway through a transaction if we just forget about the transaction entirely without terminating it somehow? I'm not sure what real hardware does in this situation, though. > +} > + > +static void i2c_bus_class_init(ObjectClass *klass, void *data) > +{ > + ResettableClass *rc = RESETTABLE_CLASS(klass); > + rc->phases.enter = i2c_bus_enter_reset; > +} > + > + static const TypeInfo i2c_bus_info = { > + .name = TYPE_I2C_BUS, > + .parent = TYPE_BUS, > + .instance_size = sizeof(I2CBus), > + .class_size = sizeof(I2CBusClass), > + .class_init = i2c_bus_class_init, > + }; Looks like you have stray extra spaces in front of this type definition (which has then caused 'diff' to not notice that you're only adding fields to the existing struct). > > static int i2c_bus_pre_save(void *opaque) > { > diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h > index 2a3abacd1b..420868a269 100644 > --- a/include/hw/i2c/i2c.h > +++ b/include/hw/i2c/i2c.h > @@ -64,7 +64,7 @@ struct I2CSlave { > }; > > #define TYPE_I2C_BUS "i2c-bus" > -OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS) > +OBJECT_DECLARE_TYPE(I2CBus, I2CBusClass, I2C_BUS) > > typedef struct I2CNode I2CNode; > > @@ -83,6 +83,10 @@ struct I2CPendingMaster { > typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; > typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; > > +struct I2CBusClass { > + DeviceClass parent_class; > +}; This isn't correct -- a FooBusClass's parent_class field should be a BusClass. But since you don't define any new fields in it, you don't need to define the struct at all. Instead, your TypeInfo for the TYPE_I2C_BUS can add a .class_init member, and leave the .class_size unset (it will then inherit the class-size from the parent class, which will be sizeof(BusClass)). > + > struct I2CBus { > BusState qbus; > I2CNodeList current_devs; > -- > 2.43.0.429.g432eaa2c6b-goog thanks -- PMM
Hi peter, On Thu, Feb 1, 2024 at 7:24 AM Peter Maydell <peter.maydell@linaro.org> wrote: > > On Fri, 26 Jan 2024 at 00:56, Joe Komlodi <komlodi@google.com> wrote: > > > > It's possible for a reset to come in the middle of a transaction, which > > causes the bus to be in an old state when a new transaction comes in. > > > > Signed-off-by: Joe Komlodi <komlodi@google.com> > > --- > > hw/i2c/core.c | 30 +++++++++++++++++++++++++----- > > include/hw/i2c/i2c.h | 6 +++++- > > 2 files changed, 30 insertions(+), 6 deletions(-) > > > > diff --git a/hw/i2c/core.c b/hw/i2c/core.c > > index 4cf30b2c86..def4f134d0 100644 > > --- a/hw/i2c/core.c > > +++ b/hw/i2c/core.c > > @@ -23,11 +23,31 @@ static Property i2c_props[] = { > > DEFINE_PROP_END_OF_LIST(), > > }; > > > > -static const TypeInfo i2c_bus_info = { > > - .name = TYPE_I2C_BUS, > > - .parent = TYPE_BUS, > > - .instance_size = sizeof(I2CBus), > > -}; > > +static void i2c_bus_enter_reset(Object *obj, ResetType type) > > +{ > > + I2CBus *bus = I2C_BUS(obj); > > + I2CNode *node, *next; > > + > > + bus->broadcast = false; > > + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { > > + QLIST_REMOVE(node, next); > > + g_free(node); > > + } > > Doesn't it confuse the device that's partway through a > transaction if we just forget about the transaction entirely > without terminating it somehow? I'm not sure what real hardware > does in this situation, though. > It could. Ideally all devices on the bus would have reset functions implemented as well, so their state can reset. With adding a bus-wide reset, what could end up happening is devices without resets implemented end up in the wrong state compared to the bus, while before they would stay in the same state as the bus. However with the bus-wide reset, devices with resets now match their state with the bus's state, while before there could be a mismatch. Fixed the comments in this patch and in the other 2 patches in v3. Thanks, Joe > > +} > > + > > +static void i2c_bus_class_init(ObjectClass *klass, void *data) > > +{ > > + ResettableClass *rc = RESETTABLE_CLASS(klass); > > + rc->phases.enter = i2c_bus_enter_reset; > > +} > > + > > + static const TypeInfo i2c_bus_info = { > > + .name = TYPE_I2C_BUS, > > + .parent = TYPE_BUS, > > + .instance_size = sizeof(I2CBus), > > + .class_size = sizeof(I2CBusClass), > > + .class_init = i2c_bus_class_init, > > + }; > > Looks like you have stray extra spaces in front of this > type definition (which has then caused 'diff' to not notice > that you're only adding fields to the existing struct). > > > > > static int i2c_bus_pre_save(void *opaque) > > { > > diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h > > index 2a3abacd1b..420868a269 100644 > > --- a/include/hw/i2c/i2c.h > > +++ b/include/hw/i2c/i2c.h > > @@ -64,7 +64,7 @@ struct I2CSlave { > > }; > > > > #define TYPE_I2C_BUS "i2c-bus" > > -OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS) > > +OBJECT_DECLARE_TYPE(I2CBus, I2CBusClass, I2C_BUS) > > > > typedef struct I2CNode I2CNode; > > > > @@ -83,6 +83,10 @@ struct I2CPendingMaster { > > typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; > > typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; > > > > +struct I2CBusClass { > > + DeviceClass parent_class; > > +}; > > This isn't correct -- a FooBusClass's parent_class field > should be a BusClass. But since you don't define any new > fields in it, you don't need to define the struct at all. > > Instead, your TypeInfo for the TYPE_I2C_BUS can add a > .class_init member, and leave the .class_size unset > (it will then inherit the class-size from the parent > class, which will be sizeof(BusClass)). > > > + > > struct I2CBus { > > BusState qbus; > > I2CNodeList current_devs; > > -- > > 2.43.0.429.g432eaa2c6b-goog > > thanks > -- PMM
diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 4cf30b2c86..def4f134d0 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -23,11 +23,31 @@ static Property i2c_props[] = { DEFINE_PROP_END_OF_LIST(), }; -static const TypeInfo i2c_bus_info = { - .name = TYPE_I2C_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(I2CBus), -}; +static void i2c_bus_enter_reset(Object *obj, ResetType type) +{ + I2CBus *bus = I2C_BUS(obj); + I2CNode *node, *next; + + bus->broadcast = false; + QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { + QLIST_REMOVE(node, next); + g_free(node); + } +} + +static void i2c_bus_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + rc->phases.enter = i2c_bus_enter_reset; +} + + static const TypeInfo i2c_bus_info = { + .name = TYPE_I2C_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(I2CBus), + .class_size = sizeof(I2CBusClass), + .class_init = i2c_bus_class_init, + }; static int i2c_bus_pre_save(void *opaque) { diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 2a3abacd1b..420868a269 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -64,7 +64,7 @@ struct I2CSlave { }; #define TYPE_I2C_BUS "i2c-bus" -OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS) +OBJECT_DECLARE_TYPE(I2CBus, I2CBusClass, I2C_BUS) typedef struct I2CNode I2CNode; @@ -83,6 +83,10 @@ struct I2CPendingMaster { typedef QLIST_HEAD(I2CNodeList, I2CNode) I2CNodeList; typedef QSIMPLEQ_HEAD(I2CPendingMasters, I2CPendingMaster) I2CPendingMasters; +struct I2CBusClass { + DeviceClass parent_class; +}; + struct I2CBus { BusState qbus; I2CNodeList current_devs;
It's possible for a reset to come in the middle of a transaction, which causes the bus to be in an old state when a new transaction comes in. Signed-off-by: Joe Komlodi <komlodi@google.com> --- hw/i2c/core.c | 30 +++++++++++++++++++++++++----- include/hw/i2c/i2c.h | 6 +++++- 2 files changed, 30 insertions(+), 6 deletions(-)