diff mbox series

[v3,08/33] Add function to control reset with gpio inputs

Message ID 20190729145654.14644-9-damien.hedde@greensocs.com
State New
Headers show
Series Multi-phase reset mechanism | expand

Commit Message

Damien Hedde July 29, 2019, 2:56 p.m. UTC
It adds the possibility to add 2 gpios to control the warm and cold reset.
With theses ios, the reset can be maintained during some time.
Each io is associated with a state to detect level changes.

Vmstate subsections are also added to the existsing device_reset
subsection.

Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
---
 hw/core/qdev-vmstate.c | 15 ++++++++++
 hw/core/qdev.c         | 65 ++++++++++++++++++++++++++++++++++++++++++
 include/hw/qdev-core.h | 57 ++++++++++++++++++++++++++++++++++++
 3 files changed, 137 insertions(+)

Comments

David Gibson July 31, 2019, 6:11 a.m. UTC | #1
On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
> It adds the possibility to add 2 gpios to control the warm and cold reset.
> With theses ios, the reset can be maintained during some time.
> Each io is associated with a state to detect level changes.
> 
> Vmstate subsections are also added to the existsing device_reset
> subsection.

This doesn't seem like a thing that should be present on every single
DeviceState.

> 
> Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
> ---
>  hw/core/qdev-vmstate.c | 15 ++++++++++
>  hw/core/qdev.c         | 65 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/qdev-core.h | 57 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 137 insertions(+)
> 
> diff --git a/hw/core/qdev-vmstate.c b/hw/core/qdev-vmstate.c
> index 24f8465c61..72f84c6cee 100644
> --- a/hw/core/qdev-vmstate.c
> +++ b/hw/core/qdev-vmstate.c
> @@ -24,10 +24,23 @@ static int device_vmstate_reset_post_load(void *opaque, int version_id)
>  {
>      DeviceState *dev = (DeviceState *) opaque;
>      BusState *bus;
> +    uint32_t io_count = 0;
> +
>      QLIST_FOREACH(bus, &dev->child_bus, sibling) {
>          bus->resetting = dev->resetting;
>          bus->reset_is_cold = dev->reset_is_cold;
>      }
> +
> +    if (dev->cold_reset_input.state) {
> +        io_count += 1;
> +    }
> +    if (dev->warm_reset_input.state) {
> +        io_count += 1;
> +    }
> +    /* ensure resetting count is coherent with io states */
> +    if (dev->resetting < io_count) {
> +        return -1;
> +    }
>      return 0;
>  }
>  
> @@ -40,6 +53,8 @@ const struct VMStateDescription device_vmstate_reset = {
>      .fields = (VMStateField[]) {
>          VMSTATE_UINT32(resetting, DeviceState),
>          VMSTATE_BOOL(reset_is_cold, DeviceState),
> +        VMSTATE_BOOL(cold_reset_input.state, DeviceState),
> +        VMSTATE_BOOL(warm_reset_input.state, DeviceState),
>          VMSTATE_END_OF_LIST()
>      },
>  };
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index 88387d3743..11a4de55ea 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -450,6 +450,67 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
>      qdev_init_gpio_in_named(dev, handler, NULL, n);
>  }
>  
> +static DeviceResetInputState *device_get_reset_input_state(DeviceState *dev,
> +                                                            bool cold)
> +{
> +    return cold ? &dev->cold_reset_input : &dev->warm_reset_input;
> +}
> +
> +static void device_reset_handler(DeviceState *dev, bool cold, bool level)
> +{
> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
> +
> +    if (dris->type == DEVICE_RESET_ACTIVE_LOW) {
> +        level = !level;
> +    }
> +
> +    if (dris->state == level) {
> +        /* io state has not changed */
> +        return;
> +    }
> +
> +    dris->state = level;
> +
> +    if (level) {
> +        resettable_assert_reset(OBJECT(dev), cold);
> +    } else {
> +        resettable_deassert_reset(OBJECT(dev));
> +    }
> +}
> +
> +static void device_cold_reset_handler(void *opaque, int n, int level)
> +{
> +    device_reset_handler((DeviceState *) opaque, true, level);
> +}
> +
> +static void device_warm_reset_handler(void *opaque, int n, int level)
> +{
> +    device_reset_handler((DeviceState *) opaque, false, level);
> +}
> +
> +void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
> +                                   bool cold, DeviceResetActiveType type)
> +{
> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
> +    qemu_irq_handler handler;
> +
> +    switch (type) {
> +    case DEVICE_RESET_ACTIVE_LOW:
> +    case DEVICE_RESET_ACTIVE_HIGH:
> +        break;
> +    default:
> +        assert(false);
> +        break;
> +    }
> +
> +    assert(!dris->exists);
> +    dris->exists = true;
> +    dris->type = type;
> +
> +    handler = cold ? device_cold_reset_handler : device_warm_reset_handler;
> +    qdev_init_gpio_in_named(dev, handler, name, 1);
> +}
> +
>  void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
>                                const char *name, int n)
>  {
> @@ -1007,6 +1068,10 @@ static void device_initfn(Object *obj)
>      dev->instance_id_alias = -1;
>      dev->realized = false;
>      dev->resetting = 0;
> +    dev->cold_reset_input.exists = false;
> +    dev->cold_reset_input.state = false;
> +    dev->warm_reset_input.exists = false;
> +    dev->warm_reset_input.state = false;
>  
>      object_property_add_bool(obj, "realized",
>                               device_get_realized, device_set_realized, NULL);
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index 926d4bbcb1..f724ddc8f4 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -136,6 +136,23 @@ struct NamedGPIOList {
>      QLIST_ENTRY(NamedGPIOList) node;
>  };
>  
> +typedef enum DeviceResetActiveType {
> +    DEVICE_RESET_ACTIVE_LOW,
> +    DEVICE_RESET_ACTIVE_HIGH,
> +} DeviceResetActiveType;
> +
> +/**
> + * DeviceResetInputState:
> + * @exists: tell if io exists
> + * @type: tell whether the io active low or high
> + * @state: true if reset is currently active
> + */
> +typedef struct DeviceResetInputState {
> +    bool exists;
> +    DeviceResetActiveType type;
> +    bool state;
> +} DeviceResetInputState;
> +
>  /**
>   * DeviceState:
>   * @realized: Indicates whether the device has been fully constructed.
> @@ -143,6 +160,8 @@ struct NamedGPIOList {
>   * used to count how many times reset has been initiated on the device.
>   * @reset_is_cold: If the device is under reset, indicates whether it is cold
>   * or warm.
> + * @cold_reset_input: state data for cold reset io
> + * @warm_reset_input: state data for warm reset io
>   *
>   * This structure should not be accessed directly.  We declare it here
>   * so that it can be embedded in individual device state structures.
> @@ -167,6 +186,8 @@ struct DeviceState {
>      uint32_t resetting;
>      bool reset_is_cold;
>      bool reset_hold_needed;
> +    DeviceResetInputState cold_reset_input;
> +    DeviceResetInputState warm_reset_input;
>  };
>  
>  struct DeviceListener {
> @@ -372,6 +393,42 @@ static inline void qdev_init_gpio_in_named(DeviceState *dev,
>  void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
>                       const char *name);
>  
> +/**
> + * qdev_init_reset_gpio_in_named:
> + * Create a gpio controlling the warm or cold reset of the device.
> + *
> + * @cold: specify whether it triggers cold or warm reset
> + * @type: what kind of reset io it is
> + *
> + * Note: the io is considered created in its inactive state. No reset
> + * is started by this function.
> + */
> +void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
> +                                   bool cold, DeviceResetActiveType type);
> +
> +/**
> + * qdev_init_warm_reset_gpio:
> + * Create the input to control the device warm reset.
> + */
> +static inline void qdev_init_warm_reset_gpio(DeviceState *dev,
> +                                             const char *name,
> +                                             DeviceResetActiveType type)
> +{
> +    qdev_init_reset_gpio_in_named(dev, name, false, type);
> +}
> +
> +/**
> + * qdev_init_cold_reset_gpio:
> + * Create the input to control the device cold reset.
> + * It can also be used as a power gate control.
> + */
> +static inline void qdev_init_cold_reset_gpio(DeviceState *dev,
> +                                             const char *name,
> +                                             DeviceResetActiveType type)
> +{
> +    qdev_init_reset_gpio_in_named(dev, name, true, type);
> +}
> +
>  BusState *qdev_get_parent_bus(DeviceState *dev);
>  
>  /*** BUS API. ***/
Damien Hedde July 31, 2019, 10:09 a.m. UTC | #2
On 7/31/19 8:11 AM, David Gibson wrote:
> On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
>> It adds the possibility to add 2 gpios to control the warm and cold reset.
>> With theses ios, the reset can be maintained during some time.
>> Each io is associated with a state to detect level changes.
>>
>> Vmstate subsections are also added to the existsing device_reset
>> subsection.
> 
> This doesn't seem like a thing that should be present on every single
> DeviceState.

I can revert to previous version where the io state has to be explicitly
added in devices using it.

Damien
Peter Maydell Aug. 7, 2019, 10:37 a.m. UTC | #3
On Wed, 31 Jul 2019 at 07:33, David Gibson <david@gibson.dropbear.id.au> wrote:
>
> On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
> > It adds the possibility to add 2 gpios to control the warm and cold reset.
> > With theses ios, the reset can be maintained during some time.
> > Each io is associated with a state to detect level changes.
> >
> > Vmstate subsections are also added to the existsing device_reset
> > subsection.
>
> This doesn't seem like a thing that should be present on every single
> DeviceState.

It's a facility that's going to be useful to multiple different
subclasses of DeviceState, so it seems to me cleaner to
have base class support for the common feature rather than
to reimplement it entirely from scratch in every subclass
that wants it.

(The patch as written breaks migration compatibility -- the
new fields need to go in their own vmstate subsection
with a suitable 'needed' function -- but that can be fixed.)

thanks
-- PMM
Peter Maydell Aug. 7, 2019, 3:18 p.m. UTC | #4
On Mon, 29 Jul 2019 at 15:59, Damien Hedde <damien.hedde@greensocs.com> wrote:
>
> It adds the possibility to add 2 gpios to control the warm and cold reset.
> With theses ios, the reset can be maintained during some time.
> Each io is associated with a state to detect level changes.
>
> Vmstate subsections are also added to the existsing device_reset
> subsection.
>
> Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
> ---
>  hw/core/qdev-vmstate.c | 15 ++++++++++
>  hw/core/qdev.c         | 65 ++++++++++++++++++++++++++++++++++++++++++
>  include/hw/qdev-core.h | 57 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 137 insertions(+)
>
> diff --git a/hw/core/qdev-vmstate.c b/hw/core/qdev-vmstate.c
> index 24f8465c61..72f84c6cee 100644
> --- a/hw/core/qdev-vmstate.c
> +++ b/hw/core/qdev-vmstate.c
> @@ -24,10 +24,23 @@ static int device_vmstate_reset_post_load(void *opaque, int version_id)
>  {
>      DeviceState *dev = (DeviceState *) opaque;
>      BusState *bus;
> +    uint32_t io_count = 0;
> +
>      QLIST_FOREACH(bus, &dev->child_bus, sibling) {
>          bus->resetting = dev->resetting;
>          bus->reset_is_cold = dev->reset_is_cold;
>      }
> +
> +    if (dev->cold_reset_input.state) {
> +        io_count += 1;
> +    }
> +    if (dev->warm_reset_input.state) {
> +        io_count += 1;
> +    }
> +    /* ensure resetting count is coherent with io states */
> +    if (dev->resetting < io_count) {
> +        return -1;
> +    }
>      return 0;
>  }
>
> @@ -40,6 +53,8 @@ const struct VMStateDescription device_vmstate_reset = {
>      .fields = (VMStateField[]) {
>          VMSTATE_UINT32(resetting, DeviceState),
>          VMSTATE_BOOL(reset_is_cold, DeviceState),
> +        VMSTATE_BOOL(cold_reset_input.state, DeviceState),
> +        VMSTATE_BOOL(warm_reset_input.state, DeviceState),

If we're just adding these fields to this VMStateDescription
then this patch should come earlier in the series than the
patch where we create and start using the fields. Otherwise
there's a migration compat break between a QEMU just
before this patch and a QEMU with it. I think the simplest
fix is to put this patch before patches 6/7 and have a note
in the commit message that this functionality can't be used
until after the patch which adds the migration support.

>          VMSTATE_END_OF_LIST()
>      },
>  };
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index 88387d3743..11a4de55ea 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -450,6 +450,67 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
>      qdev_init_gpio_in_named(dev, handler, NULL, n);
>  }
>
> +static DeviceResetInputState *device_get_reset_input_state(DeviceState *dev,
> +                                                            bool cold)
> +{
> +    return cold ? &dev->cold_reset_input : &dev->warm_reset_input;
> +}
> +
> +static void device_reset_handler(DeviceState *dev, bool cold, bool level)
> +{
> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
> +
> +    if (dris->type == DEVICE_RESET_ACTIVE_LOW) {
> +        level = !level;
> +    }
> +
> +    if (dris->state == level) {
> +        /* io state has not changed */
> +        return;
> +    }
> +
> +    dris->state = level;
> +
> +    if (level) {
> +        resettable_assert_reset(OBJECT(dev), cold);
> +    } else {
> +        resettable_deassert_reset(OBJECT(dev));
> +    }
> +}
> +
> +static void device_cold_reset_handler(void *opaque, int n, int level)
> +{
> +    device_reset_handler((DeviceState *) opaque, true, level);
> +}
> +
> +static void device_warm_reset_handler(void *opaque, int n, int level)
> +{
> +    device_reset_handler((DeviceState *) opaque, false, level);
> +}
> +
> +void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
> +                                   bool cold, DeviceResetActiveType type)
> +{
> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
> +    qemu_irq_handler handler;
> +
> +    switch (type) {
> +    case DEVICE_RESET_ACTIVE_LOW:
> +    case DEVICE_RESET_ACTIVE_HIGH:
> +        break;
> +    default:
> +        assert(false);
> +        break;

The usual way to write this is
    g_assert_not_reached();
(and no following 'break').


But the whole switch statement seems to be a complicated way
of writing
   assert(type == DEVICE_RESET_ACTIVE_LOW || type == DEVICE_RESET_ACTIVE_HIGH);

> +    }
> +
> +    assert(!dris->exists);
> +    dris->exists = true;
> +    dris->type = type;
> +
> +    handler = cold ? device_cold_reset_handler : device_warm_reset_handler;
> +    qdev_init_gpio_in_named(dev, handler, name, 1);
> +}

thanks
-- PMM
Damien Hedde Aug. 7, 2019, 4:56 p.m. UTC | #5
On 8/7/19 5:18 PM, Peter Maydell wrote:
> On Mon, 29 Jul 2019 at 15:59, Damien Hedde <damien.hedde@greensocs.com> wrote:
>>
>> It adds the possibility to add 2 gpios to control the warm and cold reset.
>> With theses ios, the reset can be maintained during some time.
>> Each io is associated with a state to detect level changes.
>>
>> Vmstate subsections are also added to the existsing device_reset
>> subsection.
>>
>> Signed-off-by: Damien Hedde <damien.hedde@greensocs.com>
>> ---
>>  hw/core/qdev-vmstate.c | 15 ++++++++++
>>  hw/core/qdev.c         | 65 ++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/qdev-core.h | 57 ++++++++++++++++++++++++++++++++++++
>>  3 files changed, 137 insertions(+)
>>
>> diff --git a/hw/core/qdev-vmstate.c b/hw/core/qdev-vmstate.c
>> index 24f8465c61..72f84c6cee 100644
>> --- a/hw/core/qdev-vmstate.c
>> +++ b/hw/core/qdev-vmstate.c
>> @@ -24,10 +24,23 @@ static int device_vmstate_reset_post_load(void *opaque, int version_id)
>>  {
>>      DeviceState *dev = (DeviceState *) opaque;
>>      BusState *bus;
>> +    uint32_t io_count = 0;
>> +
>>      QLIST_FOREACH(bus, &dev->child_bus, sibling) {
>>          bus->resetting = dev->resetting;
>>          bus->reset_is_cold = dev->reset_is_cold;
>>      }
>> +
>> +    if (dev->cold_reset_input.state) {
>> +        io_count += 1;
>> +    }
>> +    if (dev->warm_reset_input.state) {
>> +        io_count += 1;
>> +    }
>> +    /* ensure resetting count is coherent with io states */
>> +    if (dev->resetting < io_count) {
>> +        return -1;
>> +    }
>>      return 0;
>>  }
>>
>> @@ -40,6 +53,8 @@ const struct VMStateDescription device_vmstate_reset = {
>>      .fields = (VMStateField[]) {
>>          VMSTATE_UINT32(resetting, DeviceState),
>>          VMSTATE_BOOL(reset_is_cold, DeviceState),
>> +        VMSTATE_BOOL(cold_reset_input.state, DeviceState),
>> +        VMSTATE_BOOL(warm_reset_input.state, DeviceState),
> 
> If we're just adding these fields to this VMStateDescription
> then this patch should come earlier in the series than the
> patch where we create and start using the fields. Otherwise
> there's a migration compat break between a QEMU just
> before this patch and a QEMU with it. I think the simplest
> fix is to put this patch before patches 6/7 and have a note
> in the commit message that this functionality can't be used
> until after the patch which adds the migration support.

Independently of the compat break you mention, maybe it is better to
have 'conditional' subsection for each input ?

> 
>>          VMSTATE_END_OF_LIST()
>>      },
>>  };
>> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
>> index 88387d3743..11a4de55ea 100644
>> --- a/hw/core/qdev.c
>> +++ b/hw/core/qdev.c
>> @@ -450,6 +450,67 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
>>      qdev_init_gpio_in_named(dev, handler, NULL, n);
>>  }
>>
>> +static DeviceResetInputState *device_get_reset_input_state(DeviceState *dev,
>> +                                                            bool cold)
>> +{
>> +    return cold ? &dev->cold_reset_input : &dev->warm_reset_input;
>> +}
>> +
>> +static void device_reset_handler(DeviceState *dev, bool cold, bool level)
>> +{
>> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
>> +
>> +    if (dris->type == DEVICE_RESET_ACTIVE_LOW) {
>> +        level = !level;
>> +    }
>> +
>> +    if (dris->state == level) {
>> +        /* io state has not changed */
>> +        return;
>> +    }
>> +
>> +    dris->state = level;
>> +
>> +    if (level) {
>> +        resettable_assert_reset(OBJECT(dev), cold);
>> +    } else {
>> +        resettable_deassert_reset(OBJECT(dev));
>> +    }
>> +}
>> +
>> +static void device_cold_reset_handler(void *opaque, int n, int level)
>> +{
>> +    device_reset_handler((DeviceState *) opaque, true, level);
>> +}
>> +
>> +static void device_warm_reset_handler(void *opaque, int n, int level)
>> +{
>> +    device_reset_handler((DeviceState *) opaque, false, level);
>> +}
>> +
>> +void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
>> +                                   bool cold, DeviceResetActiveType type)
>> +{
>> +    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
>> +    qemu_irq_handler handler;
>> +
>> +    switch (type) {
>> +    case DEVICE_RESET_ACTIVE_LOW:
>> +    case DEVICE_RESET_ACTIVE_HIGH:
>> +        break;
>> +    default:
>> +        assert(false);
>> +        break;
> 
> The usual way to write this is
>     g_assert_not_reached();
> (and no following 'break').
> 
> 
> But the whole switch statement seems to be a complicated way
> of writing
>    assert(type == DEVICE_RESET_ACTIVE_LOW || type == DEVICE_RESET_ACTIVE_HIGH);
> 
>> +    }
>> +
>> +    assert(!dris->exists);
>> +    dris->exists = true;
>> +    dris->type = type;
>> +
>> +    handler = cold ? device_cold_reset_handler : device_warm_reset_handler;
>> +    qdev_init_gpio_in_named(dev, handler, name, 1);
>> +}
> 
> thanks
> -- PMM
>
David Gibson Aug. 9, 2019, 5:51 a.m. UTC | #6
On Wed, Aug 07, 2019 at 11:37:51AM +0100, Peter Maydell wrote:
> On Wed, 31 Jul 2019 at 07:33, David Gibson <david@gibson.dropbear.id.au> wrote:
> >
> > On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
> > > It adds the possibility to add 2 gpios to control the warm and cold reset.
> > > With theses ios, the reset can be maintained during some time.
> > > Each io is associated with a state to detect level changes.
> > >
> > > Vmstate subsections are also added to the existsing device_reset
> > > subsection.
> >
> > This doesn't seem like a thing that should be present on every single
> > DeviceState.
> 
> It's a facility that's going to be useful to multiple different
> subclasses of DeviceState, so it seems to me cleaner to
> have base class support for the common feature rather than
> to reimplement it entirely from scratch in every subclass
> that wants it.

Hm, I suppose so.  Would it really have to be from scratch, though?
Couldn't some suitable helper functions make adding such GPIOs to a
device pretty straightforward?
Damien Hedde Aug. 9, 2019, 8:45 a.m. UTC | #7
On 8/9/19 7:51 AM, David Gibson wrote:
> On Wed, Aug 07, 2019 at 11:37:51AM +0100, Peter Maydell wrote:
>> On Wed, 31 Jul 2019 at 07:33, David Gibson <david@gibson.dropbear.id.au> wrote:
>>>
>>> On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
>>>> It adds the possibility to add 2 gpios to control the warm and cold reset.
>>>> With theses ios, the reset can be maintained during some time.
>>>> Each io is associated with a state to detect level changes.
>>>>
>>>> Vmstate subsections are also added to the existsing device_reset
>>>> subsection.
>>>
>>> This doesn't seem like a thing that should be present on every single
>>> DeviceState.
>>
>> It's a facility that's going to be useful to multiple different
>> subclasses of DeviceState, so it seems to me cleaner to
>> have base class support for the common feature rather than
>> to reimplement it entirely from scratch in every subclass
>> that wants it.
> 
> Hm, I suppose so.  Would it really have to be from scratch, though?
> Couldn't some suitable helper functions make adding such GPIOs to a
> device pretty straightforward?
> 

This patch does that. A device does have to use the helper to add the
gpio. Either qdev_init_warm_reset_gpio(...) or
qdev_init_cold_reset_gpio(...) , like any another gpio.

The mechanics to control the reset with gpio change is done in the base
class and there is some state pre-allocated (and associated vmstate
description) to it.

If that's a problem I can only provide helpers and let devices handle
state allocation and vmstate addition.

Damien
David Gibson Aug. 12, 2019, 10:29 a.m. UTC | #8
On Fri, Aug 09, 2019 at 10:45:43AM +0200, Damien Hedde wrote:
> 
> 
> On 8/9/19 7:51 AM, David Gibson wrote:
> > On Wed, Aug 07, 2019 at 11:37:51AM +0100, Peter Maydell wrote:
> >> On Wed, 31 Jul 2019 at 07:33, David Gibson <david@gibson.dropbear.id.au> wrote:
> >>>
> >>> On Mon, Jul 29, 2019 at 04:56:29PM +0200, Damien Hedde wrote:
> >>>> It adds the possibility to add 2 gpios to control the warm and cold reset.
> >>>> With theses ios, the reset can be maintained during some time.
> >>>> Each io is associated with a state to detect level changes.
> >>>>
> >>>> Vmstate subsections are also added to the existsing device_reset
> >>>> subsection.
> >>>
> >>> This doesn't seem like a thing that should be present on every single
> >>> DeviceState.
> >>
> >> It's a facility that's going to be useful to multiple different
> >> subclasses of DeviceState, so it seems to me cleaner to
> >> have base class support for the common feature rather than
> >> to reimplement it entirely from scratch in every subclass
> >> that wants it.
> > 
> > Hm, I suppose so.  Would it really have to be from scratch, though?
> > Couldn't some suitable helper functions make adding such GPIOs to a
> > device pretty straightforward?
> > 
> 
> This patch does that. A device does have to use the helper to add the
> gpio. Either qdev_init_warm_reset_gpio(...) or
> qdev_init_cold_reset_gpio(...) , like any another gpio.

Ah, sorry, I misunderstood.

That seems ok then.

> 
> The mechanics to control the reset with gpio change is done in the base
> class and there is some state pre-allocated (and associated vmstate
> description) to it.
> 
> If that's a problem I can only provide helpers and let devices handle
> state allocation and vmstate addition.
> 
> Damien
>
diff mbox series

Patch

diff --git a/hw/core/qdev-vmstate.c b/hw/core/qdev-vmstate.c
index 24f8465c61..72f84c6cee 100644
--- a/hw/core/qdev-vmstate.c
+++ b/hw/core/qdev-vmstate.c
@@ -24,10 +24,23 @@  static int device_vmstate_reset_post_load(void *opaque, int version_id)
 {
     DeviceState *dev = (DeviceState *) opaque;
     BusState *bus;
+    uint32_t io_count = 0;
+
     QLIST_FOREACH(bus, &dev->child_bus, sibling) {
         bus->resetting = dev->resetting;
         bus->reset_is_cold = dev->reset_is_cold;
     }
+
+    if (dev->cold_reset_input.state) {
+        io_count += 1;
+    }
+    if (dev->warm_reset_input.state) {
+        io_count += 1;
+    }
+    /* ensure resetting count is coherent with io states */
+    if (dev->resetting < io_count) {
+        return -1;
+    }
     return 0;
 }
 
@@ -40,6 +53,8 @@  const struct VMStateDescription device_vmstate_reset = {
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(resetting, DeviceState),
         VMSTATE_BOOL(reset_is_cold, DeviceState),
+        VMSTATE_BOOL(cold_reset_input.state, DeviceState),
+        VMSTATE_BOOL(warm_reset_input.state, DeviceState),
         VMSTATE_END_OF_LIST()
     },
 };
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 88387d3743..11a4de55ea 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -450,6 +450,67 @@  void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
     qdev_init_gpio_in_named(dev, handler, NULL, n);
 }
 
+static DeviceResetInputState *device_get_reset_input_state(DeviceState *dev,
+                                                            bool cold)
+{
+    return cold ? &dev->cold_reset_input : &dev->warm_reset_input;
+}
+
+static void device_reset_handler(DeviceState *dev, bool cold, bool level)
+{
+    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
+
+    if (dris->type == DEVICE_RESET_ACTIVE_LOW) {
+        level = !level;
+    }
+
+    if (dris->state == level) {
+        /* io state has not changed */
+        return;
+    }
+
+    dris->state = level;
+
+    if (level) {
+        resettable_assert_reset(OBJECT(dev), cold);
+    } else {
+        resettable_deassert_reset(OBJECT(dev));
+    }
+}
+
+static void device_cold_reset_handler(void *opaque, int n, int level)
+{
+    device_reset_handler((DeviceState *) opaque, true, level);
+}
+
+static void device_warm_reset_handler(void *opaque, int n, int level)
+{
+    device_reset_handler((DeviceState *) opaque, false, level);
+}
+
+void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
+                                   bool cold, DeviceResetActiveType type)
+{
+    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
+    qemu_irq_handler handler;
+
+    switch (type) {
+    case DEVICE_RESET_ACTIVE_LOW:
+    case DEVICE_RESET_ACTIVE_HIGH:
+        break;
+    default:
+        assert(false);
+        break;
+    }
+
+    assert(!dris->exists);
+    dris->exists = true;
+    dris->type = type;
+
+    handler = cold ? device_cold_reset_handler : device_warm_reset_handler;
+    qdev_init_gpio_in_named(dev, handler, name, 1);
+}
+
 void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
                               const char *name, int n)
 {
@@ -1007,6 +1068,10 @@  static void device_initfn(Object *obj)
     dev->instance_id_alias = -1;
     dev->realized = false;
     dev->resetting = 0;
+    dev->cold_reset_input.exists = false;
+    dev->cold_reset_input.state = false;
+    dev->warm_reset_input.exists = false;
+    dev->warm_reset_input.state = false;
 
     object_property_add_bool(obj, "realized",
                              device_get_realized, device_set_realized, NULL);
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 926d4bbcb1..f724ddc8f4 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -136,6 +136,23 @@  struct NamedGPIOList {
     QLIST_ENTRY(NamedGPIOList) node;
 };
 
+typedef enum DeviceResetActiveType {
+    DEVICE_RESET_ACTIVE_LOW,
+    DEVICE_RESET_ACTIVE_HIGH,
+} DeviceResetActiveType;
+
+/**
+ * DeviceResetInputState:
+ * @exists: tell if io exists
+ * @type: tell whether the io active low or high
+ * @state: true if reset is currently active
+ */
+typedef struct DeviceResetInputState {
+    bool exists;
+    DeviceResetActiveType type;
+    bool state;
+} DeviceResetInputState;
+
 /**
  * DeviceState:
  * @realized: Indicates whether the device has been fully constructed.
@@ -143,6 +160,8 @@  struct NamedGPIOList {
  * used to count how many times reset has been initiated on the device.
  * @reset_is_cold: If the device is under reset, indicates whether it is cold
  * or warm.
+ * @cold_reset_input: state data for cold reset io
+ * @warm_reset_input: state data for warm reset io
  *
  * This structure should not be accessed directly.  We declare it here
  * so that it can be embedded in individual device state structures.
@@ -167,6 +186,8 @@  struct DeviceState {
     uint32_t resetting;
     bool reset_is_cold;
     bool reset_hold_needed;
+    DeviceResetInputState cold_reset_input;
+    DeviceResetInputState warm_reset_input;
 };
 
 struct DeviceListener {
@@ -372,6 +393,42 @@  static inline void qdev_init_gpio_in_named(DeviceState *dev,
 void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
                      const char *name);
 
+/**
+ * qdev_init_reset_gpio_in_named:
+ * Create a gpio controlling the warm or cold reset of the device.
+ *
+ * @cold: specify whether it triggers cold or warm reset
+ * @type: what kind of reset io it is
+ *
+ * Note: the io is considered created in its inactive state. No reset
+ * is started by this function.
+ */
+void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
+                                   bool cold, DeviceResetActiveType type);
+
+/**
+ * qdev_init_warm_reset_gpio:
+ * Create the input to control the device warm reset.
+ */
+static inline void qdev_init_warm_reset_gpio(DeviceState *dev,
+                                             const char *name,
+                                             DeviceResetActiveType type)
+{
+    qdev_init_reset_gpio_in_named(dev, name, false, type);
+}
+
+/**
+ * qdev_init_cold_reset_gpio:
+ * Create the input to control the device cold reset.
+ * It can also be used as a power gate control.
+ */
+static inline void qdev_init_cold_reset_gpio(DeviceState *dev,
+                                             const char *name,
+                                             DeviceResetActiveType type)
+{
+    qdev_init_reset_gpio_in_named(dev, name, true, type);
+}
+
 BusState *qdev_get_parent_bus(DeviceState *dev);
 
 /*** BUS API. ***/