[PATCH qemu 32/38] wdt: aspeed: Fix failed reboot due to timer miscalculation

Cédric Le Goater clg at kaod.org
Sat Nov 19 01:22:12 AEDT 2016


From: Andrew Jeffery <andrew at aj.id.au>

The implementation was incorrectly scaling the reload value by
nanoseconds per second. In reality the span represented by the reload
value is determined by the selected clock, either APB or 1MHz.

Test the selected clock and apply the appropriate scaling, then
convert to nanoseconds for the timer infrastructure. Note that the APB
rate is hard-coded and reflects the value used by the timer
controller, and isn't necessarily an accurate representation of the
clock configuration programmed in the SCU.

Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
Signed-off-by: Cédric Le Goater <clg at kaod.org>
---
 hw/watchdog/wdt_aspeed.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c
index 29d9d7f91463..860d4b73e2b6 100644
--- a/hw/watchdog/wdt_aspeed.c
+++ b/hw/watchdog/wdt_aspeed.c
@@ -56,11 +56,14 @@ static uint64_t aspeed_wdt_read(void *opaque, hwaddr offset, unsigned size)
 
 }
 
+#define PCLK_HZ 24000000
+
 static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data,
                              unsigned size)
 {
     AspeedWDTState *s = ASPEED_WDT(opaque);
     bool en = data & BIT(0);
+    bool pclk = !(data & BIT(4));
 
     switch (offset) {
     case WDT_STATUS:
@@ -73,21 +76,36 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data,
         break;
     case WDT_RESTART:
         if ((data & 0xFFFF) == 0x4755) {
+            uint32_t reload;
+
             s->reg_status = s->reg_reload_value;
 
+            if (pclk) {
+                reload = muldiv64(s->reg_reload_value, NANOSECONDS_PER_SECOND,
+                                  PCLK_HZ) ;
+            } else {
+                reload = s->reg_reload_value * 1000;
+            }
+
             if (s->enabled) {
                 timer_mod(s->timer,
-                          qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
-                          s->reg_reload_value * NANOSECONDS_PER_SECOND);
+                          qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload);
             }
         }
         break;
     case WDT_CTRL:
         if (en && !s->enabled) {
+            uint32_t reload;
+
+            if (pclk) {
+                reload = muldiv64(s->reg_reload_value, NANOSECONDS_PER_SECOND,
+                                  PCLK_HZ);
+            } else {
+                reload = s->reg_reload_value * 1000;
+            }
+
             s->enabled = true;
-            timer_mod(s->timer,
-                      qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
-                      s->reg_reload_value * NANOSECONDS_PER_SECOND);
+            timer_mod(s->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + reload);
         } else if (!en && s->enabled) {
             s->enabled = false;
             timer_del(s->timer);
-- 
2.7.4



More information about the openbmc mailing list