1 /* 2 * QTest testcase for Realtek 8139 NIC 3 * 4 * Copyright (c) 2013-2014 SUSE LINUX Products GmbH 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include <glib.h> 11 #include <string.h> 12 #include "libqtest.h" 13 #include "libqos/pci-pc.h" 14 #include "qemu/osdep.h" 15 #include "qemu/timer.h" 16 #include "qemu-common.h" 17 18 /* Tests only initialization so far. TODO: Replace with functional tests */ 19 static void nop(void) 20 { 21 } 22 23 #define CLK 33000000 24 25 static QPCIBus *pcibus; 26 static QPCIDevice *dev; 27 static void *dev_base; 28 29 static void save_fn(QPCIDevice *dev, int devfn, void *data) 30 { 31 QPCIDevice **pdev = (QPCIDevice **) data; 32 33 *pdev = dev; 34 } 35 36 static QPCIDevice *get_device(void) 37 { 38 QPCIDevice *dev; 39 40 pcibus = qpci_init_pc(); 41 qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); 42 g_assert(dev != NULL); 43 44 return dev; 45 } 46 47 #define PORT(name, len, val) \ 48 static unsigned __attribute__((unused)) in_##name(void) \ 49 { \ 50 unsigned res = qpci_io_read##len(dev, dev_base+(val)); \ 51 g_test_message("*%s -> %x\n", #name, res); \ 52 return res; \ 53 } \ 54 static void out_##name(unsigned v) \ 55 { \ 56 g_test_message("%x -> *%s\n", v, #name); \ 57 qpci_io_write##len(dev, dev_base+(val), v); \ 58 } 59 60 PORT(Timer, l, 0x48) 61 PORT(IntrMask, w, 0x3c) 62 PORT(IntrStatus, w, 0x3E) 63 PORT(TimerInt, l, 0x54) 64 65 #define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) 66 67 static void test_timer(void) 68 { 69 const unsigned from = 0.95 * CLK; 70 const unsigned to = 1.6 * CLK; 71 unsigned prev, curr, next; 72 unsigned cnt, diff; 73 74 out_IntrMask(0); 75 76 in_IntrStatus(); 77 in_Timer(); 78 in_Timer(); 79 80 /* Test 1. test counter continue and continue */ 81 out_TimerInt(0); /* disable timer */ 82 out_IntrStatus(0x4000); 83 out_Timer(12345); /* reset timer to 0 */ 84 curr = in_Timer(); 85 if (curr > 0.1 * CLK) { 86 fatal("time too big %u\n", curr); 87 } 88 for (cnt = 0; ; ) { 89 clock_step(1 * NSEC_PER_SEC); 90 prev = curr; 91 curr = in_Timer(); 92 93 /* test skip is in a specific range */ 94 diff = (curr-prev) & 0xffffffffu; 95 if (diff < from || diff > to) { 96 fatal("Invalid diff %u (%u-%u)\n", diff, from, to); 97 } 98 if (curr < prev && ++cnt == 3) { 99 break; 100 } 101 } 102 103 /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */ 104 if (in_IntrStatus() & 0x4000) { 105 fatal("got an interrupt\n"); 106 } 107 108 /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */ 109 out_TimerInt(1); 110 out_Timer(0); 111 clock_step(40); 112 if ((in_IntrStatus() & 0x4000) == 0) { 113 fatal("we should have an interrupt here!\n"); 114 } 115 116 /* Test 3. Check acknowledge */ 117 out_IntrStatus(0x4000); 118 if (in_IntrStatus() & 0x4000) { 119 fatal("got an interrupt\n"); 120 } 121 122 /* Test. Status set after Timer reset */ 123 out_Timer(0); 124 out_TimerInt(0); 125 out_IntrStatus(0x4000); 126 curr = in_Timer(); 127 out_TimerInt(curr + 0.5 * CLK); 128 clock_step(1 * NSEC_PER_SEC); 129 out_Timer(0); 130 if ((in_IntrStatus() & 0x4000) == 0) { 131 fatal("we should have an interrupt here!\n"); 132 } 133 134 /* Test. Status set after TimerInt reset */ 135 out_Timer(0); 136 out_TimerInt(0); 137 out_IntrStatus(0x4000); 138 curr = in_Timer(); 139 out_TimerInt(curr + 0.5 * CLK); 140 clock_step(1 * NSEC_PER_SEC); 141 out_TimerInt(0); 142 if ((in_IntrStatus() & 0x4000) == 0) { 143 fatal("we should have an interrupt here!\n"); 144 } 145 146 /* Test 4. Increment TimerInt we should see an interrupt */ 147 curr = in_Timer(); 148 next = curr + 5.0 * CLK; 149 out_TimerInt(next); 150 for (cnt = 0; ; ) { 151 clock_step(1 * NSEC_PER_SEC); 152 prev = curr; 153 curr = in_Timer(); 154 diff = (curr-prev) & 0xffffffffu; 155 if (diff < from || diff > to) { 156 fatal("Invalid diff %u (%u-%u)\n", diff, from, to); 157 } 158 if (cnt < 3 && curr > next) { 159 if ((in_IntrStatus() & 0x4000) == 0) { 160 fatal("we should have an interrupt here!\n"); 161 } 162 out_IntrStatus(0x4000); 163 next = curr + 5.0 * CLK; 164 out_TimerInt(next); 165 if (++cnt == 3) { 166 out_TimerInt(1); 167 } 168 /* Test 5. Second time we pass from 0 should see an interrupt */ 169 } else if (cnt >= 3 && curr < prev) { 170 /* here we should have an interrupt */ 171 if ((in_IntrStatus() & 0x4000) == 0) { 172 fatal("we should have an interrupt here!\n"); 173 } 174 out_IntrStatus(0x4000); 175 if (++cnt == 5) { 176 break; 177 } 178 } 179 } 180 181 g_test_message("Everythink is ok!\n"); 182 } 183 184 185 static void test_init(void) 186 { 187 uint64_t barsize; 188 189 dev = get_device(); 190 191 dev_base = qpci_iomap(dev, 0, &barsize); 192 193 g_assert(dev_base != NULL); 194 195 qpci_device_enable(dev); 196 197 test_timer(); 198 } 199 200 int main(int argc, char **argv) 201 { 202 int ret; 203 204 g_test_init(&argc, &argv, NULL); 205 qtest_add_func("/rtl8139/nop", nop); 206 qtest_add_func("/rtl8139/timer", test_init); 207 208 qtest_start("-device rtl8139"); 209 ret = g_test_run(); 210 211 qtest_end(); 212 213 return ret; 214 } 215