diff mbox series

[v8,04/10] hw/ssi: imx_spi: Rework imx_spi_reset() to keep CONREG register value

Message ID 1611063546-20278-5-git-send-email-bmeng.cn@gmail.com (mailing list archive)
State New, archived
Headers show
Series hw/ssi: imx_spi: Fix various bugs in the imx_spi model | expand

Commit Message

Bin Meng Jan. 19, 2021, 1:39 p.m. UTC
From: Philippe Mathieu-Daudé <f4bug@amsat.org>

When the block is disabled, all registers are reset with the
exception of the ECSPI_CONREG. It is initialized to zero
when the instance is created.

Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
     chapter 21.7.3: Control Register (ECSPIx_CONREG)

Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <20210115153049.3353008-4-f4bug@amsat.org>
Reviewed-by: Bin Meng <bin.meng@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>

---

(no changes since v7)

Changes in v7:
- remove the RFC tag

Changes in v6:
- new patch: [RFC] rework imx_spi_reset() to keep CONREG register value

 hw/ssi/imx_spi.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

Comments

Peter Maydell Jan. 28, 2021, 1:43 p.m. UTC | #1
On Tue, 19 Jan 2021 at 13:40, Bin Meng <bmeng.cn@gmail.com> wrote:
>
> From: Philippe Mathieu-Daudé <f4bug@amsat.org>
>
> When the block is disabled, all registers are reset with the
> exception of the ECSPI_CONREG. It is initialized to zero
> when the instance is created.
>
> Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
>      chapter 21.7.3: Control Register (ECSPIx_CONREG)

> diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
> index 8fb3c9b..c952a3d 100644
> --- a/hw/ssi/imx_spi.c
> +++ b/hw/ssi/imx_spi.c
> @@ -231,12 +231,23 @@ static void imx_spi_flush_txfifo(IMXSPIState *s)
>  static void imx_spi_reset(DeviceState *dev)
>  {
>      IMXSPIState *s = IMX_SPI(dev);
> +    int i;
>
>      DPRINTF("\n");
>
> -    memset(s->regs, 0, sizeof(s->regs));
> -
> -    s->regs[ECSPI_STATREG] = 0x00000003;
> +    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
> +        switch (i) {
> +        case ECSPI_CONREG:
> +            /* CONREG is not updated on reset */
> +            break;
> +        case ECSPI_STATREG:
> +            s->regs[i] = 0x00000003;
> +            break;
> +        default:
> +            s->regs[i] = 0;
> +            break;
> +        }
> +    }

This retains the CONREG register value for both:
 * 'soft' reset caused by write to device register to disable
   the block
   -- this is corrcet as per the datasheet quote
 * 'power on' reset via TYPE_DEVICE's reset method
   -- but in this case we should reset CONREG, because the Device
   reset method is like a complete device powercycle and should
   return the device state to what it was when QEMU was first
   started.

thanks
-- PMM
Bin Meng Jan. 28, 2021, 1:46 p.m. UTC | #2
On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Tue, 19 Jan 2021 at 13:40, Bin Meng <bmeng.cn@gmail.com> wrote:
> >
> > From: Philippe Mathieu-Daudé <f4bug@amsat.org>
> >
> > When the block is disabled, all registers are reset with the
> > exception of the ECSPI_CONREG. It is initialized to zero
> > when the instance is created.
> >
> > Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
> >      chapter 21.7.3: Control Register (ECSPIx_CONREG)
>
> > diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
> > index 8fb3c9b..c952a3d 100644
> > --- a/hw/ssi/imx_spi.c
> > +++ b/hw/ssi/imx_spi.c
> > @@ -231,12 +231,23 @@ static void imx_spi_flush_txfifo(IMXSPIState *s)
> >  static void imx_spi_reset(DeviceState *dev)
> >  {
> >      IMXSPIState *s = IMX_SPI(dev);
> > +    int i;
> >
> >      DPRINTF("\n");
> >
> > -    memset(s->regs, 0, sizeof(s->regs));
> > -
> > -    s->regs[ECSPI_STATREG] = 0x00000003;
> > +    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
> > +        switch (i) {
> > +        case ECSPI_CONREG:
> > +            /* CONREG is not updated on reset */
> > +            break;
> > +        case ECSPI_STATREG:
> > +            s->regs[i] = 0x00000003;
> > +            break;
> > +        default:
> > +            s->regs[i] = 0;
> > +            break;
> > +        }
> > +    }
>
> This retains the CONREG register value for both:
>  * 'soft' reset caused by write to device register to disable
>    the block
>    -- this is corrcet as per the datasheet quote
>  * 'power on' reset via TYPE_DEVICE's reset method
>    -- but in this case we should reset CONREG, because the Device
>    reset method is like a complete device powercycle and should
>    return the device state to what it was when QEMU was first
>    started.

The POR value of CONREG is zero, which should be the default value, no?

Regards,
Bin
Peter Maydell Jan. 28, 2021, 1:54 p.m. UTC | #3
On Thu, 28 Jan 2021 at 13:47, Bin Meng <bmeng.cn@gmail.com> wrote:
>
> On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
> >
> > On Tue, 19 Jan 2021 at 13:40, Bin Meng <bmeng.cn@gmail.com> wrote:
> > >
> > > From: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > >
> > > When the block is disabled, all registers are reset with the
> > > exception of the ECSPI_CONREG. It is initialized to zero
> > > when the instance is created.
> > >
> > > Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
> > >      chapter 21.7.3: Control Register (ECSPIx_CONREG)
> >
> > > diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
> > > index 8fb3c9b..c952a3d 100644
> > > --- a/hw/ssi/imx_spi.c
> > > +++ b/hw/ssi/imx_spi.c
> > > @@ -231,12 +231,23 @@ static void imx_spi_flush_txfifo(IMXSPIState *s)
> > >  static void imx_spi_reset(DeviceState *dev)
> > >  {
> > >      IMXSPIState *s = IMX_SPI(dev);
> > > +    int i;
> > >
> > >      DPRINTF("\n");
> > >
> > > -    memset(s->regs, 0, sizeof(s->regs));
> > > -
> > > -    s->regs[ECSPI_STATREG] = 0x00000003;
> > > +    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
> > > +        switch (i) {
> > > +        case ECSPI_CONREG:
> > > +            /* CONREG is not updated on reset */
> > > +            break;
> > > +        case ECSPI_STATREG:
> > > +            s->regs[i] = 0x00000003;
> > > +            break;
> > > +        default:
> > > +            s->regs[i] = 0;
> > > +            break;
> > > +        }
> > > +    }
> >
> > This retains the CONREG register value for both:
> >  * 'soft' reset caused by write to device register to disable
> >    the block
> >    -- this is corrcet as per the datasheet quote
> >  * 'power on' reset via TYPE_DEVICE's reset method
> >    -- but in this case we should reset CONREG, because the Device
> >    reset method is like a complete device powercycle and should
> >    return the device state to what it was when QEMU was first
> >    started.
>
> The POR value of CONREG is zero, which should be the default value, no?

But you're not setting it to zero here, you're leaving it with
whatever value it had before. (That's correct for soft reset,
but wrong for power-on.)

thanks
-- PMM
Bin Meng Jan. 28, 2021, 2:15 p.m. UTC | #4
On Thu, Jan 28, 2021 at 9:54 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Thu, 28 Jan 2021 at 13:47, Bin Meng <bmeng.cn@gmail.com> wrote:
> >
> > On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
> > >
> > > On Tue, 19 Jan 2021 at 13:40, Bin Meng <bmeng.cn@gmail.com> wrote:
> > > >
> > > > From: Philippe Mathieu-Daudé <f4bug@amsat.org>
> > > >
> > > > When the block is disabled, all registers are reset with the
> > > > exception of the ECSPI_CONREG. It is initialized to zero
> > > > when the instance is created.
> > > >
> > > > Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
> > > >      chapter 21.7.3: Control Register (ECSPIx_CONREG)
> > >
> > > > diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
> > > > index 8fb3c9b..c952a3d 100644
> > > > --- a/hw/ssi/imx_spi.c
> > > > +++ b/hw/ssi/imx_spi.c
> > > > @@ -231,12 +231,23 @@ static void imx_spi_flush_txfifo(IMXSPIState *s)
> > > >  static void imx_spi_reset(DeviceState *dev)
> > > >  {
> > > >      IMXSPIState *s = IMX_SPI(dev);
> > > > +    int i;
> > > >
> > > >      DPRINTF("\n");
> > > >
> > > > -    memset(s->regs, 0, sizeof(s->regs));
> > > > -
> > > > -    s->regs[ECSPI_STATREG] = 0x00000003;
> > > > +    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
> > > > +        switch (i) {
> > > > +        case ECSPI_CONREG:
> > > > +            /* CONREG is not updated on reset */
> > > > +            break;
> > > > +        case ECSPI_STATREG:
> > > > +            s->regs[i] = 0x00000003;
> > > > +            break;
> > > > +        default:
> > > > +            s->regs[i] = 0;
> > > > +            break;
> > > > +        }
> > > > +    }
> > >
> > > This retains the CONREG register value for both:
> > >  * 'soft' reset caused by write to device register to disable
> > >    the block
> > >    -- this is corrcet as per the datasheet quote
> > >  * 'power on' reset via TYPE_DEVICE's reset method
> > >    -- but in this case we should reset CONREG, because the Device
> > >    reset method is like a complete device powercycle and should
> > >    return the device state to what it was when QEMU was first
> > >    started.
> >
> > The POR value of CONREG is zero, which should be the default value, no?
>
> But you're not setting it to zero here, you're leaving it with
> whatever value it had before. (That's correct for soft reset,
> but wrong for power-on.)

I think that's ensured by object_initialize_with_type().

Regards,
Bin
Philippe Mathieu-Daudé Jan. 28, 2021, 2:17 p.m. UTC | #5
On 1/28/21 2:54 PM, Peter Maydell wrote:
> On Thu, 28 Jan 2021 at 13:47, Bin Meng <bmeng.cn@gmail.com> wrote:
>>
>> On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>>>
>>> On Tue, 19 Jan 2021 at 13:40, Bin Meng <bmeng.cn@gmail.com> wrote:
>>>>
>>>> From: Philippe Mathieu-Daudé <f4bug@amsat.org>
>>>>
>>>> When the block is disabled, all registers are reset with the
>>>> exception of the ECSPI_CONREG. It is initialized to zero
>>>> when the instance is created.
>>>>
>>>> Ref: i.MX 6DQ Applications Processor Reference Manual (IMX6DQRM),
>>>>      chapter 21.7.3: Control Register (ECSPIx_CONREG)
>>>
>>>> diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
>>>> index 8fb3c9b..c952a3d 100644
>>>> --- a/hw/ssi/imx_spi.c
>>>> +++ b/hw/ssi/imx_spi.c
>>>> @@ -231,12 +231,23 @@ static void imx_spi_flush_txfifo(IMXSPIState *s)
>>>>  static void imx_spi_reset(DeviceState *dev)
>>>>  {
>>>>      IMXSPIState *s = IMX_SPI(dev);
>>>> +    int i;
>>>>
>>>>      DPRINTF("\n");
>>>>
>>>> -    memset(s->regs, 0, sizeof(s->regs));
>>>> -
>>>> -    s->regs[ECSPI_STATREG] = 0x00000003;
>>>> +    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
>>>> +        switch (i) {
>>>> +        case ECSPI_CONREG:
>>>> +            /* CONREG is not updated on reset */
>>>> +            break;
>>>> +        case ECSPI_STATREG:
>>>> +            s->regs[i] = 0x00000003;
>>>> +            break;
>>>> +        default:
>>>> +            s->regs[i] = 0;
>>>> +            break;
>>>> +        }
>>>> +    }
>>>
>>> This retains the CONREG register value for both:
>>>  * 'soft' reset caused by write to device register to disable
>>>    the block
>>>    -- this is corrcet as per the datasheet quote
>>>  * 'power on' reset via TYPE_DEVICE's reset method
>>>    -- but in this case we should reset CONREG, because the Device
>>>    reset method is like a complete device powercycle and should
>>>    return the device state to what it was when QEMU was first
>>>    started.
>>
>> The POR value of CONREG is zero, which should be the default value, no?
> 
> But you're not setting it to zero here, you're leaving it with
> whatever value it had before. (That's correct for soft reset,
> but wrong for power-on.)

zero value on power-on is what I tried to describe as
"It is initialized to zero when the instance is created."

Most of the codebase assumes QOM provides a zero-initialized
instance state. Do you think it should be explicit?

Regards,

Phil.
Peter Maydell Jan. 28, 2021, 2:22 p.m. UTC | #6
On Thu, 28 Jan 2021 at 14:18, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 1/28/21 2:54 PM, Peter Maydell wrote:
> > On Thu, 28 Jan 2021 at 13:47, Bin Meng <bmeng.cn@gmail.com> wrote:
> >>
> >> On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
> >>> This retains the CONREG register value for both:
> >>>  * 'soft' reset caused by write to device register to disable
> >>>    the block
> >>>    -- this is corrcet as per the datasheet quote
> >>>  * 'power on' reset via TYPE_DEVICE's reset method
> >>>    -- but in this case we should reset CONREG, because the Device
> >>>    reset method is like a complete device powercycle and should
> >>>    return the device state to what it was when QEMU was first
> >>>    started.
> >>
> >> The POR value of CONREG is zero, which should be the default value, no?
> >
> > But you're not setting it to zero here, you're leaving it with
> > whatever value it had before. (That's correct for soft reset,
> > but wrong for power-on.)
>
> zero value on power-on is what I tried to describe as
> "It is initialized to zero when the instance is created."

Yes, but QOM device reset does not happen just once at startup and
not thereafter. Consider:

 * user starts QEMU
 * QOM devices are created and realized
 * QOM device reset happens
     -- CONREG is zero here because QOM structs are zero-initialized
 * guest runs
 * guest modifies CONREG from its initial value
 * system reset is requested (perhaps by user, perhaps by
   guest writing some register or another)
 * QOM device reset happens
     -- CONREG is not zero here, so reset must clear it

thanks
-- PMM
Philippe Mathieu-Daudé Jan. 28, 2021, 2:32 p.m. UTC | #7
On 1/28/21 3:22 PM, Peter Maydell wrote:
> On Thu, 28 Jan 2021 at 14:18, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>
>> On 1/28/21 2:54 PM, Peter Maydell wrote:
>>> On Thu, 28 Jan 2021 at 13:47, Bin Meng <bmeng.cn@gmail.com> wrote:
>>>>
>>>> On Thu, Jan 28, 2021 at 9:43 PM Peter Maydell <peter.maydell@linaro.org> wrote:
>>>>> This retains the CONREG register value for both:
>>>>>  * 'soft' reset caused by write to device register to disable
>>>>>    the block
>>>>>    -- this is corrcet as per the datasheet quote
>>>>>  * 'power on' reset via TYPE_DEVICE's reset method
>>>>>    -- but in this case we should reset CONREG, because the Device
>>>>>    reset method is like a complete device powercycle and should
>>>>>    return the device state to what it was when QEMU was first
>>>>>    started.
>>>>
>>>> The POR value of CONREG is zero, which should be the default value, no?
>>>
>>> But you're not setting it to zero here, you're leaving it with
>>> whatever value it had before. (That's correct for soft reset,
>>> but wrong for power-on.)
>>
>> zero value on power-on is what I tried to describe as
>> "It is initialized to zero when the instance is created."
> 
> Yes, but QOM device reset does not happen just once at startup and
> not thereafter. Consider:
> 
>  * user starts QEMU
>  * QOM devices are created and realized
>  * QOM device reset happens
>      -- CONREG is zero here because QOM structs are zero-initialized
>  * guest runs
>  * guest modifies CONREG from its initial value
>  * system reset is requested (perhaps by user, perhaps by
>    guest writing some register or another)
>  * QOM device reset happens
>      -- CONREG is not zero here, so reset must clear it

Oh I totally missed that :S

Bin, I'd correct this as:

- extract imx_spi_soft_reset(IMXSPIState *s) from imx_spi_reset()
- zero-initialize CONREG in imx_spi_reset().

static void imx_spi_soft_reset(IMXSPIState *s)
{
 ...
}

static void imx_spi_reset(DeviceState *dev)
{
    IMXSPIState *s = IMX_SPI(dev);

    s->regs[ECSPI_CONREG] = 0;
    imx_spi_soft_reset(s);
}

What do you think?

Phil.
Peter Maydell Jan. 28, 2021, 2:41 p.m. UTC | #8
On Thu, 28 Jan 2021 at 14:32, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> Oh I totally missed that :S
>
> Bin, I'd correct this as:
>
> - extract imx_spi_soft_reset(IMXSPIState *s) from imx_spi_reset()
> - zero-initialize CONREG in imx_spi_reset().
>
> static void imx_spi_soft_reset(IMXSPIState *s)
> {
>  ...
> }
>
> static void imx_spi_reset(DeviceState *dev)
> {
>     IMXSPIState *s = IMX_SPI(dev);
>
>     s->regs[ECSPI_CONREG] = 0;
>     imx_spi_soft_reset(s);
> }
>
> What do you think?

That doesn't give you anywhere to put the imx_spi_update_irq()
call, which must happen only on soft reset and not on DeviceState
reset. You could do one of:
 * have a 'common reset' function that does most of this,
   plus an imx_spi_reset which clears CONREG and calls
   common reset and an imx_spi_soft_reset which calls
   common reset and imx_spi_update_irq()
 * have imx_spi_soft_reset save the old CONREG in a local
   variable before calling imx_spi_reset and then restore it
   to s->regs

thanks
-- PMM
Philippe Mathieu-Daudé Jan. 28, 2021, 2:49 p.m. UTC | #9
On 1/28/21 3:41 PM, Peter Maydell wrote:
> On Thu, 28 Jan 2021 at 14:32, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> Oh I totally missed that :S
>>
>> Bin, I'd correct this as:
>>
>> - extract imx_spi_soft_reset(IMXSPIState *s) from imx_spi_reset()
>> - zero-initialize CONREG in imx_spi_reset().
>>
>> static void imx_spi_soft_reset(IMXSPIState *s)
>> {
>>  ...
>> }
>>
>> static void imx_spi_reset(DeviceState *dev)
>> {
>>     IMXSPIState *s = IMX_SPI(dev);
>>
>>     s->regs[ECSPI_CONREG] = 0;
>>     imx_spi_soft_reset(s);
>> }
>>
>> What do you think?
> 
> That doesn't give you anywhere to put the imx_spi_update_irq()
> call, which must happen only on soft reset and not on DeviceState
> reset. You could do one of:
>  * have a 'common reset' function that does most of this,
>    plus an imx_spi_reset which clears CONREG and calls
>    common reset and an imx_spi_soft_reset which calls
>    common reset and imx_spi_update_irq()
>  * have imx_spi_soft_reset save the old CONREG in a local
>    variable before calling imx_spi_reset and then restore it
>    to s->regs

Long term maintenance I'd prefer the 'common reset' approach
(but this is probably subjective to my view on the hardware,
since this is software, the 2nd approach is also valid but
harder to represent thinking of hardware).

Bin, can you send a v9? (using the approach you prefer)

Thanks,

Phil.
Bin Meng Jan. 28, 2021, 3 p.m. UTC | #10
On Thu, Jan 28, 2021 at 10:49 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 1/28/21 3:41 PM, Peter Maydell wrote:
> > On Thu, 28 Jan 2021 at 14:32, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >> Oh I totally missed that :S
> >>
> >> Bin, I'd correct this as:
> >>
> >> - extract imx_spi_soft_reset(IMXSPIState *s) from imx_spi_reset()
> >> - zero-initialize CONREG in imx_spi_reset().
> >>
> >> static void imx_spi_soft_reset(IMXSPIState *s)
> >> {
> >>  ...
> >> }
> >>
> >> static void imx_spi_reset(DeviceState *dev)
> >> {
> >>     IMXSPIState *s = IMX_SPI(dev);
> >>
> >>     s->regs[ECSPI_CONREG] = 0;
> >>     imx_spi_soft_reset(s);
> >> }
> >>
> >> What do you think?
> >
> > That doesn't give you anywhere to put the imx_spi_update_irq()
> > call, which must happen only on soft reset and not on DeviceState
> > reset. You could do one of:
> >  * have a 'common reset' function that does most of this,
> >    plus an imx_spi_reset which clears CONREG and calls
> >    common reset and an imx_spi_soft_reset which calls
> >    common reset and imx_spi_update_irq()
> >  * have imx_spi_soft_reset save the old CONREG in a local
> >    variable before calling imx_spi_reset and then restore it
> >    to s->regs
>
> Long term maintenance I'd prefer the 'common reset' approach
> (but this is probably subjective to my view on the hardware,
> since this is software, the 2nd approach is also valid but
> harder to represent thinking of hardware).
>
> Bin, can you send a v9? (using the approach you prefer)
>

Yes, I will send a v9.

Thanks Peter for the explanation on the QOM device reset scenarios.

Regards,
Bin
diff mbox series

Patch

diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c
index 8fb3c9b..c952a3d 100644
--- a/hw/ssi/imx_spi.c
+++ b/hw/ssi/imx_spi.c
@@ -231,12 +231,23 @@  static void imx_spi_flush_txfifo(IMXSPIState *s)
 static void imx_spi_reset(DeviceState *dev)
 {
     IMXSPIState *s = IMX_SPI(dev);
+    int i;
 
     DPRINTF("\n");
 
-    memset(s->regs, 0, sizeof(s->regs));
-
-    s->regs[ECSPI_STATREG] = 0x00000003;
+    for (i = 0; i < ARRAY_SIZE(s->regs); i++) {
+        switch (i) {
+        case ECSPI_CONREG:
+            /* CONREG is not updated on reset */
+            break;
+        case ECSPI_STATREG:
+            s->regs[i] = 0x00000003;
+            break;
+        default:
+            s->regs[i] = 0;
+            break;
+        }
+    }
 
     imx_spi_rxfifo_reset(s);
     imx_spi_txfifo_reset(s);