1 /* 2 * QTest testcase for the CMSDK APB watchdog device 3 * 4 * Copyright (c) 2021 Linaro Limited 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 "exec/hwaddr.h" 19 #include "qemu/bitops.h" 20 #include "libqtest-single.h" 21 22 #define WDOG_BASE 0x40000000 23 #define WDOG_BASE_MPS2 0x40008000 24 25 #define WDOGLOAD 0 26 #define WDOGVALUE 4 27 #define WDOGCONTROL 8 28 #define WDOGINTCLR 0xc 29 #define WDOGRIS 0x10 30 #define WDOGMIS 0x14 31 #define WDOGLOCK 0xc00 32 33 #define SSYS_BASE 0x400fe000 34 #define RCC 0x60 35 #define SYSDIV_SHIFT 23 36 #define SYSDIV_LENGTH 4 37 38 #define WDOGLOAD_DEFAULT 0xFFFFFFFF 39 #define WDOGVALUE_DEFAULT 0xFFFFFFFF 40 41 typedef struct CMSDKAPBWatchdogTestArgs { 42 int64_t tick; 43 hwaddr wdog_base; 44 const char *machine; 45 } CMSDKAPBWatchdogTestArgs; 46 47 enum { 48 MACHINE_LM3S811EVB, 49 MACHINE_MPS2_AN385, 50 }; 51 52 /* 53 * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz, 54 * which is 80ns per tick. 55 * 56 * IoTKit/ARMSSE dualtimer; driven at 25MHz in mps2-an385, so 40ns per tick 57 */ 58 static const CMSDKAPBWatchdogTestArgs machine_info[] = { 59 [MACHINE_LM3S811EVB] = { 60 .tick = 80, 61 .wdog_base = WDOG_BASE, 62 .machine = "lm3s811evb", 63 }, 64 [MACHINE_MPS2_AN385] = { 65 .tick = 40, 66 .wdog_base = WDOG_BASE_MPS2, 67 .machine = "mps2-an385", 68 }, 69 }; 70 71 static void test_watchdog(const void *ptr) 72 { 73 const CMSDKAPBWatchdogTestArgs *args = ptr; 74 hwaddr wdog_base = args->wdog_base; 75 int64_t tick = args->tick; 76 g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); 77 qtest_start(cmdline); 78 79 g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); 80 81 writel(wdog_base + WDOGCONTROL, 1); 82 writel(wdog_base + WDOGLOAD, 1000); 83 84 /* Step to just past the 500th tick */ 85 clock_step(500 * tick + 1); 86 g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); 87 g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); 88 89 /* Just past the 1000th tick: timer should have fired */ 90 clock_step(500 * tick); 91 g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); 92 g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 0); 93 94 /* VALUE reloads at following tick */ 95 clock_step(tick); 96 g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); 97 98 /* Writing any value to WDOGINTCLR clears the interrupt and reloads */ 99 clock_step(500 * tick); 100 g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); 101 g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); 102 writel(wdog_base + WDOGINTCLR, 0); 103 g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); 104 g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); 105 106 qtest_end(); 107 } 108 109 /* 110 * This test can only be executed in the stellaris board since it relies on a 111 * component of the board to change the clocking parameters of the watchdog. 112 */ 113 static void test_clock_change(const void *ptr) 114 { 115 uint32_t rcc; 116 const CMSDKAPBWatchdogTestArgs *args = ptr; 117 g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); 118 qtest_start(cmdline); 119 120 /* 121 * Test that writing to the stellaris board's RCC register to 122 * change the system clock frequency causes the watchdog 123 * to change the speed it counts at. 124 */ 125 g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); 126 127 writel(WDOG_BASE + WDOGCONTROL, 1); 128 writel(WDOG_BASE + WDOGLOAD, 1000); 129 130 /* Step to just past the 500th tick */ 131 clock_step(80 * 500 + 1); 132 g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); 133 g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); 134 135 /* Rewrite RCC.SYSDIV from 16 to 8, so the clock is now 40ns per tick */ 136 rcc = readl(SSYS_BASE + RCC); 137 g_assert_cmphex(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf); 138 rcc = deposit32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH, 7); 139 writel(SSYS_BASE + RCC, rcc); 140 141 /* Just past the 1000th tick: timer should have fired */ 142 clock_step(40 * 500); 143 g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); 144 145 g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0); 146 147 /* VALUE reloads at following tick */ 148 clock_step(41); 149 g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); 150 151 /* Writing any value to WDOGINTCLR clears the interrupt and reloads */ 152 clock_step(40 * 500); 153 g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); 154 g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); 155 writel(WDOG_BASE + WDOGINTCLR, 0); 156 g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); 157 g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); 158 159 qtest_end(); 160 } 161 162 int main(int argc, char **argv) 163 { 164 int r; 165 166 g_test_init(&argc, &argv, NULL); 167 g_test_set_nonfatal_assertions(); 168 169 if (qtest_has_machine(machine_info[MACHINE_LM3S811EVB].machine)) { 170 qtest_add_data_func("/cmsdk-apb-watchdog/watchdog", 171 &machine_info[MACHINE_LM3S811EVB], test_watchdog); 172 qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_clock_change", 173 &machine_info[MACHINE_LM3S811EVB], 174 test_clock_change); 175 } 176 if (qtest_has_machine(machine_info[MACHINE_MPS2_AN385].machine)) { 177 qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_mps2", 178 &machine_info[MACHINE_MPS2_AN385], test_watchdog); 179 } 180 181 r = g_test_run(); 182 183 return r; 184 } 185