xref: /openbmc/qemu/tests/qtest/npcm7xx_watchdog_timer-test.c (revision f94e74a7e29482582cbb98acd0b3b10142c7712a)
1  /*
2   * QTests for Nuvoton NPCM7xx Timer Watchdog Modules.
3   *
4   * Copyright 2020 Google LLC
5   *
6   * This program is free software; you can redistribute it and/or modify it
7   * under the terms of the GNU General Public License as published by the
8   * Free Software Foundation; either version 2 of the License, or
9   * (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14   * for more details.
15   */
16  
17  #include "qemu/osdep.h"
18  #include "qemu/timer.h"
19  
20  #include "libqtest.h"
21  #include "qapi/qmp/qdict.h"
22  
23  #define WTCR_OFFSET     0x1c
24  #define REF_HZ          (25000000)
25  
26  /* WTCR bit fields */
27  #define WTCLK(rv)       ((rv) << 10)
28  #define WTE             BIT(7)
29  #define WTIE            BIT(6)
30  #define WTIS(rv)        ((rv) << 4)
31  #define WTIF            BIT(3)
32  #define WTRF            BIT(2)
33  #define WTRE            BIT(1)
34  #define WTR             BIT(0)
35  
36  typedef struct Watchdog {
37      int irq;
38      uint64_t base_addr;
39  } Watchdog;
40  
41  static const Watchdog watchdog_list[] = {
42      {
43          .irq        = 47,
44          .base_addr  = 0xf0008000
45      },
46      {
47          .irq        = 48,
48          .base_addr  = 0xf0009000
49      },
50      {
51          .irq        = 49,
52          .base_addr  = 0xf000a000
53      }
54  };
55  
watchdog_index(const Watchdog * wd)56  static int watchdog_index(const Watchdog *wd)
57  {
58      ptrdiff_t diff = wd - watchdog_list;
59  
60      g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list));
61  
62      return diff;
63  }
64  
watchdog_read_wtcr(QTestState * qts,const Watchdog * wd)65  static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd)
66  {
67      return qtest_readl(qts, wd->base_addr + WTCR_OFFSET);
68  }
69  
watchdog_write_wtcr(QTestState * qts,const Watchdog * wd,uint32_t value)70  static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd,
71          uint32_t value)
72  {
73      qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value);
74  }
75  
watchdog_prescaler(QTestState * qts,const Watchdog * wd)76  static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd)
77  {
78      switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) {
79      case 0:
80          return 1;
81      case 1:
82          return 256;
83      case 2:
84          return 2048;
85      case 3:
86          return 65536;
87      default:
88          g_assert_not_reached();
89      }
90  }
91  
get_watchdog_action(QTestState * qts)92  static QDict *get_watchdog_action(QTestState *qts)
93  {
94      QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG");
95      QDict *data;
96  
97      data = qdict_get_qdict(ev, "data");
98      qobject_ref(data);
99      qobject_unref(ev);
100      return data;
101  }
102  
103  #define RESET_CYCLES 1024
watchdog_interrupt_cycles(QTestState * qts,const Watchdog * wd)104  static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd)
105  {
106      uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2);
107      return 1 << (14 + 2 * wtis);
108  }
109  
watchdog_calculate_steps(uint32_t count,uint32_t prescale)110  static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale)
111  {
112      return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale;
113  }
114  
watchdog_interrupt_steps(QTestState * qts,const Watchdog * wd)115  static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd)
116  {
117      return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd),
118              watchdog_prescaler(qts, wd));
119  }
120  
121  /* Check wtcr can be reset to default value */
test_init(gconstpointer watchdog)122  static void test_init(gconstpointer watchdog)
123  {
124      const Watchdog *wd = watchdog;
125      QTestState *qts = qtest_init("-machine quanta-gsj");
126  
127      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
128  
129      watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR);
130      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1));
131  
132      qtest_quit(qts);
133  }
134  
135  /* Check a watchdog can generate interrupt and reset actions */
test_reset_action(gconstpointer watchdog)136  static void test_reset_action(gconstpointer watchdog)
137  {
138      const Watchdog *wd = watchdog;
139      QTestState *qts = qtest_init("-machine quanta-gsj");
140      QDict *ad;
141  
142      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
143  
144      watchdog_write_wtcr(qts, wd,
145              WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR);
146      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
147              WTCLK(0) | WTE | WTRE | WTIE);
148  
149      /* Check a watchdog can generate an interrupt */
150      qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
151      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
152              WTCLK(0) | WTE | WTIF | WTIE | WTRE);
153      g_assert_true(qtest_get_irq(qts, wd->irq));
154  
155      /* Check a watchdog can generate a reset signal */
156      qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
157                  watchdog_prescaler(qts, wd)));
158      ad = get_watchdog_action(qts);
159      /* The signal is a reset signal */
160      g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset"));
161      qobject_unref(ad);
162      qtest_qmp_eventwait(qts, "RESET");
163      /*
164       * Make sure WTCR is reset to default except for WTRF bit which shouldn't
165       * be reset.
166       */
167      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF);
168      qtest_quit(qts);
169  }
170  
171  /* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */
test_prescaler(gconstpointer watchdog)172  static void test_prescaler(gconstpointer watchdog)
173  {
174      const Watchdog *wd = watchdog;
175      int inc = g_test_quick() ? 3 : 1;
176  
177      for (int wtclk = 0; wtclk < 4; wtclk += inc) {
178          for (int wtis = 0; wtis < 4; wtis += inc) {
179              QTestState *qts = qtest_init("-machine quanta-gsj");
180  
181              qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
182              watchdog_write_wtcr(qts, wd,
183                      WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR);
184              /*
185               * The interrupt doesn't fire until watchdog_interrupt_steps()
186               * cycles passed
187               */
188              qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1);
189              g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF);
190              g_assert_false(qtest_get_irq(qts, wd->irq));
191              qtest_clock_step(qts, 1);
192              g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
193              g_assert_true(qtest_get_irq(qts, wd->irq));
194  
195              qtest_quit(qts);
196          }
197      }
198  }
199  
200  /*
201   * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not
202   * set.
203   */
test_enabling_flags(gconstpointer watchdog)204  static void test_enabling_flags(gconstpointer watchdog)
205  {
206      const Watchdog *wd = watchdog;
207      QTestState *qts;
208      QDict *rsp;
209  
210      /* Neither WTIE or WTRE is set, no interrupt or reset should happen */
211      qts = qtest_init("-machine quanta-gsj");
212      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
213      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR);
214      qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
215      g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
216      g_assert_false(qtest_get_irq(qts, wd->irq));
217      qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
218                  watchdog_prescaler(qts, wd)));
219      g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
220      g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
221      qtest_quit(qts);
222  
223      /* Only WTIE is set, interrupt is triggered but reset should not happen */
224      qts = qtest_init("-machine quanta-gsj");
225      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
226      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
227      qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
228      g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
229      g_assert_true(qtest_get_irq(qts, wd->irq));
230      qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
231                  watchdog_prescaler(qts, wd)));
232      g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
233      g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
234      qtest_quit(qts);
235  
236      /* Only WTRE is set, interrupt is triggered but reset should not happen */
237      qts = qtest_init("-machine quanta-gsj");
238      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
239      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR);
240      qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
241      g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
242      g_assert_false(qtest_get_irq(qts, wd->irq));
243      qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
244                  watchdog_prescaler(qts, wd)));
245      rsp = get_watchdog_action(qts);
246      g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset"));
247      qobject_unref(rsp);
248      qtest_qmp_eventwait(qts, "RESET");
249      qtest_quit(qts);
250  
251      /*
252       * The case when both flags are set is already tested in
253       * test_reset_action().
254       */
255  }
256  
257  /* Check a watchdog can pause and resume by setting WTE bits */
test_pause(gconstpointer watchdog)258  static void test_pause(gconstpointer watchdog)
259  {
260      const Watchdog *wd = watchdog;
261      QTestState *qts;
262      int64_t remaining_steps, steps;
263  
264      qts = qtest_init("-machine quanta-gsj");
265      qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
266      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
267      remaining_steps = watchdog_interrupt_steps(qts, wd);
268      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
269  
270      /* Run for half of the execution period. */
271      steps = remaining_steps / 2;
272      remaining_steps -= steps;
273      qtest_clock_step(qts, steps);
274  
275      /* Pause the watchdog */
276      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE);
277      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
278  
279      /* Run for a long period of time, the watchdog shouldn't fire */
280      qtest_clock_step(qts, steps << 4);
281      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
282      g_assert_false(qtest_get_irq(qts, wd->irq));
283  
284      /* Resume the watchdog */
285      watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE);
286      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
287  
288      /* Run for the reset of the execution period, the watchdog should fire */
289      qtest_clock_step(qts, remaining_steps);
290      g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
291              WTCLK(0) | WTE | WTIF | WTIE);
292      g_assert_true(qtest_get_irq(qts, wd->irq));
293  
294      qtest_quit(qts);
295  }
296  
watchdog_add_test(const char * name,const Watchdog * wd,GTestDataFunc fn)297  static void watchdog_add_test(const char *name, const Watchdog* wd,
298          GTestDataFunc fn)
299  {
300      g_autofree char *full_name = g_strdup_printf(
301              "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name);
302      qtest_add_data_func(full_name, wd, fn);
303  }
304  #define add_test(name, td) watchdog_add_test(#name, td, test_##name)
305  
main(int argc,char ** argv)306  int main(int argc, char **argv)
307  {
308      g_test_init(&argc, &argv, NULL);
309      g_test_set_nonfatal_assertions();
310  
311      for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) {
312          const Watchdog *wd = &watchdog_list[i];
313  
314          add_test(init, wd);
315          add_test(reset_action, wd);
316          add_test(prescaler, wd);
317          add_test(enabling_flags, wd);
318          add_test(pause, wd);
319      }
320  
321      return g_test_run();
322  }
323