Re: [PATCH V1 1/2] hw/rtc: Fixed loongson rtc emulation errors

2025-07-07 Thread Bibo Mao




On 2025/7/7 下午2:36, lixianglai wrote:

Hi Bibo Mao:



On 2025/6/13 上午9:31, Xianglai Li wrote:

The expire time is sent to the timer only
when the expire Time is greater than 0 or
greater than now. Otherwise, the timer
will trigger interruption continuously.

Timer interrupts are sent using pulse functions.

Signed-off-by: Xianglai Li 
---
Cc: Bibo Mao 
Cc: Song Gao 
Cc: Jiaxun Yang 
Cc: Xianglai Li 

  hw/loongarch/virt-fdt-build.c | 11 +--
  hw/rtc/ls7a_rtc.c | 26 +-
  2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/hw/loongarch/virt-fdt-build.c 
b/hw/loongarch/virt-fdt-build.c

index 728ce46699..c613131a07 100644
--- a/hw/loongarch/virt-fdt-build.c
+++ b/hw/loongarch/virt-fdt-build.c
@@ -17,6 +17,11 @@
  #include "system/reset.h"
  #include "target/loongarch/cpu.h"
  +#define FDT_IRQ_FLAGS_EDGE_LO_HI 1
+#define FDT_IRQ_FLAGS_EDGE_HI_LO 2
+#define FDT_IRQ_FLAGS_LEVEL_HI 4
+#define FDT_IRQ_FLAGS_LEVEL_LO 8
+
  static void create_fdt(LoongArchVirtMachineState *lvms)
  {
  MachineState *ms = MACHINE(lvms);
@@ -416,7 +421,8 @@ static void 
fdt_add_uart_node(LoongArchVirtMachineState *lvms,

  if (chosen) {
  qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", 
nodename);

  }
-    qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+    qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq,
+   FDT_IRQ_FLAGS_LEVEL_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
    *pch_pic_phandle);
  g_free(nodename);
@@ -436,7 +442,8 @@ static void 
fdt_add_rtc_node(LoongArchVirtMachineState *lvms,

  "loongson,ls7a-rtc");
  qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 
2, size);

  qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
-   VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+   VIRT_RTC_IRQ - VIRT_GSI_BASE ,
+   FDT_IRQ_FLAGS_EDGE_LO_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
    *pch_pic_phandle);
  g_free(nodename);
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
index 10097b2db7..7eca75a42a 100644
--- a/hw/rtc/ls7a_rtc.c
+++ b/hw/rtc/ls7a_rtc.c
@@ -145,20 +145,24 @@ static void toymatch_write(LS7ARtcState *s, 
uint64_t val, int num)

  now = qemu_clock_get_ms(rtc_clock);
  toymatch_val_to_time(s, val, &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - 
s->offset_toy) * 1000;

-    timer_mod(s->toy_timer[num], expire_time);
+    if (expire_time > now) {
+    timer_mod(s->toy_timer[num], expire_time);
+    }

Should rtc alarm interrupt be injected directly if (expire_time <= now)?


Through experiments on the hardware platform, sending an alarm less than
the current time to loongson rtc alarm does not trigger an interrupt.

According to the behavior of the hardware, we do not need to trigger an
interrupt immediately when expire_time <= now
Sounds good. yes, the behavior of qemu emulation should be the same with 
real HW.


Regards
Bibo Mao


Thanks,
Xianglai Li.


Regards
Bibo Mao

  }
  }
    static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
  {
-    uint64_t expire_ns;
+    int64_t expire_ns;
    /* it do not support write when toy disabled */
  if (rtc_enabled(s)) {
  s->rtcmatch[num] = val;
  /* calculate expire time */
  expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
-    timer_mod_ns(s->rtc_timer[num], expire_ns);
+    if (expire_ns > 0) {
+    timer_mod_ns(s->rtc_timer[num], expire_ns);
+    }
  }
  }
  @@ -185,7 +189,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s)
  static void ls7a_toy_start(LS7ARtcState *s)
  {
  int i;
-    uint64_t expire_time, now;
+    int64_t expire_time, now;
  struct tm tm = {};
    now = qemu_clock_get_ms(rtc_clock);
@@ -194,19 +198,23 @@ static void ls7a_toy_start(LS7ARtcState *s)
  for (i = 0; i < TIMER_NUMS; i++) {
  toymatch_val_to_time(s, s->toymatch[i], &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - 
s->offset_toy) * 1000;

-    timer_mod(s->toy_timer[i], expire_time);
+    if (expire_time > now) {
+    timer_mod(s->toy_timer[i], expire_time);
+    }
  }
  }
    static void ls7a_rtc_start(LS7ARtcState *s)
  {
  int i;
-    uint64_t expire_time;
+    int64_t expire_time;
    /* recalculate expire time and enable timer */
  for (i = 0; i < TIMER_NUMS; i++) {
  expire_time = ticks_to_ns(s->rtcmatch[i]) - 
ticks_to_ns(s->offset_rtc);

-    timer_mod_ns(s->rtc_timer[i], expire_time);
+    if (expire_time > 0) {
+    timer_mod_ns(s->rtc_timer[i], expire_time);
+    }
  }
  }
  @@ -370,7 +378,7 @@ static void toy_timer_cb(void *opaque)
  LS7ARtcState *s = opaque;
   

Re: [PATCH V1 1/2] hw/rtc: Fixed loongson rtc emulation errors

2025-07-06 Thread lixianglai

Hi Bibo Mao:



On 2025/6/13 上午9:31, Xianglai Li wrote:

The expire time is sent to the timer only
when the expire Time is greater than 0 or
greater than now. Otherwise, the timer
will trigger interruption continuously.

Timer interrupts are sent using pulse functions.

Signed-off-by: Xianglai Li 
---
Cc: Bibo Mao 
Cc: Song Gao 
Cc: Jiaxun Yang 
Cc: Xianglai Li 

  hw/loongarch/virt-fdt-build.c | 11 +--
  hw/rtc/ls7a_rtc.c | 26 +-
  2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/hw/loongarch/virt-fdt-build.c 
b/hw/loongarch/virt-fdt-build.c

index 728ce46699..c613131a07 100644
--- a/hw/loongarch/virt-fdt-build.c
+++ b/hw/loongarch/virt-fdt-build.c
@@ -17,6 +17,11 @@
  #include "system/reset.h"
  #include "target/loongarch/cpu.h"
  +#define FDT_IRQ_FLAGS_EDGE_LO_HI 1
+#define FDT_IRQ_FLAGS_EDGE_HI_LO 2
+#define FDT_IRQ_FLAGS_LEVEL_HI 4
+#define FDT_IRQ_FLAGS_LEVEL_LO 8
+
  static void create_fdt(LoongArchVirtMachineState *lvms)
  {
  MachineState *ms = MACHINE(lvms);
@@ -416,7 +421,8 @@ static void 
fdt_add_uart_node(LoongArchVirtMachineState *lvms,

  if (chosen) {
  qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", 
nodename);

  }
-    qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+    qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq,
+   FDT_IRQ_FLAGS_LEVEL_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
    *pch_pic_phandle);
  g_free(nodename);
@@ -436,7 +442,8 @@ static void 
fdt_add_rtc_node(LoongArchVirtMachineState *lvms,

  "loongson,ls7a-rtc");
  qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 
2, size);

  qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
-   VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+   VIRT_RTC_IRQ - VIRT_GSI_BASE ,
+   FDT_IRQ_FLAGS_EDGE_LO_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
    *pch_pic_phandle);
  g_free(nodename);
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
index 10097b2db7..7eca75a42a 100644
--- a/hw/rtc/ls7a_rtc.c
+++ b/hw/rtc/ls7a_rtc.c
@@ -145,20 +145,24 @@ static void toymatch_write(LS7ARtcState *s, 
uint64_t val, int num)

  now = qemu_clock_get_ms(rtc_clock);
  toymatch_val_to_time(s, val, &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - 
s->offset_toy) * 1000;

-    timer_mod(s->toy_timer[num], expire_time);
+    if (expire_time > now) {
+    timer_mod(s->toy_timer[num], expire_time);
+    }

Should rtc alarm interrupt be injected directly if (expire_time <= now)?


Through experiments on the hardware platform, sending an alarm less than
the current time to loongson rtc alarm does not trigger an interrupt.

According to the behavior of the hardware, we do not need to trigger an
interrupt immediately when expire_time <= now

Thanks,
Xianglai Li.


Regards
Bibo Mao

  }
  }
    static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)
  {
-    uint64_t expire_ns;
+    int64_t expire_ns;
    /* it do not support write when toy disabled */
  if (rtc_enabled(s)) {
  s->rtcmatch[num] = val;
  /* calculate expire time */
  expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
-    timer_mod_ns(s->rtc_timer[num], expire_ns);
+    if (expire_ns > 0) {
+    timer_mod_ns(s->rtc_timer[num], expire_ns);
+    }
  }
  }
  @@ -185,7 +189,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s)
  static void ls7a_toy_start(LS7ARtcState *s)
  {
  int i;
-    uint64_t expire_time, now;
+    int64_t expire_time, now;
  struct tm tm = {};
    now = qemu_clock_get_ms(rtc_clock);
@@ -194,19 +198,23 @@ static void ls7a_toy_start(LS7ARtcState *s)
  for (i = 0; i < TIMER_NUMS; i++) {
  toymatch_val_to_time(s, s->toymatch[i], &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - 
s->offset_toy) * 1000;

-    timer_mod(s->toy_timer[i], expire_time);
+    if (expire_time > now) {
+    timer_mod(s->toy_timer[i], expire_time);
+    }
  }
  }
    static void ls7a_rtc_start(LS7ARtcState *s)
  {
  int i;
-    uint64_t expire_time;
+    int64_t expire_time;
    /* recalculate expire time and enable timer */
  for (i = 0; i < TIMER_NUMS; i++) {
  expire_time = ticks_to_ns(s->rtcmatch[i]) - 
ticks_to_ns(s->offset_rtc);

-    timer_mod_ns(s->rtc_timer[i], expire_time);
+    if (expire_time > 0) {
+    timer_mod_ns(s->rtc_timer[i], expire_time);
+    }
  }
  }
  @@ -370,7 +378,7 @@ static void toy_timer_cb(void *opaque)
  LS7ARtcState *s = opaque;
    if (toy_enabled(s)) {
-    qemu_irq_raise(s->irq);
+    qemu_irq_pulse(s->irq);
  }
  }
  @@ -379,7 +387,7 @@ static void rtc_timer_

Re: [PATCH V1 1/2] hw/rtc: Fixed loongson rtc emulation errors

2025-06-12 Thread Bibo Mao




On 2025/6/13 上午9:31, Xianglai Li wrote:

The expire time is sent to the timer only
when the expire Time is greater than 0 or
greater than now. Otherwise, the timer
will trigger interruption continuously.

Timer interrupts are sent using pulse functions.

Signed-off-by: Xianglai Li 
---
Cc: Bibo Mao 
Cc: Song Gao 
Cc: Jiaxun Yang 
Cc: Xianglai Li 

  hw/loongarch/virt-fdt-build.c | 11 +--
  hw/rtc/ls7a_rtc.c | 26 +-
  2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c
index 728ce46699..c613131a07 100644
--- a/hw/loongarch/virt-fdt-build.c
+++ b/hw/loongarch/virt-fdt-build.c
@@ -17,6 +17,11 @@
  #include "system/reset.h"
  #include "target/loongarch/cpu.h"
  
+#define FDT_IRQ_FLAGS_EDGE_LO_HI 1

+#define FDT_IRQ_FLAGS_EDGE_HI_LO 2
+#define FDT_IRQ_FLAGS_LEVEL_HI 4
+#define FDT_IRQ_FLAGS_LEVEL_LO 8
+
  static void create_fdt(LoongArchVirtMachineState *lvms)
  {
  MachineState *ms = MACHINE(lvms);
@@ -416,7 +421,8 @@ static void fdt_add_uart_node(LoongArchVirtMachineState 
*lvms,
  if (chosen) {
  qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
  }
-qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq,
+   FDT_IRQ_FLAGS_LEVEL_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
*pch_pic_phandle);
  g_free(nodename);
@@ -436,7 +442,8 @@ static void fdt_add_rtc_node(LoongArchVirtMachineState 
*lvms,
  "loongson,ls7a-rtc");
  qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
  qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
-   VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+   VIRT_RTC_IRQ - VIRT_GSI_BASE ,
+   FDT_IRQ_FLAGS_EDGE_LO_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
*pch_pic_phandle);
  g_free(nodename);
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
index 10097b2db7..7eca75a42a 100644
--- a/hw/rtc/ls7a_rtc.c
+++ b/hw/rtc/ls7a_rtc.c
@@ -145,20 +145,24 @@ static void toymatch_write(LS7ARtcState *s, uint64_t val, 
int num)
  now = qemu_clock_get_ms(rtc_clock);
  toymatch_val_to_time(s, val, &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
-timer_mod(s->toy_timer[num], expire_time);
+if (expire_time > now) {
+timer_mod(s->toy_timer[num], expire_time);
+}

Should rtc alarm interrupt be injected directly if (expire_time <= now)?

Regards
Bibo Mao

  }
  }
  
  static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)

  {
-uint64_t expire_ns;
+int64_t expire_ns;
  
  /* it do not support write when toy disabled */

  if (rtc_enabled(s)) {
  s->rtcmatch[num] = val;
  /* calculate expire time */
  expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
-timer_mod_ns(s->rtc_timer[num], expire_ns);
+if (expire_ns > 0) {
+timer_mod_ns(s->rtc_timer[num], expire_ns);
+}
  }
  }
  
@@ -185,7 +189,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s)

  static void ls7a_toy_start(LS7ARtcState *s)
  {
  int i;
-uint64_t expire_time, now;
+int64_t expire_time, now;
  struct tm tm = {};
  
  now = qemu_clock_get_ms(rtc_clock);

@@ -194,19 +198,23 @@ static void ls7a_toy_start(LS7ARtcState *s)
  for (i = 0; i < TIMER_NUMS; i++) {
  toymatch_val_to_time(s, s->toymatch[i], &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
-timer_mod(s->toy_timer[i], expire_time);
+if (expire_time > now) {
+timer_mod(s->toy_timer[i], expire_time);
+}
  }
  }
  
  static void ls7a_rtc_start(LS7ARtcState *s)

  {
  int i;
-uint64_t expire_time;
+int64_t expire_time;
  
  /* recalculate expire time and enable timer */

  for (i = 0; i < TIMER_NUMS; i++) {
  expire_time = ticks_to_ns(s->rtcmatch[i]) - 
ticks_to_ns(s->offset_rtc);
-timer_mod_ns(s->rtc_timer[i], expire_time);
+if (expire_time > 0) {
+timer_mod_ns(s->rtc_timer[i], expire_time);
+}
  }
  }
  
@@ -370,7 +378,7 @@ static void toy_timer_cb(void *opaque)

  LS7ARtcState *s = opaque;
  
  if (toy_enabled(s)) {

-qemu_irq_raise(s->irq);
+qemu_irq_pulse(s->irq);
  }
  }
  
@@ -379,7 +387,7 @@ static void rtc_timer_cb(void *opaque)

  LS7ARtcState *s = opaque;
  
  if (rtc_enabled(s)) {

-qemu_irq_raise(s->irq);
+qemu_irq_pulse(s->irq);
  }
  }
  






Re: [PATCH V1 1/2] hw/rtc: Fixed loongson rtc emulation errors

2025-06-12 Thread Bibo Mao




On 2025/6/13 上午9:31, Xianglai Li wrote:

The expire time is sent to the timer only
when the expire Time is greater than 0 or
greater than now. Otherwise, the timer
will trigger interruption continuously.

Again, one line of the comments should no more than 75 bytes :)

I think it should be split into two patches, one is fixup with irq pulse 
type setting, the other is fixup with expired time out calculation in 
rtc emulation driver.


Regards
Bibo Mao


Timer interrupts are sent using pulse functions.




Signed-off-by: Xianglai Li 
---
Cc: Bibo Mao 
Cc: Song Gao 
Cc: Jiaxun Yang 
Cc: Xianglai Li 

  hw/loongarch/virt-fdt-build.c | 11 +--
  hw/rtc/ls7a_rtc.c | 26 +-
  2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c
index 728ce46699..c613131a07 100644
--- a/hw/loongarch/virt-fdt-build.c
+++ b/hw/loongarch/virt-fdt-build.c
@@ -17,6 +17,11 @@
  #include "system/reset.h"
  #include "target/loongarch/cpu.h"
  
+#define FDT_IRQ_FLAGS_EDGE_LO_HI 1

+#define FDT_IRQ_FLAGS_EDGE_HI_LO 2
+#define FDT_IRQ_FLAGS_LEVEL_HI 4
+#define FDT_IRQ_FLAGS_LEVEL_LO 8
+
  static void create_fdt(LoongArchVirtMachineState *lvms)
  {
  MachineState *ms = MACHINE(lvms);
@@ -416,7 +421,8 @@ static void fdt_add_uart_node(LoongArchVirtMachineState 
*lvms,
  if (chosen) {
  qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename);
  }
-qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4);
+qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq,
+   FDT_IRQ_FLAGS_LEVEL_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
*pch_pic_phandle);
  g_free(nodename);
@@ -436,7 +442,8 @@ static void fdt_add_rtc_node(LoongArchVirtMachineState 
*lvms,
  "loongson,ls7a-rtc");
  qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size);
  qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts",
-   VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4);
+   VIRT_RTC_IRQ - VIRT_GSI_BASE ,
+   FDT_IRQ_FLAGS_EDGE_LO_HI);
  qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent",
*pch_pic_phandle);
  g_free(nodename);
diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c
index 10097b2db7..7eca75a42a 100644
--- a/hw/rtc/ls7a_rtc.c
+++ b/hw/rtc/ls7a_rtc.c
@@ -145,20 +145,24 @@ static void toymatch_write(LS7ARtcState *s, uint64_t val, 
int num)
  now = qemu_clock_get_ms(rtc_clock);
  toymatch_val_to_time(s, val, &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
-timer_mod(s->toy_timer[num], expire_time);
+if (expire_time > now) {
+timer_mod(s->toy_timer[num], expire_time);
+}
  }
  }
  
  static void rtcmatch_write(LS7ARtcState *s, uint64_t val, int num)

  {
-uint64_t expire_ns;
+int64_t expire_ns;
  
  /* it do not support write when toy disabled */

  if (rtc_enabled(s)) {
  s->rtcmatch[num] = val;
  /* calculate expire time */
  expire_ns = ticks_to_ns(val) - ticks_to_ns(s->offset_rtc);
-timer_mod_ns(s->rtc_timer[num], expire_ns);
+if (expire_ns > 0) {
+timer_mod_ns(s->rtc_timer[num], expire_ns);
+}
  }
  }
  
@@ -185,7 +189,7 @@ static void ls7a_rtc_stop(LS7ARtcState *s)

  static void ls7a_toy_start(LS7ARtcState *s)
  {
  int i;
-uint64_t expire_time, now;
+int64_t expire_time, now;
  struct tm tm = {};
  
  now = qemu_clock_get_ms(rtc_clock);

@@ -194,19 +198,23 @@ static void ls7a_toy_start(LS7ARtcState *s)
  for (i = 0; i < TIMER_NUMS; i++) {
  toymatch_val_to_time(s, s->toymatch[i], &tm);
  expire_time = now + (qemu_timedate_diff(&tm) - s->offset_toy) * 1000;
-timer_mod(s->toy_timer[i], expire_time);
+if (expire_time > now) {
+timer_mod(s->toy_timer[i], expire_time);
+}
  }
  }
  
  static void ls7a_rtc_start(LS7ARtcState *s)

  {
  int i;
-uint64_t expire_time;
+int64_t expire_time;
  
  /* recalculate expire time and enable timer */

  for (i = 0; i < TIMER_NUMS; i++) {
  expire_time = ticks_to_ns(s->rtcmatch[i]) - 
ticks_to_ns(s->offset_rtc);
-timer_mod_ns(s->rtc_timer[i], expire_time);
+if (expire_time > 0) {
+timer_mod_ns(s->rtc_timer[i], expire_time);
+}
  }
  }
  
@@ -370,7 +378,7 @@ static void toy_timer_cb(void *opaque)

  LS7ARtcState *s = opaque;
  
  if (toy_enabled(s)) {

-qemu_irq_raise(s->irq);
+qemu_irq_pulse(s->irq);
  }
  }
  
@@ -379,7 +387,7 @@ static void rtc_timer_cb(void *opaque)

  LS7ARtcState *s = opaque;
  
  if (rtc_enabled(s)) {

-qemu_irq_raise(s->irq);
+qemu_irq