All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
@ 2019-02-26 11:38 bzt
  2019-02-26 12:25 ` Peter Maydell
  2019-03-04 15:55 ` Peter Maydell
  0 siblings, 2 replies; 16+ messages in thread
From: bzt @ 2019-02-26 11:38 UTC (permalink / raw)
  To: Peter Maydell, Andrew Baumann, Philippe Mathieu-Daudé,
	qemu-arm, qemu-devel

Dear qemu developers,

Honestly SubmitAPatch is a bit complicated for me, so I'm hoping I've
done everything right. To be sure, you can find my patch here:
https://github.com/bztsrc/qemu-local-timer and diff against the latest
github repo here:
https://github.com/bztsrc/qemu-local-timer/blob/patches/3.1.50.diff

Currently the IRQ_TIMER in bcm2836_control is defined, but not
implemented. This patch adds the basic functionality to qemu by
implementing 3 new registers in bcm2836_control. You can route the
interrupt to one of the cores' IRQ or FIQ line by writing 0x40000024,
and you can set up a periodic interrupt with 38.4MHz frequency by
writing the divider into 0x40000034. Prescaler are not taken into
account. When the IRQ fired, you'll see it in 0x40000040+4*N bit 11
with the rest of the local IRQs, and you can acknowledge it by writing
1<<31 to 0x40000038.

The patch is pretty simple, should be easy to review: it does not
create new classes, does not delete anything, does not change class
interface, and only two files modified, the bcm2836_control.c and it's
header.

Sign-off-by: Zoltán Baldaszti <bztemail@gmail.com>
Subject: [PATCH] Added periodic IRQ support for bcm2836_control local timer

diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
index cfa5bc7365..fbd31de0f1 100644
--- a/hw/intc/bcm2836_control.c
+++ b/hw/intc/bcm2836_control.c
@@ -7,7 +7,11 @@
  * This code is licensed under the GNU GPLv2 and later.
  *
  * At present, only implements interrupt routing, and mailboxes (i.e.,
- * not local timer, PMU interrupt, or AXI counters).
+ * not PMU interrupt, or AXI counters).
+ *
+ * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
+ * The IRQ_TIMER support is still very basic, does not handle timer access,
+ * and such there's no point in enabling it without the interrupt flag set.
  *
  * Ref:
  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
@@ -18,6 +22,9 @@
 #include "qemu/log.h"

 #define REG_GPU_ROUTE           0x0c
+#define REG_LOCALTIMERROUTING   0x24
+#define REG_LOCALTIMERCONTROL   0x34
+#define REG_LOCALTIMERACK       0x38
 #define REG_TIMERCONTROL        0x40
 #define REG_MBOXCONTROL         0x50
 #define REG_IRQSRC              0x60
@@ -43,6 +50,13 @@
 #define IRQ_TIMER       11
 #define IRQ_MAX         IRQ_TIMER

+#define LOCALTIMER_FREQ      38400000
+#define LOCALTIMER_INTFLAG   (1 << 31)
+#define LOCALTIMER_RELOAD    (1 << 30)
+#define LOCALTIMER_INTENABLE (1 << 29)
+#define LOCALTIMER_ENABLE    (1 << 28)
+#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
+
 static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
                           uint32_t controlreg, uint8_t controlidx)
 {
@@ -78,6 +92,15 @@ static void bcm2836_control_update(BCM2836ControlState *s)
         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
     }

+    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
+    if (s->triggered) {
+        if (s->route_localtimer & 4) {
+            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        } else {
+            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        }
+    }
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         /* handle local timer interrupts for this core */
         if (s->timerirqs[i]) {
@@ -162,6 +185,62 @@ static void bcm2836_control_set_gpu_fiq(void
*opaque, int irq, int level)
     bcm2836_control_update(s);
 }

+static void bcm2836_control_local_timer_set_next(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+    uint64_t next_event;
+
+    assert(s->period > 0);
+
+    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        (NANOSECONDS_PER_SECOND * s->period / LOCALTIMER_FREQ);
+    timer_mod(s->timer, next_event);
+}
+
+static void bcm2836_control_local_timer_tick(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+
+    bcm2836_control_local_timer_set_next(s);
+
+    if (!s->triggered) {
+        s->triggered = 1;
+        bcm2836_control_update(s);
+    }
+}
+
+static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    s->period = LOCALTIMER_VALUE(val);
+    if (val & LOCALTIMER_INTENABLE) {
+        if (!s->timer) {
+            s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                bcm2836_control_local_timer_tick, s);
+        }
+        bcm2836_control_local_timer_set_next(s);
+    } else {
+        if (s->timer) {
+            timer_del(s->timer);
+            s->timer = NULL;
+        }
+        s->triggered = 0;
+    }
+}
+
+static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    if (val & LOCALTIMER_INTFLAG) {
+        s->triggered = 0;
+    }
+    if (val & LOCALTIMER_RELOAD) {
+        bcm2836_control_local_timer_set_next(s);
+    }
+}
+
 static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
unsigned size)
 {
     BCM2836ControlState *s = opaque;
@@ -170,6 +249,14 @@ static uint64_t bcm2836_control_read(void
*opaque, hwaddr offset, unsigned size)
         assert(s->route_gpu_fiq < BCM2836_NCORES
                && s->route_gpu_irq < BCM2836_NCORES);
         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        return s->route_localtimer;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        return s->period |
+            (s->timer ? LOCALTIMER_ENABLE | LOCALTIMER_INTENABLE : 0) |
+            (s->triggered ? LOCALTIMER_INTFLAG : 0);
+    } else if (offset == REG_LOCALTIMERACK) {
+        return 0;
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -195,6 +282,12 @@ static void bcm2836_control_write(void *opaque,
hwaddr offset,
     if (offset == REG_GPU_ROUTE) {
         s->route_gpu_irq = val & 0x3;
         s->route_gpu_fiq = (val >> 2) & 0x3;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        s->route_localtimer = val & 7;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        bcm2836_control_local_timer_control(s, val);
+    } else if (offset == REG_LOCALTIMERACK) {
+        bcm2836_control_local_timer_ack(s, val);
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -227,6 +320,13 @@ static void bcm2836_control_reset(DeviceState *d)

     s->route_gpu_irq = s->route_gpu_fiq = 0;

+    s->route_localtimer = s->triggered = 0;
+    s->period = 0;
+    if(s->timer) {
+        timer_del(s->timer);
+        s->timer = NULL;
+    }
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         s->timercontrol[i] = 0;
         s->mailboxcontrol[i] = 0;
diff --git a/include/hw/intc/bcm2836_control.h
b/include/hw/intc/bcm2836_control.h
index 613f3c4186..873bd52253 100644
--- a/include/hw/intc/bcm2836_control.h
+++ b/include/hw/intc/bcm2836_control.h
@@ -5,6 +5,9 @@
  * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
  * Written by Andrew Baumann
  *
+ * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
+ * Added basic IRQ_TIMER interrupt support
+ *
  * This code is licensed under the GNU GPLv2 and later.
  */

@@ -12,6 +15,7 @@
 #define BCM2836_CONTROL_H

 #include "hw/sysbus.h"
+#include "qemu/timer.h"

 /* 4 mailboxes per core, for 16 total */
 #define BCM2836_NCORES 4
@@ -39,6 +43,12 @@ typedef struct BCM2836ControlState {
     bool gpu_irq, gpu_fiq;
     uint8_t timerirqs[BCM2836_NCORES];

+    /* local timer */
+    uint8_t route_localtimer;
+    uint32_t period;
+    bool triggered;
+    QEMUTimer *timer;
+
     /* interrupt source registers, post-routing (also input-derived;
visible) */
     uint32_t irqsrc[BCM2836_NCORES];
     uint32_t fiqsrc[BCM2836_NCORES];

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-02-26 11:38 [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer bzt
@ 2019-02-26 12:25 ` Peter Maydell
  2019-02-27 11:54   ` bzt
  2019-03-04 15:55 ` Peter Maydell
  1 sibling, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-02-26 12:25 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Tue, 26 Feb 2019 at 11:38, bzt <bztemail@gmail.com> wrote:
>
> Dear qemu developers,
>
> Honestly SubmitAPatch is a bit complicated for me, so I'm hoping I've
> done everything right. To be sure, you can find my patch here:
> https://github.com/bztsrc/qemu-local-timer and diff against the latest
> github repo here:
> https://github.com/bztsrc/qemu-local-timer/blob/patches/3.1.50.diff

Thanks for the patch. You've got the most important things
right -- patch sent as an email, and with a Signed-off-by:
line from you. I've put this on my to-review queue, and I
should be able to get to it within a week or so.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-02-26 12:25 ` Peter Maydell
@ 2019-02-27 11:54   ` bzt
  2019-02-27 18:30     ` Andrew Baumann
  0 siblings, 1 reply; 16+ messages in thread
From: bzt @ 2019-02-27 11:54 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi Peter,

Thank you very much! Let me know if I need to modify anything! Btw you
should update the maintainer's list, currently it does not list you as
a reviewer :-)

I'd like to add more drivers for the bcm283[567] too, and this
question goes to Andrew Baumann mostly. I've seen implemented those
missing drivers in his repo, which aren't in the qemu mainline yet. I
don't want to reinvent the wheel, so I'm willing to fix those classes
to get them right, but I'd like to know what's wrong with them in the
first place. I'm interested in fixing the TYPE_BCM2835_POWER and
TYPE_BCM2835_ST drivers, which I know many hoppy OS developers lack
and would improve the raspi emulation experience remarkably. It would
be still a long way to boot a raspbian image, but still, a small step
towards that goal at least.

Thanks,
bzt

On 2/26/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Tue, 26 Feb 2019 at 11:38, bzt <bztemail@gmail.com> wrote:
>>
>> Dear qemu developers,
>>
>> Honestly SubmitAPatch is a bit complicated for me, so I'm hoping I've
>> done everything right. To be sure, you can find my patch here:
>> https://github.com/bztsrc/qemu-local-timer and diff against the latest
>> github repo here:
>> https://github.com/bztsrc/qemu-local-timer/blob/patches/3.1.50.diff
>
> Thanks for the patch. You've got the most important things
> right -- patch sent as an email, and with a Signed-off-by:
> line from you. I've put this on my to-review queue, and I
> should be able to get to it within a week or so.
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-02-27 11:54   ` bzt
@ 2019-02-27 18:30     ` Andrew Baumann
  2019-02-28 14:12       ` bzt
  0 siblings, 1 reply; 16+ messages in thread
From: Andrew Baumann @ 2019-02-27 18:30 UTC (permalink / raw)
  To: bzt, Peter Maydell; +Cc: Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

> From: bzt <bztemail@gmail.com>
> Sent: Wednesday, 27 February 2019 03:54
> 
> I'd like to add more drivers for the bcm283[567] too, and this question goes to
> Andrew Baumann mostly. I've seen implemented those missing drivers in his
> repo, which aren't in the qemu mainline yet. I don't want to reinvent the wheel,
> so I'm willing to fix those classes to get them right, but I'd like to know what's
> wrong with them in the first place.

Nothing's wrong with them per se, but when I was working on upstreaming the raspi board I prioritised the device support needed to boot Linux and Windows on raspi2. The remaining devices were always planned for eventual inclusion, but just fell off the end of my TODO list. They never went through code review on qemu-devel, so would probably need some work to bring them up to modern qemu APIs and standards.

> I'm interested in fixing the
> TYPE_BCM2835_POWER and TYPE_BCM2835_ST drivers, which I know many
> hoppy OS developers lack and would improve the raspi emulation experience
> remarkably.

Sounds good to me! 

> It would be still a long way to boot a raspbian image, but still, a
> small step towards that goal at least.

That's interesting. Raspbian did seem to be working (on pi2) when I last worked on this. Perhaps it now depends on these devices, but didn't before?

Cheers,
Andrew

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-02-27 18:30     ` Andrew Baumann
@ 2019-02-28 14:12       ` bzt
  0 siblings, 0 replies; 16+ messages in thread
From: bzt @ 2019-02-28 14:12 UTC (permalink / raw)
  To: Andrew Baumann
  Cc: Peter Maydell, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi Andrew,

That's great news, I'll then bring those drivers up to the modern qemu API!

Maybe that's a Linux kernel module configuration issue as well, but
with the BCM System Timer small delays depend on polling the counter.
Without the qemu support that counter register remains zero, causing
an endless loop. TBH it was years ago when I last checked raspbian,
but I know for sure that many bare metal applications and libraries
(like https://github.com/rsta2/circle) fails to boot because of this,
and the community is very excited to have the BCM ST emulation in
qemu. The PM thing is mostly for me, I'd like to reboot and shut down
the vm gracefully from the guest without the semihosting hack :-)

Thank you again, I'll get to work!

Best wishes,
bzt


On 2/27/19, Andrew Baumann <Andrew.Baumann@microsoft.com> wrote:
>> From: bzt <bztemail@gmail.com>
>> Sent: Wednesday, 27 February 2019 03:54
>>
>> I'd like to add more drivers for the bcm283[567] too, and this question
>> goes to
>> Andrew Baumann mostly. I've seen implemented those missing drivers in his
>> repo, which aren't in the qemu mainline yet. I don't want to reinvent the
>> wheel,
>> so I'm willing to fix those classes to get them right, but I'd like to
>> know what's
>> wrong with them in the first place.
>
> Nothing's wrong with them per se, but when I was working on upstreaming the
> raspi board I prioritised the device support needed to boot Linux and
> Windows on raspi2. The remaining devices were always planned for eventual
> inclusion, but just fell off the end of my TODO list. They never went
> through code review on qemu-devel, so would probably need some work to bring
> them up to modern qemu APIs and standards.
>
>> I'm interested in fixing the
>> TYPE_BCM2835_POWER and TYPE_BCM2835_ST drivers, which I know many
>> hoppy OS developers lack and would improve the raspi emulation experience
>> remarkably.
>
> Sounds good to me!
>
>> It would be still a long way to boot a raspbian image, but still, a
>> small step towards that goal at least.
>
> That's interesting. Raspbian did seem to be working (on pi2) when I last
> worked on this. Perhaps it now depends on these devices, but didn't before?
>
> Cheers,
> Andrew
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-02-26 11:38 [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer bzt
  2019-02-26 12:25 ` Peter Maydell
@ 2019-03-04 15:55 ` Peter Maydell
  2019-03-04 19:27   ` bzt
  1 sibling, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-03-04 15:55 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Tue, 26 Feb 2019 at 11:38, bzt <bztemail@gmail.com> wrote:
>
> Dear qemu developers,
>
> Honestly SubmitAPatch is a bit complicated for me, so I'm hoping I've
> done everything right. To be sure, you can find my patch here:
> https://github.com/bztsrc/qemu-local-timer and diff against the latest
> github repo here:
> https://github.com/bztsrc/qemu-local-timer/blob/patches/3.1.50.diff
>
> Currently the IRQ_TIMER in bcm2836_control is defined, but not
> implemented. This patch adds the basic functionality to qemu by
> implementing 3 new registers in bcm2836_control. You can route the
> interrupt to one of the cores' IRQ or FIQ line by writing 0x40000024,
> and you can set up a periodic interrupt with 38.4MHz frequency by
> writing the divider into 0x40000034. Prescaler are not taken into
> account. When the IRQ fired, you'll see it in 0x40000040+4*N bit 11
> with the rest of the local IRQs, and you can acknowledge it by writing
> 1<<31 to 0x40000038.
>
> The patch is pretty simple, should be easy to review: it does not
> create new classes, does not delete anything, does not change class
> interface, and only two files modified, the bcm2836_control.c and it's
> header.
>
> Sign-off-by: Zoltán Baldaszti <bztemail@gmail.com>
> Subject: [PATCH] Added periodic IRQ support for bcm2836_control local timer

OK, here are my substantive review comments.

> diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
> index cfa5bc7365..fbd31de0f1 100644
> --- a/hw/intc/bcm2836_control.c
> +++ b/hw/intc/bcm2836_control.c
> @@ -7,7 +7,11 @@
>   * This code is licensed under the GNU GPLv2 and later.
>   *
>   * At present, only implements interrupt routing, and mailboxes (i.e.,
> - * not local timer, PMU interrupt, or AXI counters).
> + * not PMU interrupt, or AXI counters).
> + *
> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
> + * The IRQ_TIMER support is still very basic, does not handle timer access,
> + * and such there's no point in enabling it without the interrupt flag set.

Can you be more precise about what's missing here? I didn't
see anything in the spec that looked like a register for
reading the current timer value (though it would certainly
be usual to provide one).

>   *
>   * Ref:
>   * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
> @@ -18,6 +22,9 @@
>  #include "qemu/log.h"
>
>  #define REG_GPU_ROUTE           0x0c
> +#define REG_LOCALTIMERROUTING   0x24
> +#define REG_LOCALTIMERCONTROL   0x34
> +#define REG_LOCALTIMERACK       0x38
>  #define REG_TIMERCONTROL        0x40
>  #define REG_MBOXCONTROL         0x50
>  #define REG_IRQSRC              0x60
> @@ -43,6 +50,13 @@
>  #define IRQ_TIMER       11
>  #define IRQ_MAX         IRQ_TIMER
>
> +#define LOCALTIMER_FREQ      38400000
> +#define LOCALTIMER_INTFLAG   (1 << 31)
> +#define LOCALTIMER_RELOAD    (1 << 30)
> +#define LOCALTIMER_INTENABLE (1 << 29)
> +#define LOCALTIMER_ENABLE    (1 << 28)
> +#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
> +
>  static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
>                            uint32_t controlreg, uint8_t controlidx)
>  {
> @@ -78,6 +92,15 @@ static void bcm2836_control_update(BCM2836ControlState *s)
>          s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
>      }
>
> +    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
> +    if (s->triggered) {
> +        if (s->route_localtimer & 4) {
> +            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
> +        } else {
> +            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
> +        }
> +    }
> +
>      for (i = 0; i < BCM2836_NCORES; i++) {
>          /* handle local timer interrupts for this core */
>          if (s->timerirqs[i]) {
> @@ -162,6 +185,62 @@ static void bcm2836_control_set_gpu_fiq(void
> *opaque, int irq, int level)
>      bcm2836_control_update(s);
>  }
>
> +static void bcm2836_control_local_timer_set_next(void *opaque)
> +{
> +    BCM2836ControlState *s = opaque;
> +    uint64_t next_event;
> +
> +    assert(s->period > 0);
> +
> +    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> +        (NANOSECONDS_PER_SECOND * s->period / LOCALTIMER_FREQ);

This sort of X * Y / Z calculation for timers should
be done with muldiv64() which uses a larger intermediate
result to avoid overflow problems:

       next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
          muldiv64(s->period, NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);

> +    timer_mod(s->timer, next_event);
> +}
> +
> +static void bcm2836_control_local_timer_tick(void *opaque)
> +{
> +    BCM2836ControlState *s = opaque;
> +
> +    bcm2836_control_local_timer_set_next(s);
> +
> +    if (!s->triggered) {
> +        s->triggered = 1;
> +        bcm2836_control_update(s);
> +    }
> +}
> +
> +static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
> +{
> +    BCM2836ControlState *s = opaque;
> +
> +    s->period = LOCALTIMER_VALUE(val);
> +    if (val & LOCALTIMER_INTENABLE) {
> +        if (!s->timer) {
> +            s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
> +                bcm2836_control_local_timer_tick, s);

You should just create the timer once, at device initialization,
not every time the guest enables it. Enabling the timer
can be done with timer_mod().

> +        }
> +        bcm2836_control_local_timer_set_next(s);
> +    } else {
> +        if (s->timer) {
> +            timer_del(s->timer);
> +            s->timer = NULL;

This leaks the timer, because timer_del() is just "turn
off the timer", not "deinitialize and free the timer memory".
You just want to use timer_del() here, and leave s->timer
set so that when it's enabled you can use timer_mod() to
restart it.

> +        }
> +        s->triggered = 0;
> +    }
> +}
> +
> +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
> +{
> +    BCM2836ControlState *s = opaque;
> +
> +    if (val & LOCALTIMER_INTFLAG) {
> +        s->triggered = 0;
> +    }
> +    if (val & LOCALTIMER_RELOAD) {
> +        bcm2836_control_local_timer_set_next(s);
> +    }
> +}
> +
>  static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
> unsigned size)
>  {
>      BCM2836ControlState *s = opaque;
> @@ -170,6 +249,14 @@ static uint64_t bcm2836_control_read(void
> *opaque, hwaddr offset, unsigned size)
>          assert(s->route_gpu_fiq < BCM2836_NCORES
>                 && s->route_gpu_irq < BCM2836_NCORES);
>          return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
> +    } else if (offset == REG_LOCALTIMERROUTING) {
> +        return s->route_localtimer;
> +    } else if (offset == REG_LOCALTIMERCONTROL) {
> +        return s->period |
> +            (s->timer ? LOCALTIMER_ENABLE | LOCALTIMER_INTENABLE : 0) |

This looks odd - the timer enable and interrupt enable bits
should be independent, not always either both set or both cleared.

I suspect this register will be simpler to model if you
have the state struct have a
  uint32_t local_timer_control;
which just stores the whole register value (so no
separate s->triggered and s->period -- just pull
the fields out of the s->local_timer_control with
extract32() or masking when you need them).

> +            (s->triggered ? LOCALTIMER_INTFLAG : 0);
> +    } else if (offset == REG_LOCALTIMERACK) {
> +        return 0;
>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
>          return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
> @@ -195,6 +282,12 @@ static void bcm2836_control_write(void *opaque,
> hwaddr offset,
>      if (offset == REG_GPU_ROUTE) {
>          s->route_gpu_irq = val & 0x3;
>          s->route_gpu_fiq = (val >> 2) & 0x3;
> +    } else if (offset == REG_LOCALTIMERROUTING) {
> +        s->route_localtimer = val & 7;
> +    } else if (offset == REG_LOCALTIMERCONTROL) {
> +        bcm2836_control_local_timer_control(s, val);
> +    } else if (offset == REG_LOCALTIMERACK) {
> +        bcm2836_control_local_timer_ack(s, val);
>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
>          s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
> @@ -227,6 +320,13 @@ static void bcm2836_control_reset(DeviceState *d)
>
>      s->route_gpu_irq = s->route_gpu_fiq = 0;
>
> +    s->route_localtimer = s->triggered = 0;
> +    s->period = 0;
> +    if(s->timer) {
> +        timer_del(s->timer);
> +        s->timer = NULL;

As above, this shouldn't be NULLing out s->timer; you can just
unconditionally call timer_del().

> +    }
> +
>      for (i = 0; i < BCM2836_NCORES; i++) {
>          s->timercontrol[i] = 0;
>          s->mailboxcontrol[i] = 0;
> diff --git a/include/hw/intc/bcm2836_control.h
> b/include/hw/intc/bcm2836_control.h
> index 613f3c4186..873bd52253 100644
> --- a/include/hw/intc/bcm2836_control.h
> +++ b/include/hw/intc/bcm2836_control.h
> @@ -5,6 +5,9 @@
>   * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
>   * Written by Andrew Baumann
>   *
> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
> + * Added basic IRQ_TIMER interrupt support
> + *
>   * This code is licensed under the GNU GPLv2 and later.
>   */
>
> @@ -12,6 +15,7 @@
>  #define BCM2836_CONTROL_H
>
>  #include "hw/sysbus.h"
> +#include "qemu/timer.h"
>
>  /* 4 mailboxes per core, for 16 total */
>  #define BCM2836_NCORES 4
> @@ -39,6 +43,12 @@ typedef struct BCM2836ControlState {
>      bool gpu_irq, gpu_fiq;
>      uint8_t timerirqs[BCM2836_NCORES];
>
> +    /* local timer */
> +    uint8_t route_localtimer;
> +    uint32_t period;
> +    bool triggered;
> +    QEMUTimer *timer;

Slightly nicer to just embed the QEMUTimer struct (and
then initialize it with timer_init_ns() rather than using
timer_new_ns(), which does 'allocate and init'.)

> +
>      /* interrupt source registers, post-routing (also input-derived;
> visible) */
>      uint32_t irqsrc[BCM2836_NCORES];
>      uint32_t fiqsrc[BCM2836_NCORES];

New fields in this struct require new entries in the
vmstate_bcm2836_control struct so the data is migrated.
In this case we don't care about maintaining migration
compatibility between QEMU versions, so the easiest thing
is to bump the .version_id field to 2, and use
VMSTATE_*_V(..., 2) macros to mark the new fields as
being only present in version 2.

If you take my suggestion about using a single uint32_t
local_timer_control and an embedded QEMUTimer struct,
that works out at
   VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
   VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
   VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),

thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-04 15:55 ` Peter Maydell
@ 2019-03-04 19:27   ` bzt
  2019-03-04 20:40     ` bzt
  2019-03-05 16:37     ` Peter Maydell
  0 siblings, 2 replies; 16+ messages in thread
From: bzt @ 2019-03-04 19:27 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi,

Thanks for the review! Most of it makes sense, and I'll modify the
patch accordingly. There are few things though, mostly related to
emulating this unique timer.

On 3/4/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> OK, here are my substantive review comments.
>
>> diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
>> index cfa5bc7365..fbd31de0f1 100644
>> --- a/hw/intc/bcm2836_control.c
>> +++ b/hw/intc/bcm2836_control.c
>> @@ -7,7 +7,11 @@
>>   * This code is licensed under the GNU GPLv2 and later.
>>   *
>>   * At present, only implements interrupt routing, and mailboxes (i.e.,
>> - * not local timer, PMU interrupt, or AXI counters).
>> + * not PMU interrupt, or AXI counters).
>> + *
>> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
>> + * The IRQ_TIMER support is still very basic, does not handle timer
>> access,
>> + * and such there's no point in enabling it without the interrupt flag
>> set.
>
> Can you be more precise about what's missing here? I didn't

The local timer (as per the referenced QA7 doc calls it, section
4.11), means 3 registers in the ARM Control MMIO, at addresses 0x24,
0x34, 0x38. Please note that I haven't named IRQ_TIMER bit in the
irqsrc and fiqsrc fields, that was already defined. This patch
implements that one.

> see anything in the spec that looked like a register for
> reading the current timer value (though it would certainly
> be usual to provide one).

Those are registers 0x1C and 0x20, called "Core timer access LS/MS",
section 4.3. I'll make the comment more clear. Those are not local
timer IRQ related, because they could use a different frequency than
the IRQ, just happen to be enabled with a bit in the local timer's
control register (and not in the core timer control register at 0x0),
and on real hardware triggering an IRQ requires both the timer enable
and interrupt enable bits to be set. The timer counter is a 64 bits
one, while the IRQ's counter is only 28 bit wide, which cannot be
directly read, does not use the prescaler, and it's reload value
stored in the local timer control register itself (unusal, but that's
how it is).

As I've said, this patch only provides the IRQ, and not the timer
part. That's a whole different story, specially beacuse of the
counters (calculating correct value for the selected clocksource
(crystal/APB), using divisor and prescaler value, the 32 bit register
locking mechanism etc.), that's for another patch.

On a real hardware the interrupt has to be implemented using the
timer. On qemu, we don't need to emulate the whole timer circuit and
counter, we can use QEMUTimer to calculate when to trigger a periodic
IRQ (which is always at fixed 38.4Mhz not using any divisor or
prescaler). But I think regadless we should emulate the control
register in that timer enable bit should be set when we generate local
timer IRQs. I hope this makes sense to you.

>
>>   *
>>   * Ref:
>>   *
>> https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
>> @@ -18,6 +22,9 @@
>>  #include "qemu/log.h"
>>
>>  #define REG_GPU_ROUTE           0x0c
>> +#define REG_LOCALTIMERROUTING   0x24
>> +#define REG_LOCALTIMERCONTROL   0x34
>> +#define REG_LOCALTIMERACK       0x38
>>  #define REG_TIMERCONTROL        0x40
>>  #define REG_MBOXCONTROL         0x50
>>  #define REG_IRQSRC              0x60
>> @@ -43,6 +50,13 @@
>>  #define IRQ_TIMER       11
>>  #define IRQ_MAX         IRQ_TIMER
>>
>> +#define LOCALTIMER_FREQ      38400000
>> +#define LOCALTIMER_INTFLAG   (1 << 31)
>> +#define LOCALTIMER_RELOAD    (1 << 30)
>> +#define LOCALTIMER_INTENABLE (1 << 29)
>> +#define LOCALTIMER_ENABLE    (1 << 28)
>> +#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
>> +
>>  static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t
>> irq,
>>                            uint32_t controlreg, uint8_t controlidx)
>>  {
>> @@ -78,6 +92,15 @@ static void bcm2836_control_update(BCM2836ControlState
>> *s)
>>          s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
>>      }
>>
>> +    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
>> +    if (s->triggered) {
>> +        if (s->route_localtimer & 4) {
>> +            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>> IRQ_TIMER;
>> +        } else {
>> +            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>> IRQ_TIMER;
>> +        }
>> +    }
>> +
>>      for (i = 0; i < BCM2836_NCORES; i++) {
>>          /* handle local timer interrupts for this core */
>>          if (s->timerirqs[i]) {
>> @@ -162,6 +185,62 @@ static void bcm2836_control_set_gpu_fiq(void
>> *opaque, int irq, int level)
>>      bcm2836_control_update(s);
>>  }
>>
>> +static void bcm2836_control_local_timer_set_next(void *opaque)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +    uint64_t next_event;
>> +
>> +    assert(s->period > 0);
>> +
>> +    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>> +        (NANOSECONDS_PER_SECOND * s->period / LOCALTIMER_FREQ);
>
> This sort of X * Y / Z calculation for timers should
> be done with muldiv64() which uses a larger intermediate
> result to avoid overflow problems:
>
>        next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>           muldiv64(s->period, NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
>

OK!

>> +    timer_mod(s->timer, next_event);
>> +}
>> +
>> +static void bcm2836_control_local_timer_tick(void *opaque)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +
>> +    bcm2836_control_local_timer_set_next(s);
>> +
>> +    if (!s->triggered) {
>> +        s->triggered = 1;
>> +        bcm2836_control_update(s);
>> +    }
>> +}
>> +
>> +static void bcm2836_control_local_timer_control(void *opaque, uint32_t
>> val)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +
>> +    s->period = LOCALTIMER_VALUE(val);
>> +    if (val & LOCALTIMER_INTENABLE) {
>> +        if (!s->timer) {
>> +            s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
>> +                bcm2836_control_local_timer_tick, s);
>
> You should just create the timer once, at device initialization,
> not every time the guest enables it. Enabling the timer
> can be done with timer_mod().

OK!

>
>> +        }
>> +        bcm2836_control_local_timer_set_next(s);
>> +    } else {
>> +        if (s->timer) {
>> +            timer_del(s->timer);
>> +            s->timer = NULL;
>
> This leaks the timer, because timer_del() is just "turn
> off the timer", not "deinitialize and free the timer memory".
> You just want to use timer_del() here, and leave s->timer
> set so that when it's enabled you can use timer_mod() to
> restart it.

OK!

>
>> +        }
>> +        s->triggered = 0;
>> +    }
>> +}
>> +
>> +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +
>> +    if (val & LOCALTIMER_INTFLAG) {
>> +        s->triggered = 0;
>> +    }
>> +    if (val & LOCALTIMER_RELOAD) {
>> +        bcm2836_control_local_timer_set_next(s);
>> +    }
>> +}
>> +
>>  static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
>> unsigned size)
>>  {
>>      BCM2836ControlState *s = opaque;
>> @@ -170,6 +249,14 @@ static uint64_t bcm2836_control_read(void
>> *opaque, hwaddr offset, unsigned size)
>>          assert(s->route_gpu_fiq < BCM2836_NCORES
>>                 && s->route_gpu_irq < BCM2836_NCORES);
>>          return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
>> +    } else if (offset == REG_LOCALTIMERROUTING) {
>> +        return s->route_localtimer;
>> +    } else if (offset == REG_LOCALTIMERCONTROL) {
>> +        return s->period |
>> +            (s->timer ? LOCALTIMER_ENABLE | LOCALTIMER_INTENABLE : 0) |
>
> This looks odd - the timer enable and interrupt enable bits
> should be independent, not always either both set or both cleared.

That's emulation of the real hardware, see my comment above. On a real
hardware you can't enable interrupts without the timer enabled.

>
> I suspect this register will be simpler to model if you
> have the state struct have a
>   uint32_t local_timer_control;
> which just stores the whole register value (so no
> separate s->triggered and s->period -- just pull
> the fields out of the s->local_timer_control with
> extract32() or masking when you need them).

OK!

>
>> +            (s->triggered ? LOCALTIMER_INTFLAG : 0);
>> +    } else if (offset == REG_LOCALTIMERACK) {
>> +        return 0;
>>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
>>          return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
>>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
>> @@ -195,6 +282,12 @@ static void bcm2836_control_write(void *opaque,
>> hwaddr offset,
>>      if (offset == REG_GPU_ROUTE) {
>>          s->route_gpu_irq = val & 0x3;
>>          s->route_gpu_fiq = (val >> 2) & 0x3;
>> +    } else if (offset == REG_LOCALTIMERROUTING) {
>> +        s->route_localtimer = val & 7;
>> +    } else if (offset == REG_LOCALTIMERCONTROL) {
>> +        bcm2836_control_local_timer_control(s, val);
>> +    } else if (offset == REG_LOCALTIMERACK) {
>> +        bcm2836_control_local_timer_ack(s, val);
>>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
>>          s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
>>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
>> @@ -227,6 +320,13 @@ static void bcm2836_control_reset(DeviceState *d)
>>
>>      s->route_gpu_irq = s->route_gpu_fiq = 0;
>>
>> +    s->route_localtimer = s->triggered = 0;
>> +    s->period = 0;
>> +    if(s->timer) {
>> +        timer_del(s->timer);
>> +        s->timer = NULL;
>
> As above, this shouldn't be NULLing out s->timer; you can just
> unconditionally call timer_del().

OK!

>
>> +    }
>> +
>>      for (i = 0; i < BCM2836_NCORES; i++) {
>>          s->timercontrol[i] = 0;
>>          s->mailboxcontrol[i] = 0;
>> diff --git a/include/hw/intc/bcm2836_control.h
>> b/include/hw/intc/bcm2836_control.h
>> index 613f3c4186..873bd52253 100644
>> --- a/include/hw/intc/bcm2836_control.h
>> +++ b/include/hw/intc/bcm2836_control.h
>> @@ -5,6 +5,9 @@
>>   * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
>>   * Written by Andrew Baumann
>>   *
>> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
>> + * Added basic IRQ_TIMER interrupt support
>> + *
>>   * This code is licensed under the GNU GPLv2 and later.
>>   */
>>
>> @@ -12,6 +15,7 @@
>>  #define BCM2836_CONTROL_H
>>
>>  #include "hw/sysbus.h"
>> +#include "qemu/timer.h"
>>
>>  /* 4 mailboxes per core, for 16 total */
>>  #define BCM2836_NCORES 4
>> @@ -39,6 +43,12 @@ typedef struct BCM2836ControlState {
>>      bool gpu_irq, gpu_fiq;
>>      uint8_t timerirqs[BCM2836_NCORES];
>>
>> +    /* local timer */
>> +    uint8_t route_localtimer;
>> +    uint32_t period;
>> +    bool triggered;
>> +    QEMUTimer *timer;
>
> Slightly nicer to just embed the QEMUTimer struct (and
> then initialize it with timer_init_ns() rather than using
> timer_new_ns(), which does 'allocate and init'.)

What do you mean by embed? Statically include the QEMUTimer struct?
That would require more memory even if a guest never turns the local
timer IRQ on. Is that acceptable? Otherwise OK!

>
>> +
>>      /* interrupt source registers, post-routing (also input-derived;
>> visible) */
>>      uint32_t irqsrc[BCM2836_NCORES];
>>      uint32_t fiqsrc[BCM2836_NCORES];
>
> New fields in this struct require new entries in the
> vmstate_bcm2836_control struct so the data is migrated.
> In this case we don't care about maintaining migration
> compatibility between QEMU versions, so the easiest thing
> is to bump the .version_id field to 2, and use
> VMSTATE_*_V(..., 2) macros to mark the new fields as
> being only present in version 2.
>
> If you take my suggestion about using a single uint32_t
> local_timer_control and an embedded QEMUTimer struct,
> that works out at
>    VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
>    VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
>    VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),

OK!

>
> thanks
> -- PMM
>

Thanks for the review again, I'll modify the patch accordingly.

bzt

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-04 19:27   ` bzt
@ 2019-03-04 20:40     ` bzt
  2019-03-05 16:37     ` Peter Maydell
  1 sibling, 0 replies; 16+ messages in thread
From: bzt @ 2019-03-04 20:40 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi Peter,

Here's the modified patch. I've changed the comment, I hope now it
makes clear that dispite this patch handles the timer enable bit
(which is required for the interrupt), it only adds the periodic IRQ
feature, and not the full timer functionality.

Otherwise I've modified everything you asked for.
bzt

Sign-off-by: Zoltán Baldaszti <bztemail@gmail.com>
Subject: [PATCH] Added periodic IRQ support for bcm2836_control local timer
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
index cfa5bc7365..2157099b31 100644
--- a/hw/intc/bcm2836_control.c
+++ b/hw/intc/bcm2836_control.c
@@ -7,7 +7,13 @@
  * This code is licensed under the GNU GPLv2 and later.
  *
  * At present, only implements interrupt routing, and mailboxes (i.e.,
- * not local timer, PMU interrupt, or AXI counters).
+ * not PMU interrupt, or AXI counters).
+ *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * The IRQ_TIMER support is still very basic, does not provide timer counter
+ * access and other timer features, it just generates periodic IRQs. But it
+ * still requires not only the interrupt enable, but the timer enable bit to
+ * be set.
  *
  * Ref:
  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
@@ -18,6 +24,9 @@
 #include "qemu/log.h"

 #define REG_GPU_ROUTE           0x0c
+#define REG_LOCALTIMERROUTING   0x24
+#define REG_LOCALTIMERCONTROL   0x34
+#define REG_LOCALTIMERACK       0x38
 #define REG_TIMERCONTROL        0x40
 #define REG_MBOXCONTROL         0x50
 #define REG_IRQSRC              0x60
@@ -43,6 +52,13 @@
 #define IRQ_TIMER       11
 #define IRQ_MAX         IRQ_TIMER

+#define LOCALTIMER_FREQ      38400000
+#define LOCALTIMER_INTFLAG   (1 << 31)
+#define LOCALTIMER_RELOAD    (1 << 30)
+#define LOCALTIMER_INTENABLE (1 << 29)
+#define LOCALTIMER_ENABLE    (1 << 28)
+#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
+
 static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
                           uint32_t controlreg, uint8_t controlidx)
 {
@@ -78,6 +94,15 @@ static void bcm2836_control_update(BCM2836ControlState *s)
         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
     }

+    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
+    if (s->local_timer_control & LOCALTIMER_INTFLAG) {
+        if (s->route_localtimer & 4) {
+            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        } else {
+            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        }
+    }
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         /* handle local timer interrupts for this core */
         if (s->timerirqs[i]) {
@@ -162,6 +187,58 @@ static void bcm2836_control_set_gpu_fiq(void
*opaque, int irq, int level)
     bcm2836_control_update(s);
 }

+static void bcm2836_control_local_timer_set_next(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+    uint64_t next_event;
+
+    assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
+
+    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
+            NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
+    timer_mod(&s->timer, next_event);
+}
+
+static void bcm2836_control_local_timer_tick(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+
+    bcm2836_control_local_timer_set_next(s);
+
+    if (!(s->local_timer_control & LOCALTIMER_INTFLAG)) {
+        s->local_timer_control |= LOCALTIMER_INTFLAG;
+        bcm2836_control_update(s);
+    }
+}
+
+static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    s->local_timer_control = val;
+    if ((val & LOCALTIMER_ENABLE) && (val & LOCALTIMER_INTENABLE)) {
+        bcm2836_control_local_timer_set_next(s);
+    } else {
+        timer_del(&s->timer);
+        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
+    }
+}
+
+static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    if (val & LOCALTIMER_INTFLAG) {
+        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
+    }
+    if ((val & LOCALTIMER_RELOAD) &&
+        (s->local_timer_control & LOCALTIMER_ENABLE) &&
+        (s->local_timer_control & LOCALTIMER_INTENABLE)) {
+            bcm2836_control_local_timer_set_next(s);
+    }
+}
+
 static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
unsigned size)
 {
     BCM2836ControlState *s = opaque;
@@ -170,6 +247,12 @@ static uint64_t bcm2836_control_read(void
*opaque, hwaddr offset, unsigned size)
         assert(s->route_gpu_fiq < BCM2836_NCORES
                && s->route_gpu_irq < BCM2836_NCORES);
         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        return s->route_localtimer;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        return s->local_timer_control;
+    } else if (offset == REG_LOCALTIMERACK) {
+        return 0;
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -195,6 +278,12 @@ static void bcm2836_control_write(void *opaque,
hwaddr offset,
     if (offset == REG_GPU_ROUTE) {
         s->route_gpu_irq = val & 0x3;
         s->route_gpu_fiq = (val >> 2) & 0x3;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        s->route_localtimer = val & 7;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        bcm2836_control_local_timer_control(s, val);
+    } else if (offset == REG_LOCALTIMERACK) {
+        bcm2836_control_local_timer_ack(s, val);
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -227,6 +316,10 @@ static void bcm2836_control_reset(DeviceState *d)

     s->route_gpu_irq = s->route_gpu_fiq = 0;

+    timer_del(&s->timer);
+    s->route_localtimer = 0;
+    s->local_timer_control = 0;
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         s->timercontrol[i] = 0;
         s->mailboxcontrol[i] = 0;
@@ -263,11 +356,14 @@ static void bcm2836_control_init(Object *obj)
     /* outputs to CPU cores */
     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+
+    /* create a qemu virtual timer */
+    timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
bcm2836_control_local_timer_tick, s);
 }

 static const VMStateDescription vmstate_bcm2836_control = {
     .name = TYPE_BCM2836_CONTROL,
-    .version_id = 1,
+    .version_id = 2,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
@@ -277,6 +373,9 @@ static const VMStateDescription vmstate_bcm2836_control = {
         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState,
BCM2836_NCORES),
         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
                              BCM2836_NCORES),
+        VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
+        VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
+        VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/include/hw/intc/bcm2836_control.h
b/include/hw/intc/bcm2836_control.h
index 613f3c4186..ed4690523d 100644
--- a/include/hw/intc/bcm2836_control.h
+++ b/include/hw/intc/bcm2836_control.h
@@ -5,6 +5,9 @@
  * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
  * Written by Andrew Baumann
  *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * Added basic IRQ_TIMER interrupt support
+ *
  * This code is licensed under the GNU GPLv2 and later.
  */

@@ -12,6 +15,7 @@
 #define BCM2836_CONTROL_H

 #include "hw/sysbus.h"
+#include "qemu/timer.h"

 /* 4 mailboxes per core, for 16 total */
 #define BCM2836_NCORES 4
@@ -39,6 +43,11 @@ typedef struct BCM2836ControlState {
     bool gpu_irq, gpu_fiq;
     uint8_t timerirqs[BCM2836_NCORES];

+    /* local timer */
+    QEMUTimer timer;
+    uint32_t local_timer_control;
+    uint8_t route_localtimer;
+
     /* interrupt source registers, post-routing (also input-derived;
visible) */
     uint32_t irqsrc[BCM2836_NCORES];
     uint32_t fiqsrc[BCM2836_NCORES];


On 3/4/19, bzt <bztemail@gmail.com> wrote:
> Hi,
>
> Thanks for the review! Most of it makes sense, and I'll modify the
> patch accordingly. There are few things though, mostly related to
> emulating this unique timer.
>
> On 3/4/19, Peter Maydell <peter.maydell@linaro.org> wrote:
>> OK, here are my substantive review comments.
>>
>>> diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
>>> index cfa5bc7365..fbd31de0f1 100644
>>> --- a/hw/intc/bcm2836_control.c
>>> +++ b/hw/intc/bcm2836_control.c
>>> @@ -7,7 +7,11 @@
>>>   * This code is licensed under the GNU GPLv2 and later.
>>>   *
>>>   * At present, only implements interrupt routing, and mailboxes (i.e.,
>>> - * not local timer, PMU interrupt, or AXI counters).
>>> + * not PMU interrupt, or AXI counters).
>>> + *
>>> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
>>> + * The IRQ_TIMER support is still very basic, does not handle timer
>>> access,
>>> + * and such there's no point in enabling it without the interrupt flag
>>> set.
>>
>> Can you be more precise about what's missing here? I didn't
>
> The local timer (as per the referenced QA7 doc calls it, section
> 4.11), means 3 registers in the ARM Control MMIO, at addresses 0x24,
> 0x34, 0x38. Please note that I haven't named IRQ_TIMER bit in the
> irqsrc and fiqsrc fields, that was already defined. This patch
> implements that one.
>
>> see anything in the spec that looked like a register for
>> reading the current timer value (though it would certainly
>> be usual to provide one).
>
> Those are registers 0x1C and 0x20, called "Core timer access LS/MS",
> section 4.3. I'll make the comment more clear. Those are not local
> timer IRQ related, because they could use a different frequency than
> the IRQ, just happen to be enabled with a bit in the local timer's
> control register (and not in the core timer control register at 0x0),
> and on real hardware triggering an IRQ requires both the timer enable
> and interrupt enable bits to be set. The timer counter is a 64 bits
> one, while the IRQ's counter is only 28 bit wide, which cannot be
> directly read, does not use the prescaler, and it's reload value
> stored in the local timer control register itself (unusal, but that's
> how it is).
>
> As I've said, this patch only provides the IRQ, and not the timer
> part. That's a whole different story, specially beacuse of the
> counters (calculating correct value for the selected clocksource
> (crystal/APB), using divisor and prescaler value, the 32 bit register
> locking mechanism etc.), that's for another patch.
>
> On a real hardware the interrupt has to be implemented using the
> timer. On qemu, we don't need to emulate the whole timer circuit and
> counter, we can use QEMUTimer to calculate when to trigger a periodic
> IRQ (which is always at fixed 38.4Mhz not using any divisor or
> prescaler). But I think regadless we should emulate the control
> register in that timer enable bit should be set when we generate local
> timer IRQs. I hope this makes sense to you.
>
>>
>>>   *
>>>   * Ref:
>>>   *
>>> https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
>>> @@ -18,6 +22,9 @@
>>>  #include "qemu/log.h"
>>>
>>>  #define REG_GPU_ROUTE           0x0c
>>> +#define REG_LOCALTIMERROUTING   0x24
>>> +#define REG_LOCALTIMERCONTROL   0x34
>>> +#define REG_LOCALTIMERACK       0x38
>>>  #define REG_TIMERCONTROL        0x40
>>>  #define REG_MBOXCONTROL         0x50
>>>  #define REG_IRQSRC              0x60
>>> @@ -43,6 +50,13 @@
>>>  #define IRQ_TIMER       11
>>>  #define IRQ_MAX         IRQ_TIMER
>>>
>>> +#define LOCALTIMER_FREQ      38400000
>>> +#define LOCALTIMER_INTFLAG   (1 << 31)
>>> +#define LOCALTIMER_RELOAD    (1 << 30)
>>> +#define LOCALTIMER_INTENABLE (1 << 29)
>>> +#define LOCALTIMER_ENABLE    (1 << 28)
>>> +#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
>>> +
>>>  static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t
>>> irq,
>>>                            uint32_t controlreg, uint8_t controlidx)
>>>  {
>>> @@ -78,6 +92,15 @@ static void
>>> bcm2836_control_update(BCM2836ControlState
>>> *s)
>>>          s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
>>>      }
>>>
>>> +    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ
>>> */
>>> +    if (s->triggered) {
>>> +        if (s->route_localtimer & 4) {
>>> +            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>>> IRQ_TIMER;
>>> +        } else {
>>> +            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>>> IRQ_TIMER;
>>> +        }
>>> +    }
>>> +
>>>      for (i = 0; i < BCM2836_NCORES; i++) {
>>>          /* handle local timer interrupts for this core */
>>>          if (s->timerirqs[i]) {
>>> @@ -162,6 +185,62 @@ static void bcm2836_control_set_gpu_fiq(void
>>> *opaque, int irq, int level)
>>>      bcm2836_control_update(s);
>>>  }
>>>
>>> +static void bcm2836_control_local_timer_set_next(void *opaque)
>>> +{
>>> +    BCM2836ControlState *s = opaque;
>>> +    uint64_t next_event;
>>> +
>>> +    assert(s->period > 0);
>>> +
>>> +    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>>> +        (NANOSECONDS_PER_SECOND * s->period / LOCALTIMER_FREQ);
>>
>> This sort of X * Y / Z calculation for timers should
>> be done with muldiv64() which uses a larger intermediate
>> result to avoid overflow problems:
>>
>>        next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>>           muldiv64(s->period, NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
>>
>
> OK!
>
>>> +    timer_mod(s->timer, next_event);
>>> +}
>>> +
>>> +static void bcm2836_control_local_timer_tick(void *opaque)
>>> +{
>>> +    BCM2836ControlState *s = opaque;
>>> +
>>> +    bcm2836_control_local_timer_set_next(s);
>>> +
>>> +    if (!s->triggered) {
>>> +        s->triggered = 1;
>>> +        bcm2836_control_update(s);
>>> +    }
>>> +}
>>> +
>>> +static void bcm2836_control_local_timer_control(void *opaque, uint32_t
>>> val)
>>> +{
>>> +    BCM2836ControlState *s = opaque;
>>> +
>>> +    s->period = LOCALTIMER_VALUE(val);
>>> +    if (val & LOCALTIMER_INTENABLE) {
>>> +        if (!s->timer) {
>>> +            s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
>>> +                bcm2836_control_local_timer_tick, s);
>>
>> You should just create the timer once, at device initialization,
>> not every time the guest enables it. Enabling the timer
>> can be done with timer_mod().
>
> OK!
>
>>
>>> +        }
>>> +        bcm2836_control_local_timer_set_next(s);
>>> +    } else {
>>> +        if (s->timer) {
>>> +            timer_del(s->timer);
>>> +            s->timer = NULL;
>>
>> This leaks the timer, because timer_del() is just "turn
>> off the timer", not "deinitialize and free the timer memory".
>> You just want to use timer_del() here, and leave s->timer
>> set so that when it's enabled you can use timer_mod() to
>> restart it.
>
> OK!
>
>>
>>> +        }
>>> +        s->triggered = 0;
>>> +    }
>>> +}
>>> +
>>> +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
>>> +{
>>> +    BCM2836ControlState *s = opaque;
>>> +
>>> +    if (val & LOCALTIMER_INTFLAG) {
>>> +        s->triggered = 0;
>>> +    }
>>> +    if (val & LOCALTIMER_RELOAD) {
>>> +        bcm2836_control_local_timer_set_next(s);
>>> +    }
>>> +}
>>> +
>>>  static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
>>> unsigned size)
>>>  {
>>>      BCM2836ControlState *s = opaque;
>>> @@ -170,6 +249,14 @@ static uint64_t bcm2836_control_read(void
>>> *opaque, hwaddr offset, unsigned size)
>>>          assert(s->route_gpu_fiq < BCM2836_NCORES
>>>                 && s->route_gpu_irq < BCM2836_NCORES);
>>>          return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
>>> +    } else if (offset == REG_LOCALTIMERROUTING) {
>>> +        return s->route_localtimer;
>>> +    } else if (offset == REG_LOCALTIMERCONTROL) {
>>> +        return s->period |
>>> +            (s->timer ? LOCALTIMER_ENABLE | LOCALTIMER_INTENABLE : 0) |
>>
>> This looks odd - the timer enable and interrupt enable bits
>> should be independent, not always either both set or both cleared.
>
> That's emulation of the real hardware, see my comment above. On a real
> hardware you can't enable interrupts without the timer enabled.
>
>>
>> I suspect this register will be simpler to model if you
>> have the state struct have a
>>   uint32_t local_timer_control;
>> which just stores the whole register value (so no
>> separate s->triggered and s->period -- just pull
>> the fields out of the s->local_timer_control with
>> extract32() or masking when you need them).
>
> OK!
>
>>
>>> +            (s->triggered ? LOCALTIMER_INTFLAG : 0);
>>> +    } else if (offset == REG_LOCALTIMERACK) {
>>> +        return 0;
>>>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL)
>>> {
>>>          return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
>>>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
>>> @@ -195,6 +282,12 @@ static void bcm2836_control_write(void *opaque,
>>> hwaddr offset,
>>>      if (offset == REG_GPU_ROUTE) {
>>>          s->route_gpu_irq = val & 0x3;
>>>          s->route_gpu_fiq = (val >> 2) & 0x3;
>>> +    } else if (offset == REG_LOCALTIMERROUTING) {
>>> +        s->route_localtimer = val & 7;
>>> +    } else if (offset == REG_LOCALTIMERCONTROL) {
>>> +        bcm2836_control_local_timer_control(s, val);
>>> +    } else if (offset == REG_LOCALTIMERACK) {
>>> +        bcm2836_control_local_timer_ack(s, val);
>>>      } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL)
>>> {
>>>          s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
>>>      } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
>>> @@ -227,6 +320,13 @@ static void bcm2836_control_reset(DeviceState *d)
>>>
>>>      s->route_gpu_irq = s->route_gpu_fiq = 0;
>>>
>>> +    s->route_localtimer = s->triggered = 0;
>>> +    s->period = 0;
>>> +    if(s->timer) {
>>> +        timer_del(s->timer);
>>> +        s->timer = NULL;
>>
>> As above, this shouldn't be NULLing out s->timer; you can just
>> unconditionally call timer_del().
>
> OK!
>
>>
>>> +    }
>>> +
>>>      for (i = 0; i < BCM2836_NCORES; i++) {
>>>          s->timercontrol[i] = 0;
>>>          s->mailboxcontrol[i] = 0;
>>> diff --git a/include/hw/intc/bcm2836_control.h
>>> b/include/hw/intc/bcm2836_control.h
>>> index 613f3c4186..873bd52253 100644
>>> --- a/include/hw/intc/bcm2836_control.h
>>> +++ b/include/hw/intc/bcm2836_control.h
>>> @@ -5,6 +5,9 @@
>>>   * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015,
>>> Microsoft
>>>   * Written by Andrew Baumann
>>>   *
>>> + * 64-bit ARM Local Timer Copyright (c) 2019. Zoltán Baldaszti
>>> + * Added basic IRQ_TIMER interrupt support
>>> + *
>>>   * This code is licensed under the GNU GPLv2 and later.
>>>   */
>>>
>>> @@ -12,6 +15,7 @@
>>>  #define BCM2836_CONTROL_H
>>>
>>>  #include "hw/sysbus.h"
>>> +#include "qemu/timer.h"
>>>
>>>  /* 4 mailboxes per core, for 16 total */
>>>  #define BCM2836_NCORES 4
>>> @@ -39,6 +43,12 @@ typedef struct BCM2836ControlState {
>>>      bool gpu_irq, gpu_fiq;
>>>      uint8_t timerirqs[BCM2836_NCORES];
>>>
>>> +    /* local timer */
>>> +    uint8_t route_localtimer;
>>> +    uint32_t period;
>>> +    bool triggered;
>>> +    QEMUTimer *timer;
>>
>> Slightly nicer to just embed the QEMUTimer struct (and
>> then initialize it with timer_init_ns() rather than using
>> timer_new_ns(), which does 'allocate and init'.)
>
> What do you mean by embed? Statically include the QEMUTimer struct?
> That would require more memory even if a guest never turns the local
> timer IRQ on. Is that acceptable? Otherwise OK!
>
>>
>>> +
>>>      /* interrupt source registers, post-routing (also input-derived;
>>> visible) */
>>>      uint32_t irqsrc[BCM2836_NCORES];
>>>      uint32_t fiqsrc[BCM2836_NCORES];
>>
>> New fields in this struct require new entries in the
>> vmstate_bcm2836_control struct so the data is migrated.
>> In this case we don't care about maintaining migration
>> compatibility between QEMU versions, so the easiest thing
>> is to bump the .version_id field to 2, and use
>> VMSTATE_*_V(..., 2) macros to mark the new fields as
>> being only present in version 2.
>>
>> If you take my suggestion about using a single uint32_t
>> local_timer_control and an embedded QEMUTimer struct,
>> that works out at
>>    VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
>>    VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
>>    VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
>
> OK!
>
>>
>> thanks
>> -- PMM
>>
>
> Thanks for the review again, I'll modify the patch accordingly.
>
> bzt
>

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-04 19:27   ` bzt
  2019-03-04 20:40     ` bzt
@ 2019-03-05 16:37     ` Peter Maydell
  2019-03-07 15:27       ` bzt
  1 sibling, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-03-05 16:37 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Mon, 4 Mar 2019 at 19:27, bzt <bztemail@gmail.com> wrote:
>
> On 3/4/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> >> + * The IRQ_TIMER support is still very basic, does not handle timer
> >> access,
> >> + * and such there's no point in enabling it without the interrupt flag
> >> set.
> >
> > Can you be more precise about what's missing here? I didn't
>
> The local timer (as per the referenced QA7 doc calls it, section
> 4.11), means 3 registers in the ARM Control MMIO, at addresses 0x24,
> 0x34, 0x38. Please note that I haven't named IRQ_TIMER bit in the
> irqsrc and fiqsrc fields, that was already defined. This patch
> implements that one.
>
> > see anything in the spec that looked like a register for
> > reading the current timer value (though it would certainly
> > be usual to provide one).
>
> Those are registers 0x1C and 0x20, called "Core timer access LS/MS",
> section 4.3. I'll make the comment more clear. Those are not local
> timer IRQ related, because they could use a different frequency than
> the IRQ, just happen to be enabled with a bit in the local timer's
> control register (and not in the core timer control register at 0x0),
> and on real hardware triggering an IRQ requires both the timer enable
> and interrupt enable bits to be set. The timer counter is a 64 bits
> one, while the IRQ's counter is only 28 bit wide, which cannot be
> directly read, does not use the prescaler, and it's reload value
> stored in the local timer control register itself (unusal, but that's
> how it is).

OK, so that's just an entirely separate timer whose control bit
happens to be in the same register? I'd assumed that the enable
bit here was so you could have the local timer be running but
not generate interrupts -- in which case when it expired it would
set the "interrupt flag" (and reload), but it wouldn't assert
the external interrupt line.

In fact thinking about it that does seem like the more plausible
reading of that specification:
 * bit 28 (Timer enable) enables the timer
 * when it gets to zero we set bit 31 "Interrupt flag"
 * the outbound interrupt line is the logical OR of
   bits 31 ('interrupt flag') and 29 ('interrupt enable')

Are you sure it doesn't work that way ?

I'd have thought that any enable bit for the "core timer" would
be in the core timer control register at 0x4000_0000. Since the
"core timer" is (apparently) the source of the clock for all the
per-CPU architected generic timers, it seems more likely that
it just runs constantly without an enable bit.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-05 16:37     ` Peter Maydell
@ 2019-03-07 15:27       ` bzt
  2019-03-07 15:43         ` Peter Maydell
  0 siblings, 1 reply; 16+ messages in thread
From: bzt @ 2019-03-07 15:27 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi,

Yes, could be. The QA7 spec is not really detailed, and calling both
timers simply ARM timers can be misleading. But it's not relevant
anyway from the IRQ's point of view. My latest patch checks both bits
to be set to generate the IRQ, so it does not really matter. As I've
said, this patch adds only the periodic IRQ, and not a full timer
emulation.

Do you want any modifications on the patch? If so, what exactly? Let
me know and I'll update it.

Cheers,
bzt

On 3/5/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Mon, 4 Mar 2019 at 19:27, bzt <bztemail@gmail.com> wrote:
>>
>> On 3/4/19, Peter Maydell <peter.maydell@linaro.org> wrote:
>> >> + * The IRQ_TIMER support is still very basic, does not handle timer
>> >> access,
>> >> + * and such there's no point in enabling it without the interrupt
>> >> flag
>> >> set.
>> >
>> > Can you be more precise about what's missing here? I didn't
>>
>> The local timer (as per the referenced QA7 doc calls it, section
>> 4.11), means 3 registers in the ARM Control MMIO, at addresses 0x24,
>> 0x34, 0x38. Please note that I haven't named IRQ_TIMER bit in the
>> irqsrc and fiqsrc fields, that was already defined. This patch
>> implements that one.
>>
>> > see anything in the spec that looked like a register for
>> > reading the current timer value (though it would certainly
>> > be usual to provide one).
>>
>> Those are registers 0x1C and 0x20, called "Core timer access LS/MS",
>> section 4.3. I'll make the comment more clear. Those are not local
>> timer IRQ related, because they could use a different frequency than
>> the IRQ, just happen to be enabled with a bit in the local timer's
>> control register (and not in the core timer control register at 0x0),
>> and on real hardware triggering an IRQ requires both the timer enable
>> and interrupt enable bits to be set. The timer counter is a 64 bits
>> one, while the IRQ's counter is only 28 bit wide, which cannot be
>> directly read, does not use the prescaler, and it's reload value
>> stored in the local timer control register itself (unusal, but that's
>> how it is).
>
> OK, so that's just an entirely separate timer whose control bit
> happens to be in the same register? I'd assumed that the enable
> bit here was so you could have the local timer be running but
> not generate interrupts -- in which case when it expired it would
> set the "interrupt flag" (and reload), but it wouldn't assert
> the external interrupt line.
>
> In fact thinking about it that does seem like the more plausible
> reading of that specification:
>  * bit 28 (Timer enable) enables the timer
>  * when it gets to zero we set bit 31 "Interrupt flag"
>  * the outbound interrupt line is the logical OR of
>    bits 31 ('interrupt flag') and 29 ('interrupt enable')
>
> Are you sure it doesn't work that way ?
>
> I'd have thought that any enable bit for the "core timer" would
> be in the core timer control register at 0x4000_0000. Since the
> "core timer" is (apparently) the source of the clock for all the
> per-CPU architected generic timers, it seems more likely that
> it just runs constantly without an enable bit.
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-07 15:27       ` bzt
@ 2019-03-07 15:43         ` Peter Maydell
  2019-03-07 15:57           ` bzt
  0 siblings, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-03-07 15:43 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Thu, 7 Mar 2019 at 15:27, bzt <bztemail@gmail.com> wrote:
> Yes, could be. The QA7 spec is not really detailed, and calling both
> timers simply ARM timers can be misleading. But it's not relevant
> anyway from the IRQ's point of view. My latest patch checks both bits
> to be set to generate the IRQ, so it does not really matter. As I've
> said, this patch adds only the periodic IRQ, and not a full timer
> emulation.
>
> Do you want any modifications on the patch? If so, what exactly? Let
> me know and I'll update it.

I assume by "latest patch" you mean a planned v3
that you haven't sent yet?

I think it's probably best to go with my interpretation of
the specs, if you think it makes sense:
 * running and stopping the timer is controlled by the
   "timer enable" bit (and doesn't care about the
   "interrupt enable" bit)
 * the timer timing out always sets the "interrupt flag" bit
 * we set the destination core IRQ/FIQ if the "interrupt flag"
   and "interrupt enable" bits are both set (we don't care
   about whether the "timer enable" bit is set for this)

That should be only very minor changes from what you have now.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-07 15:43         ` Peter Maydell
@ 2019-03-07 15:57           ` bzt
  2019-03-07 16:08             ` Peter Maydell
  0 siblings, 1 reply; 16+ messages in thread
From: bzt @ 2019-03-07 15:57 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Nope. I meant the second patch, sent on 4th Mar, which had all the
things fixed you listed in your review.

But here's the modification for your latest query. This one controls
the timer depending on ENABLE bit. It sets the INTFLAG even if
INTENABLE is not set, and only raises the IRQ if both are set.

Sign-off-by: Zoltán Baldaszti <bztemail@gmail.com>
Subject: [PATCH] Added periodic IRQ support for bcm2836_control local timer
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
index cfa5bc7365..d75dedbf20 100644
--- a/hw/intc/bcm2836_control.c
+++ b/hw/intc/bcm2836_control.c
@@ -7,7 +7,13 @@
  * This code is licensed under the GNU GPLv2 and later.
  *
  * At present, only implements interrupt routing, and mailboxes (i.e.,
- * not local timer, PMU interrupt, or AXI counters).
+ * not PMU interrupt, or AXI counters).
+ *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * The IRQ_TIMER support is still very basic, does not provide timer counter
+ * access and other timer features, it just generates periodic IRQs. But it
+ * still requires not only the interrupt enable, but the timer enable bit to
+ * be set.
  *
  * Ref:
  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
@@ -18,6 +24,9 @@
 #include "qemu/log.h"

 #define REG_GPU_ROUTE           0x0c
+#define REG_LOCALTIMERROUTING   0x24
+#define REG_LOCALTIMERCONTROL   0x34
+#define REG_LOCALTIMERACK       0x38
 #define REG_TIMERCONTROL        0x40
 #define REG_MBOXCONTROL         0x50
 #define REG_IRQSRC              0x60
@@ -43,6 +52,13 @@
 #define IRQ_TIMER       11
 #define IRQ_MAX         IRQ_TIMER

+#define LOCALTIMER_FREQ      38400000
+#define LOCALTIMER_INTFLAG   (1 << 31)
+#define LOCALTIMER_RELOAD    (1 << 30)
+#define LOCALTIMER_INTENABLE (1 << 29)
+#define LOCALTIMER_ENABLE    (1 << 28)
+#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
+
 static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
                           uint32_t controlreg, uint8_t controlidx)
 {
@@ -78,6 +94,17 @@ static void bcm2836_control_update(BCM2836ControlState *s)
         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
     }

+    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
+    if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
+        (s->local_timer_control & LOCALTIMER_INTFLAG)) {
+        if (s->route_localtimer & 4) {
+            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        } else {
+            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        }
+        s->local_timer_control &= ~LOCALTIMER_INTENABLE;
+    }
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         /* handle local timer interrupts for this core */
         if (s->timerirqs[i]) {
@@ -162,6 +189,55 @@ static void bcm2836_control_set_gpu_fiq(void
*opaque, int irq, int level)
     bcm2836_control_update(s);
 }

+static void bcm2836_control_local_timer_set_next(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+    uint64_t next_event;
+
+    assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
+
+    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
+            NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
+    timer_mod(&s->timer, next_event);
+}
+
+static void bcm2836_control_local_timer_tick(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+
+    bcm2836_control_local_timer_set_next(s);
+
+    s->local_timer_control |= LOCALTIMER_INTFLAG;
+    bcm2836_control_update(s);
+}
+
+static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    s->local_timer_control = val & ~LOCALTIMER_INTFLAG;
+    if (val & LOCALTIMER_ENABLE) {
+        bcm2836_control_local_timer_set_next(s);
+    } else {
+        timer_del(&s->timer);
+    }
+}
+
+static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    if (val & LOCALTIMER_INTFLAG) {
+        s->local_timer_control |= LOCALTIMER_INTENABLE;
+        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
+    }
+    if ((val & LOCALTIMER_RELOAD) &&
+        (s->local_timer_control & LOCALTIMER_ENABLE)) {
+            bcm2836_control_local_timer_set_next(s);
+    }
+}
+
 static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
unsigned size)
 {
     BCM2836ControlState *s = opaque;
@@ -170,6 +246,12 @@ static uint64_t bcm2836_control_read(void
*opaque, hwaddr offset, unsigned size)
         assert(s->route_gpu_fiq < BCM2836_NCORES
                && s->route_gpu_irq < BCM2836_NCORES);
         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        return s->route_localtimer;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        return s->local_timer_control;
+    } else if (offset == REG_LOCALTIMERACK) {
+        return 0;
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -195,6 +277,12 @@ static void bcm2836_control_write(void *opaque,
hwaddr offset,
     if (offset == REG_GPU_ROUTE) {
         s->route_gpu_irq = val & 0x3;
         s->route_gpu_fiq = (val >> 2) & 0x3;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        s->route_localtimer = val & 7;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        bcm2836_control_local_timer_control(s, val);
+    } else if (offset == REG_LOCALTIMERACK) {
+        bcm2836_control_local_timer_ack(s, val);
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -227,6 +315,10 @@ static void bcm2836_control_reset(DeviceState *d)

     s->route_gpu_irq = s->route_gpu_fiq = 0;

+    timer_del(&s->timer);
+    s->route_localtimer = 0;
+    s->local_timer_control = 0;
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         s->timercontrol[i] = 0;
         s->mailboxcontrol[i] = 0;
@@ -263,11 +355,14 @@ static void bcm2836_control_init(Object *obj)
     /* outputs to CPU cores */
     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+
+    /* create a qemu virtual timer */
+    timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
bcm2836_control_local_timer_tick, s);
 }

 static const VMStateDescription vmstate_bcm2836_control = {
     .name = TYPE_BCM2836_CONTROL,
-    .version_id = 1,
+    .version_id = 2,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
@@ -277,6 +372,9 @@ static const VMStateDescription vmstate_bcm2836_control = {
         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState,
BCM2836_NCORES),
         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
                              BCM2836_NCORES),
+        VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
+        VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
+        VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/include/hw/intc/bcm2836_control.h
b/include/hw/intc/bcm2836_control.h
index 613f3c4186..de061b8929 100644
--- a/include/hw/intc/bcm2836_control.h
+++ b/include/hw/intc/bcm2836_control.h
@@ -5,6 +5,9 @@
  * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
  * Written by Andrew Baumann
  *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * Added basic IRQ_TIMER interrupt support
+ *
  * This code is licensed under the GNU GPLv2 and later.
  */

@@ -12,6 +15,7 @@
 #define BCM2836_CONTROL_H

 #include "hw/sysbus.h"
+#include "qemu/timer.h"

 /* 4 mailboxes per core, for 16 total */
 #define BCM2836_NCORES 4
@@ -39,6 +43,11 @@ typedef struct BCM2836ControlState {
     bool gpu_irq, gpu_fiq;
     uint8_t timerirqs[BCM2836_NCORES];

+    /* local timer */
+    QEMUTimer timer;
+    uint32_t local_timer_control;
+    uint8_t route_localtimer;
+
     /* interrupt source registers, post-routing (also input-derived;
visible) */
     uint32_t irqsrc[BCM2836_NCORES];
     uint32_t fiqsrc[BCM2836_NCORES];


On 3/7/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Thu, 7 Mar 2019 at 15:27, bzt <bztemail@gmail.com> wrote:
>> Yes, could be. The QA7 spec is not really detailed, and calling both
>> timers simply ARM timers can be misleading. But it's not relevant
>> anyway from the IRQ's point of view. My latest patch checks both bits
>> to be set to generate the IRQ, so it does not really matter. As I've
>> said, this patch adds only the periodic IRQ, and not a full timer
>> emulation.
>>
>> Do you want any modifications on the patch? If so, what exactly? Let
>> me know and I'll update it.
>
> I assume by "latest patch" you mean a planned v3
> that you haven't sent yet?
>
> I think it's probably best to go with my interpretation of
> the specs, if you think it makes sense:
>  * running and stopping the timer is controlled by the
>    "timer enable" bit (and doesn't care about the
>    "interrupt enable" bit)
>  * the timer timing out always sets the "interrupt flag" bit
>  * we set the destination core IRQ/FIQ if the "interrupt flag"
>    and "interrupt enable" bits are both set (we don't care
>    about whether the "timer enable" bit is set for this)
>
> That should be only very minor changes from what you have now.
>
> thanks
> -- PMM
>

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-07 15:57           ` bzt
@ 2019-03-07 16:08             ` Peter Maydell
  2019-03-09  1:03               ` bzt
  0 siblings, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-03-07 16:08 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Thu, 7 Mar 2019 at 15:57, bzt <bztemail@gmail.com> wrote:
>
> Nope. I meant the second patch, sent on 4th Mar, which had all the
> things fixed you listed in your review.
>
> But here's the modification for your latest query. This one controls
> the timer depending on ENABLE bit. It sets the INTFLAG even if
> INTENABLE is not set, and only raises the IRQ if both are set.

Thanks. I've listed a couple of minor things below
which I think are not quite handling the flags right,
but the rest looks good.

> @@ -78,6 +94,17 @@ static void bcm2836_control_update(BCM2836ControlState *s)
>          s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
>      }
>
> +    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
> +    if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
> +        (s->local_timer_control & LOCALTIMER_INTFLAG)) {
> +        if (s->route_localtimer & 4) {
> +            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
> +        } else {
> +            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
> +        }
> +        s->local_timer_control &= ~LOCALTIMER_INTENABLE;

This shouldn't clear the INTENABLE flag.

> +    }
> +
>      for (i = 0; i < BCM2836_NCORES; i++) {
>          /* handle local timer interrupts for this core */
>          if (s->timerirqs[i]) {
> @@ -162,6 +189,55 @@ static void bcm2836_control_set_gpu_fiq(void
> *opaque, int irq, int level)
>      bcm2836_control_update(s);
>  }

> +static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
> +{
> +    BCM2836ControlState *s = opaque;
> +
> +    s->local_timer_control = val & ~LOCALTIMER_INTFLAG;

This will clear the LOCALTIMER_INTFLAG if it was already
set -- you want to preserve it, something like
   s->local_timer_control = (s->local_timer_control & LOCALTIMER_INTFLAG) |
     (val & ~LOCALTIMER_INTFLAG);

> +    if (val & LOCALTIMER_ENABLE) {
> +        bcm2836_control_local_timer_set_next(s);
> +    } else {
> +        timer_del(&s->timer);
> +    }
> +}
> +
> +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
> +{
> +    BCM2836ControlState *s = opaque;
> +
> +    if (val & LOCALTIMER_INTFLAG) {
> +        s->local_timer_control |= LOCALTIMER_INTENABLE;
> +        s->local_timer_control &= ~LOCALTIMER_INTFLAG;

This should just clear the INTFLAG, it doesn't affect INTENABLE.

> +    }
> +    if ((val & LOCALTIMER_RELOAD) &&
> +        (s->local_timer_control & LOCALTIMER_ENABLE)) {
> +            bcm2836_control_local_timer_set_next(s);
> +    }
> +}

thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-07 16:08             ` Peter Maydell
@ 2019-03-09  1:03               ` bzt
  2019-03-09 14:39                 ` Peter Maydell
  0 siblings, 1 reply; 16+ messages in thread
From: bzt @ 2019-03-09  1:03 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi,

Thanks for your answers. If I don't clear the INTENABLE flag, then the
IRQ would keep firing constantly. This is not how the real hardware
works: it triggers the IRQ once, and then it inhibits. The timer won't
trigger the IRQ again until you acknowledge it by writing the INTFLAG
into the ack register. My solution emulates this behaviour. That's
what the triggered flag was for in my original patch. Should I bring
that flag back or is the current solution acceptable knowing this?

About keeping the INTFLAG on control write that's to avoid an instant
IRQ, but that's OK. I'll modify that.

Thanks,
bzt


On 3/7/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Thu, 7 Mar 2019 at 15:57, bzt <bztemail@gmail.com> wrote:
>>
>> Nope. I meant the second patch, sent on 4th Mar, which had all the
>> things fixed you listed in your review.
>>
>> But here's the modification for your latest query. This one controls
>> the timer depending on ENABLE bit. It sets the INTFLAG even if
>> INTENABLE is not set, and only raises the IRQ if both are set.
>
> Thanks. I've listed a couple of minor things below
> which I think are not quite handling the flags right,
> but the rest looks good.
>
>> @@ -78,6 +94,17 @@ static void bcm2836_control_update(BCM2836ControlState
>> *s)
>>          s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
>>      }
>>
>> +    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
>> +    if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
>> +        (s->local_timer_control & LOCALTIMER_INTFLAG)) {
>> +        if (s->route_localtimer & 4) {
>> +            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>> IRQ_TIMER;
>> +        } else {
>> +            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 <<
>> IRQ_TIMER;
>> +        }
>> +        s->local_timer_control &= ~LOCALTIMER_INTENABLE;
>
> This shouldn't clear the INTENABLE flag.
>
>> +    }
>> +
>>      for (i = 0; i < BCM2836_NCORES; i++) {
>>          /* handle local timer interrupts for this core */
>>          if (s->timerirqs[i]) {
>> @@ -162,6 +189,55 @@ static void bcm2836_control_set_gpu_fiq(void
>> *opaque, int irq, int level)
>>      bcm2836_control_update(s);
>>  }
>
>> +static void bcm2836_control_local_timer_control(void *opaque, uint32_t
>> val)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +
>> +    s->local_timer_control = val & ~LOCALTIMER_INTFLAG;
>
> This will clear the LOCALTIMER_INTFLAG if it was already
> set -- you want to preserve it, something like
>    s->local_timer_control = (s->local_timer_control & LOCALTIMER_INTFLAG) |
>      (val & ~LOCALTIMER_INTFLAG);
>
>> +    if (val & LOCALTIMER_ENABLE) {
>> +        bcm2836_control_local_timer_set_next(s);
>> +    } else {
>> +        timer_del(&s->timer);
>> +    }
>> +}
>> +
>> +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
>> +{
>> +    BCM2836ControlState *s = opaque;
>> +
>> +    if (val & LOCALTIMER_INTFLAG) {
>> +        s->local_timer_control |= LOCALTIMER_INTENABLE;
>> +        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
>
> This should just clear the INTFLAG, it doesn't affect INTENABLE.
>
>> +    }
>> +    if ((val & LOCALTIMER_RELOAD) &&
>> +        (s->local_timer_control & LOCALTIMER_ENABLE)) {
>> +            bcm2836_control_local_timer_set_next(s);
>> +    }
>> +}
>
> thanks
> -- PMM
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-09  1:03               ` bzt
@ 2019-03-09 14:39                 ` Peter Maydell
  2019-03-10 11:02                   ` bzt
  0 siblings, 1 reply; 16+ messages in thread
From: Peter Maydell @ 2019-03-09 14:39 UTC (permalink / raw)
  To: bzt
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

On Sat, 9 Mar 2019 at 01:03, bzt <bztemail@gmail.com> wrote:
> Thanks for your answers. If I don't clear the INTENABLE flag, then the
> IRQ would keep firing constantly. This is not how the real hardware
> works: it triggers the IRQ once, and then it inhibits. The timer won't
> trigger the IRQ again until you acknowledge it by writing the INTFLAG
> into the ack register. My solution emulates this behaviour. That's
> what the triggered flag was for in my original patch. Should I bring
> that flag back or is the current solution acceptable knowing this?

Huh. The QA7 spec doc is pretty clear that the IRQ is kept high
until the guest acknowledges it (and that is how in general
IRQ/FIQ works for Arm -- it is level triggered and it stays high
until the guest silences the device):
"An interrupt is generated as long as the interrupt flag is set
and the interrupt-enable bit is set" and "The user must clear the
interrupt flag".


thanks
-- PMM

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer
  2019-03-09 14:39                 ` Peter Maydell
@ 2019-03-10 11:02                   ` bzt
  0 siblings, 0 replies; 16+ messages in thread
From: bzt @ 2019-03-10 11:02 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Baumann, Philippe Mathieu-Daudé, qemu-arm, QEMU Developers

Hi,

Okay, as you wish. My code works either way and on real hardware as
well, because I acknowledge the periodic IRQ as soon as possible, so
good for me.

Sign-off-by: Zoltán Baldaszti <bztemail@gmail.com>
Subject: [PATCH] Added periodic IRQ support for bcm2836_control local timer
diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c
index cfa5bc7365..82d2f51ffe 100644
--- a/hw/intc/bcm2836_control.c
+++ b/hw/intc/bcm2836_control.c
@@ -7,7 +7,13 @@
  * This code is licensed under the GNU GPLv2 and later.
  *
  * At present, only implements interrupt routing, and mailboxes (i.e.,
- * not local timer, PMU interrupt, or AXI counters).
+ * not PMU interrupt, or AXI counters).
+ *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * The IRQ_TIMER support is still very basic, does not provide timer counter
+ * access and other timer features, it just generates periodic IRQs. But it
+ * still requires not only the interrupt enable, but the timer enable bit to
+ * be set.
  *
  * Ref:
  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
@@ -18,6 +24,9 @@
 #include "qemu/log.h"

 #define REG_GPU_ROUTE           0x0c
+#define REG_LOCALTIMERROUTING   0x24
+#define REG_LOCALTIMERCONTROL   0x34
+#define REG_LOCALTIMERACK       0x38
 #define REG_TIMERCONTROL        0x40
 #define REG_MBOXCONTROL         0x50
 #define REG_IRQSRC              0x60
@@ -43,6 +52,13 @@
 #define IRQ_TIMER       11
 #define IRQ_MAX         IRQ_TIMER

+#define LOCALTIMER_FREQ      38400000
+#define LOCALTIMER_INTFLAG   (1 << 31)
+#define LOCALTIMER_RELOAD    (1 << 30)
+#define LOCALTIMER_INTENABLE (1 << 29)
+#define LOCALTIMER_ENABLE    (1 << 28)
+#define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
+
 static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
                           uint32_t controlreg, uint8_t controlidx)
 {
@@ -78,6 +94,17 @@ static void bcm2836_control_update(BCM2836ControlState *s)
         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
     }

+    /* handle THE local timer interrupt for one of the cores' IRQ/FIQ */
+    if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
+        (s->local_timer_control & LOCALTIMER_INTFLAG)) {
+        /* note: this will keep firing the IRQ as Peter asked */
+        if (s->route_localtimer & 4) {
+            s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        } else {
+            s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
+        }
+    }
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         /* handle local timer interrupts for this core */
         if (s->timerirqs[i]) {
@@ -162,6 +189,54 @@ static void bcm2836_control_set_gpu_fiq(void
*opaque, int irq, int level)
     bcm2836_control_update(s);
 }

+static void bcm2836_control_local_timer_set_next(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+    uint64_t next_event;
+
+    assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
+
+    next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+        muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
+            NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
+    timer_mod(&s->timer, next_event);
+}
+
+static void bcm2836_control_local_timer_tick(void *opaque)
+{
+    BCM2836ControlState *s = opaque;
+
+    bcm2836_control_local_timer_set_next(s);
+
+    s->local_timer_control |= LOCALTIMER_INTFLAG;
+    bcm2836_control_update(s);
+}
+
+static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    s->local_timer_control = val;
+    if (val & LOCALTIMER_ENABLE) {
+        bcm2836_control_local_timer_set_next(s);
+    } else {
+        timer_del(&s->timer);
+    }
+}
+
+static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
+{
+    BCM2836ControlState *s = opaque;
+
+    if (val & LOCALTIMER_INTFLAG) {
+        s->local_timer_control &= ~LOCALTIMER_INTFLAG;
+    }
+    if ((val & LOCALTIMER_RELOAD) &&
+        (s->local_timer_control & LOCALTIMER_ENABLE)) {
+            bcm2836_control_local_timer_set_next(s);
+    }
+}
+
 static uint64_t bcm2836_control_read(void *opaque, hwaddr offset,
unsigned size)
 {
     BCM2836ControlState *s = opaque;
@@ -170,6 +245,12 @@ static uint64_t bcm2836_control_read(void
*opaque, hwaddr offset, unsigned size)
         assert(s->route_gpu_fiq < BCM2836_NCORES
                && s->route_gpu_irq < BCM2836_NCORES);
         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        return s->route_localtimer;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        return s->local_timer_control;
+    } else if (offset == REG_LOCALTIMERACK) {
+        return 0;
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -195,6 +276,12 @@ static void bcm2836_control_write(void *opaque,
hwaddr offset,
     if (offset == REG_GPU_ROUTE) {
         s->route_gpu_irq = val & 0x3;
         s->route_gpu_fiq = (val >> 2) & 0x3;
+    } else if (offset == REG_LOCALTIMERROUTING) {
+        s->route_localtimer = val & 7;
+    } else if (offset == REG_LOCALTIMERCONTROL) {
+        bcm2836_control_local_timer_control(s, val);
+    } else if (offset == REG_LOCALTIMERACK) {
+        bcm2836_control_local_timer_ack(s, val);
     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
@@ -227,6 +314,10 @@ static void bcm2836_control_reset(DeviceState *d)

     s->route_gpu_irq = s->route_gpu_fiq = 0;

+    timer_del(&s->timer);
+    s->route_localtimer = 0;
+    s->local_timer_control = 0;
+
     for (i = 0; i < BCM2836_NCORES; i++) {
         s->timercontrol[i] = 0;
         s->mailboxcontrol[i] = 0;
@@ -263,11 +354,14 @@ static void bcm2836_control_init(Object *obj)
     /* outputs to CPU cores */
     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
+
+    /* create a qemu virtual timer */
+    timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
bcm2836_control_local_timer_tick, s);
 }

 static const VMStateDescription vmstate_bcm2836_control = {
     .name = TYPE_BCM2836_CONTROL,
-    .version_id = 1,
+    .version_id = 2,
     .minimum_version_id = 1,
     .fields = (VMStateField[]) {
         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
@@ -277,6 +371,9 @@ static const VMStateDescription vmstate_bcm2836_control = {
         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState,
BCM2836_NCORES),
         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
                              BCM2836_NCORES),
+        VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
+        VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
+        VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/include/hw/intc/bcm2836_control.h
b/include/hw/intc/bcm2836_control.h
index 613f3c4186..de061b8929 100644
--- a/include/hw/intc/bcm2836_control.h
+++ b/include/hw/intc/bcm2836_control.h
@@ -5,6 +5,9 @@
  * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
  * Written by Andrew Baumann
  *
+ * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
+ * Added basic IRQ_TIMER interrupt support
+ *
  * This code is licensed under the GNU GPLv2 and later.
  */

@@ -12,6 +15,7 @@
 #define BCM2836_CONTROL_H

 #include "hw/sysbus.h"
+#include "qemu/timer.h"

 /* 4 mailboxes per core, for 16 total */
 #define BCM2836_NCORES 4
@@ -39,6 +43,11 @@ typedef struct BCM2836ControlState {
     bool gpu_irq, gpu_fiq;
     uint8_t timerirqs[BCM2836_NCORES];

+    /* local timer */
+    QEMUTimer timer;
+    uint32_t local_timer_control;
+    uint8_t route_localtimer;
+
     /* interrupt source registers, post-routing (also input-derived;
visible) */
     uint32_t irqsrc[BCM2836_NCORES];
     uint32_t fiqsrc[BCM2836_NCORES];


On 3/9/19, Peter Maydell <peter.maydell@linaro.org> wrote:
> On Sat, 9 Mar 2019 at 01:03, bzt <bztemail@gmail.com> wrote:
>> Thanks for your answers. If I don't clear the INTENABLE flag, then the
>> IRQ would keep firing constantly. This is not how the real hardware
>> works: it triggers the IRQ once, and then it inhibits. The timer won't
>> trigger the IRQ again until you acknowledge it by writing the INTFLAG
>> into the ack register. My solution emulates this behaviour. That's
>> what the triggered flag was for in my original patch. Should I bring
>> that flag back or is the current solution acceptable knowing this?
>
> Huh. The QA7 spec doc is pretty clear that the IRQ is kept high
> until the guest acknowledges it (and that is how in general
> IRQ/FIQ works for Arm -- it is level triggered and it stays high
> until the guest silences the device):
> "An interrupt is generated as long as the interrupt flag is set
> and the interrupt-enable bit is set" and "The user must clear the
> interrupt flag".
>
>
> thanks
> -- PMM
>

^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2019-03-10 11:02 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-26 11:38 [Qemu-devel] [PATCH] Added periodic IRQ support for bcm2836_control local timer bzt
2019-02-26 12:25 ` Peter Maydell
2019-02-27 11:54   ` bzt
2019-02-27 18:30     ` Andrew Baumann
2019-02-28 14:12       ` bzt
2019-03-04 15:55 ` Peter Maydell
2019-03-04 19:27   ` bzt
2019-03-04 20:40     ` bzt
2019-03-05 16:37     ` Peter Maydell
2019-03-07 15:27       ` bzt
2019-03-07 15:43         ` Peter Maydell
2019-03-07 15:57           ` bzt
2019-03-07 16:08             ` Peter Maydell
2019-03-09  1:03               ` bzt
2019-03-09 14:39                 ` Peter Maydell
2019-03-10 11:02                   ` bzt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.