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