xref: /qemu/tests/qtest/rtl8139-test.c (revision 74dcb2535ddb459cec3ab0c136924e39856686c3)
174769fe7SAndreas Färber /*
274769fe7SAndreas Färber  * QTest testcase for Realtek 8139 NIC
374769fe7SAndreas Färber  *
474769fe7SAndreas Färber  * Copyright (c) 2013-2014 SUSE LINUX Products GmbH
574769fe7SAndreas Färber  *
674769fe7SAndreas Färber  * This work is licensed under the terms of the GNU GPL, version 2 or later.
774769fe7SAndreas Färber  * See the COPYING file in the top-level directory.
874769fe7SAndreas Färber  */
974769fe7SAndreas Färber 
10681c28a3SPeter Maydell #include "qemu/osdep.h"
11dd210749SThomas Huth #include "libqtest-single.h"
12069bb583SFrediano Ziglio #include "libqos/pci-pc.h"
13e0cf11f3SAlberto Garcia #include "qemu/timer.h"
1474769fe7SAndreas Färber 
155ebecf20SThomas Huth static int verbosity_level;
165ebecf20SThomas Huth 
1774769fe7SAndreas Färber /* Tests only initialization so far. TODO: Replace with functional tests */
nop(void)1874769fe7SAndreas Färber static void nop(void)
1974769fe7SAndreas Färber {
2074769fe7SAndreas Färber }
2174769fe7SAndreas Färber 
2237b9ab92SLaurent Vivier #define CLK 33333333
23069bb583SFrediano Ziglio 
24069bb583SFrediano Ziglio static QPCIBus *pcibus;
25a186fedbSPhilippe Mathieu-Daudé static QPCIDevice *pcidev;
26b4ba67d9SDavid Gibson static QPCIBar dev_bar;
27069bb583SFrediano Ziglio 
save_fn(QPCIDevice * dev,int devfn,void * data)28069bb583SFrediano Ziglio static void save_fn(QPCIDevice *dev, int devfn, void *data)
29069bb583SFrediano Ziglio {
30069bb583SFrediano Ziglio     QPCIDevice **pdev = (QPCIDevice **) data;
31069bb583SFrediano Ziglio 
32069bb583SFrediano Ziglio     *pdev = dev;
33069bb583SFrediano Ziglio }
34069bb583SFrediano Ziglio 
get_device(void)35069bb583SFrediano Ziglio static QPCIDevice *get_device(void)
36069bb583SFrediano Ziglio {
37069bb583SFrediano Ziglio     QPCIDevice *dev;
38069bb583SFrediano Ziglio 
39143e6db6SEmanuele Giuseppe Esposito     pcibus = qpci_new_pc(global_qtest, NULL);
40069bb583SFrediano Ziglio     qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev);
41069bb583SFrediano Ziglio     g_assert(dev != NULL);
42069bb583SFrediano Ziglio 
43069bb583SFrediano Ziglio     return dev;
44069bb583SFrediano Ziglio }
45069bb583SFrediano Ziglio 
46069bb583SFrediano Ziglio #define PORT(name, len, val) \
47069bb583SFrediano Ziglio static unsigned __attribute__((unused)) in_##name(void) \
48069bb583SFrediano Ziglio { \
49a186fedbSPhilippe Mathieu-Daudé     unsigned res = qpci_io_read##len(pcidev, dev_bar, (val));     \
505ebecf20SThomas Huth     if (verbosity_level >= 2) { \
5113ee9e30SThomas Huth         g_test_message("*%s -> %x", #name, res); \
525ebecf20SThomas Huth     } \
53069bb583SFrediano Ziglio     return res; \
54069bb583SFrediano Ziglio } \
55069bb583SFrediano Ziglio static void out_##name(unsigned v) \
56069bb583SFrediano Ziglio { \
575ebecf20SThomas Huth     if (verbosity_level >= 2) { \
5813ee9e30SThomas Huth         g_test_message("%x -> *%s", v, #name); \
595ebecf20SThomas Huth     } \
60a186fedbSPhilippe Mathieu-Daudé     qpci_io_write##len(pcidev, dev_bar, (val), v);        \
61069bb583SFrediano Ziglio }
62069bb583SFrediano Ziglio 
63069bb583SFrediano Ziglio PORT(Timer, l, 0x48)
64069bb583SFrediano Ziglio PORT(IntrMask, w, 0x3c)
65069bb583SFrediano Ziglio PORT(IntrStatus, w, 0x3E)
66069bb583SFrediano Ziglio PORT(TimerInt, l, 0x54)
67069bb583SFrediano Ziglio 
68*74dcb253SPierrick Bouvier #define fatal(...) do { g_test_message(__VA_ARGS__); g_assert_not_reached(); } while (0)
69069bb583SFrediano Ziglio 
test_timer(void)70069bb583SFrediano Ziglio static void test_timer(void)
71069bb583SFrediano Ziglio {
72069bb583SFrediano Ziglio     const unsigned from = 0.95 * CLK;
73069bb583SFrediano Ziglio     const unsigned to = 1.6 * CLK;
74069bb583SFrediano Ziglio     unsigned prev, curr, next;
75069bb583SFrediano Ziglio     unsigned cnt, diff;
76069bb583SFrediano Ziglio 
77069bb583SFrediano Ziglio     out_IntrMask(0);
78069bb583SFrediano Ziglio 
79069bb583SFrediano Ziglio     in_IntrStatus();
80069bb583SFrediano Ziglio     in_Timer();
81069bb583SFrediano Ziglio     in_Timer();
82069bb583SFrediano Ziglio 
83069bb583SFrediano Ziglio     /* Test 1. test counter continue and continue */
84069bb583SFrediano Ziglio     out_TimerInt(0); /* disable timer */
85069bb583SFrediano Ziglio     out_IntrStatus(0x4000);
86069bb583SFrediano Ziglio     out_Timer(12345); /* reset timer to 0 */
87069bb583SFrediano Ziglio     curr = in_Timer();
88069bb583SFrediano Ziglio     if (curr > 0.1 * CLK) {
89069bb583SFrediano Ziglio         fatal("time too big %u\n", curr);
90069bb583SFrediano Ziglio     }
91069bb583SFrediano Ziglio     for (cnt = 0; ; ) {
9213566fe3SStefan Hajnoczi         clock_step(1 * NANOSECONDS_PER_SECOND);
93069bb583SFrediano Ziglio         prev = curr;
94069bb583SFrediano Ziglio         curr = in_Timer();
95069bb583SFrediano Ziglio 
96069bb583SFrediano Ziglio         /* test skip is in a specific range */
97069bb583SFrediano Ziglio         diff = (curr-prev) & 0xffffffffu;
98069bb583SFrediano Ziglio         if (diff < from || diff > to) {
99069bb583SFrediano Ziglio             fatal("Invalid diff %u (%u-%u)\n", diff, from, to);
100069bb583SFrediano Ziglio         }
101069bb583SFrediano Ziglio         if (curr < prev && ++cnt == 3) {
102069bb583SFrediano Ziglio             break;
103069bb583SFrediano Ziglio         }
104069bb583SFrediano Ziglio     }
105069bb583SFrediano Ziglio 
106069bb583SFrediano Ziglio     /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */
107069bb583SFrediano Ziglio     if (in_IntrStatus() & 0x4000) {
108069bb583SFrediano Ziglio         fatal("got an interrupt\n");
109069bb583SFrediano Ziglio     }
110069bb583SFrediano Ziglio 
111069bb583SFrediano Ziglio     /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */
112069bb583SFrediano Ziglio     out_TimerInt(1);
113069bb583SFrediano Ziglio     out_Timer(0);
114069bb583SFrediano Ziglio     clock_step(40);
115069bb583SFrediano Ziglio     if ((in_IntrStatus() & 0x4000) == 0) {
116069bb583SFrediano Ziglio         fatal("we should have an interrupt here!\n");
117069bb583SFrediano Ziglio     }
118069bb583SFrediano Ziglio 
119069bb583SFrediano Ziglio     /* Test 3. Check acknowledge */
120069bb583SFrediano Ziglio     out_IntrStatus(0x4000);
121069bb583SFrediano Ziglio     if (in_IntrStatus() & 0x4000) {
122069bb583SFrediano Ziglio         fatal("got an interrupt\n");
123069bb583SFrediano Ziglio     }
124069bb583SFrediano Ziglio 
125069bb583SFrediano Ziglio     /* Test. Status set after Timer reset */
126069bb583SFrediano Ziglio     out_Timer(0);
127069bb583SFrediano Ziglio     out_TimerInt(0);
128069bb583SFrediano Ziglio     out_IntrStatus(0x4000);
129069bb583SFrediano Ziglio     curr = in_Timer();
130069bb583SFrediano Ziglio     out_TimerInt(curr + 0.5 * CLK);
13113566fe3SStefan Hajnoczi     clock_step(1 * NANOSECONDS_PER_SECOND);
132069bb583SFrediano Ziglio     out_Timer(0);
133069bb583SFrediano Ziglio     if ((in_IntrStatus() & 0x4000) == 0) {
134069bb583SFrediano Ziglio         fatal("we should have an interrupt here!\n");
135069bb583SFrediano Ziglio     }
136069bb583SFrediano Ziglio 
137069bb583SFrediano Ziglio     /* Test. Status set after TimerInt reset */
138069bb583SFrediano Ziglio     out_Timer(0);
139069bb583SFrediano Ziglio     out_TimerInt(0);
140069bb583SFrediano Ziglio     out_IntrStatus(0x4000);
141069bb583SFrediano Ziglio     curr = in_Timer();
142069bb583SFrediano Ziglio     out_TimerInt(curr + 0.5 * CLK);
14313566fe3SStefan Hajnoczi     clock_step(1 * NANOSECONDS_PER_SECOND);
144069bb583SFrediano Ziglio     out_TimerInt(0);
145069bb583SFrediano Ziglio     if ((in_IntrStatus() & 0x4000) == 0) {
146069bb583SFrediano Ziglio         fatal("we should have an interrupt here!\n");
147069bb583SFrediano Ziglio     }
148069bb583SFrediano Ziglio 
149069bb583SFrediano Ziglio     /* Test 4. Increment TimerInt we should see an interrupt */
150069bb583SFrediano Ziglio     curr = in_Timer();
151069bb583SFrediano Ziglio     next = curr + 5.0 * CLK;
152069bb583SFrediano Ziglio     out_TimerInt(next);
153069bb583SFrediano Ziglio     for (cnt = 0; ; ) {
15413566fe3SStefan Hajnoczi         clock_step(1 * NANOSECONDS_PER_SECOND);
155069bb583SFrediano Ziglio         prev = curr;
156069bb583SFrediano Ziglio         curr = in_Timer();
157069bb583SFrediano Ziglio         diff = (curr-prev) & 0xffffffffu;
158069bb583SFrediano Ziglio         if (diff < from || diff > to) {
159069bb583SFrediano Ziglio             fatal("Invalid diff %u (%u-%u)\n", diff, from, to);
160069bb583SFrediano Ziglio         }
161069bb583SFrediano Ziglio         if (cnt < 3 && curr > next) {
162069bb583SFrediano Ziglio             if ((in_IntrStatus() & 0x4000) == 0) {
163069bb583SFrediano Ziglio                 fatal("we should have an interrupt here!\n");
164069bb583SFrediano Ziglio             }
165069bb583SFrediano Ziglio             out_IntrStatus(0x4000);
166069bb583SFrediano Ziglio             next = curr + 5.0 * CLK;
167069bb583SFrediano Ziglio             out_TimerInt(next);
168069bb583SFrediano Ziglio             if (++cnt == 3) {
169069bb583SFrediano Ziglio                 out_TimerInt(1);
170069bb583SFrediano Ziglio             }
171069bb583SFrediano Ziglio         /* Test 5. Second time we pass from 0 should see an interrupt */
172069bb583SFrediano Ziglio         } else if (cnt >= 3 && curr < prev) {
173069bb583SFrediano Ziglio             /* here we should have an interrupt */
174069bb583SFrediano Ziglio             if ((in_IntrStatus() & 0x4000) == 0) {
175069bb583SFrediano Ziglio                 fatal("we should have an interrupt here!\n");
176069bb583SFrediano Ziglio             }
177069bb583SFrediano Ziglio             out_IntrStatus(0x4000);
178069bb583SFrediano Ziglio             if (++cnt == 5) {
179069bb583SFrediano Ziglio                 break;
180069bb583SFrediano Ziglio             }
181069bb583SFrediano Ziglio         }
182069bb583SFrediano Ziglio     }
183069bb583SFrediano Ziglio 
18413ee9e30SThomas Huth     g_test_message("Everythink is ok!");
185069bb583SFrediano Ziglio }
186069bb583SFrediano Ziglio 
187069bb583SFrediano Ziglio 
test_init(void)188069bb583SFrediano Ziglio static void test_init(void)
189069bb583SFrediano Ziglio {
190069bb583SFrediano Ziglio     uint64_t barsize;
191069bb583SFrediano Ziglio 
192a186fedbSPhilippe Mathieu-Daudé     pcidev = get_device();
193069bb583SFrediano Ziglio 
194a186fedbSPhilippe Mathieu-Daudé     dev_bar = qpci_iomap(pcidev, 0, &barsize);
195069bb583SFrediano Ziglio 
196a186fedbSPhilippe Mathieu-Daudé     qpci_device_enable(pcidev);
197069bb583SFrediano Ziglio 
198069bb583SFrediano Ziglio     test_timer();
199069bb583SFrediano Ziglio }
200069bb583SFrediano Ziglio 
main(int argc,char ** argv)20174769fe7SAndreas Färber int main(int argc, char **argv)
20274769fe7SAndreas Färber {
20374769fe7SAndreas Färber     int ret;
2045ebecf20SThomas Huth     char *v_env = getenv("V");
2055ebecf20SThomas Huth 
2065ebecf20SThomas Huth     if (v_env) {
2075ebecf20SThomas Huth         verbosity_level = atoi(v_env);
2085ebecf20SThomas Huth     }
20974769fe7SAndreas Färber 
210ae4b01b3SRichard W.M. Jones     g_test_init(&argc, &argv, NULL);
211ae4b01b3SRichard W.M. Jones 
212f5af1dadSThomas Huth     if (!qtest_has_device("rtl8139")) {
213f5af1dadSThomas Huth         return 0;
214f5af1dadSThomas Huth     }
215f5af1dadSThomas Huth 
216e5d1730dSEric Blake     qtest_start("-device rtl8139");
217e5d1730dSEric Blake 
21874769fe7SAndreas Färber     qtest_add_func("/rtl8139/nop", nop);
219069bb583SFrediano Ziglio     qtest_add_func("/rtl8139/timer", test_init);
22074769fe7SAndreas Färber 
22174769fe7SAndreas Färber     ret = g_test_run();
22274769fe7SAndreas Färber 
22374769fe7SAndreas Färber     qtest_end();
22474769fe7SAndreas Färber 
22574769fe7SAndreas Färber     return ret;
22674769fe7SAndreas Färber }
227