master
c 421 lines 13.8 KB
Raw
1 /*
2 * QTests for FlexCAN CAN controller device model
3 *
4 * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include "libqtest-single.h"
11
12 #include "hw/net/flexcan.h"
13 #include "hw/net/can/flexcan_regs.h"
14
15 #define FSL_IMX6_CAN2_ADDR 0x02094000
16 #define FSL_IMX6_CAN2_SIZE 0x4000
17 #define FSL_IMX6_CAN1_ADDR 0x02090000
18 #define FSL_IMX6_CAN1_SIZE 0x4000
19
20 #define FC_QEMU_ARGS "-nographic -M sabrelite " \
21 "-object can-bus,id=qcan0 " \
22 "-machine canbus0=qcan0 -machine canbus1=qcan0"
23
24 /* used for masking out unused/reserved bits */
25 #define FC_MB_CNT_USED_MASK (~0xF080FFFFu)
26
27 #define FCREG(BASE_ADDR, REG) ((BASE_ADDR) + offsetof(FlexcanRegs, REG))
28 #define FCMB(BASE_ADDR, MB_IDX, WORD_IDX) (FCREG(BASE_ADDR, mbs) + \
29 0x10 * (MB_IDX) + 4 * (WORD_IDX))
30
31 typedef struct FcTestFrame {
32 uint32_t id;
33 uint32_t data[2];
34 uint8_t len;
35 bool ide;
36 bool rtr;
37
38 /* rx only */
39 bool expect_overrun;
40 } FcTestFrame;
41
42 static const FcTestFrame fc_test_frame_1 = {
43 .id = 0x5AF,
44 .len = 8,
45 .data = {
46 0x01020304,
47 0x0A0B0C0D
48 },
49 .ide = false
50 };
51
52 static const FcTestFrame fc_test_frame_1_ide = {
53 .id = 0x105AF5AF,
54 .len = 8,
55 .data = {
56 0x01020304,
57 0x0A0B0C0D
58 },
59 .ide = true
60 };
61
62 static void fc_reset(hwaddr ba, uint32_t mcr_flags)
63 {
64 /* disable */
65 writel(FCREG(ba, mcr), 0xD890000F);
66
67 /* enable in freeze mode */
68 writel(FCREG(ba, mcr), 0x5980000F);
69
70 /* soft reset */
71 writel(FCREG(ba, mcr), 0x5980000F | FLEXCAN_MCR_SOFTRST);
72
73 g_assert_cmpuint(readl(FCREG(ba, mcr)), ==, 0x5980000F);
74 g_assert_cmpuint(readl(FCREG(ba, ctrl)), ==, 0);
75 g_assert_cmpuint(readl(FCREG(ba, ctrl2)), ==, 0);
76
77 writel(FCREG(ba, mcr), (0x5980000F & ~FLEXCAN_MCR_HALT) | mcr_flags);
78 writel(FCREG(ba, ctrl2), FLEXCAN_CTRL2_RRS);
79
80 /* initialize all mailboxes as rx inactive */
81 for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
82 writel(FCMB(ba, i, 0), FLEXCAN_MB_CODE_RX_INACTIVE);
83 writel(FCMB(ba, i, 1), 0);
84 writel(FCMB(ba, i, 2), 0);
85 writel(FCMB(ba, i, 3), 0);
86 }
87 }
88
89 static uint64_t fc_get_irqs(hwaddr ba)
90 {
91 return (uint64_t)readl(FCREG(ba, iflag1)) |
92 ((uint64_t)readl(FCREG(ba, iflag2)) << 32);
93 }
94
95 static void fc_clear_irq(hwaddr ba, int idx)
96 {
97 if (idx >= 32) {
98 writel(FCREG(ba, iflag2), (uint32_t)1 << (idx - 32));
99 } else {
100 writel(FCREG(ba, iflag1), (uint32_t)1 << idx);
101 }
102
103 g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << idx), ==, 0);
104 }
105
106 static void fc_setup_rx_mb(hwaddr ba, int mbidx)
107 {
108 writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_RX_EMPTY);
109 writel(FCMB(ba, mbidx, 1), 0);
110 /* the data value should be ignored for RX mb */
111 writel(FCMB(ba, mbidx, 2), 0);
112 writel(FCMB(ba, mbidx, 3), 0);
113
114 g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==, FLEXCAN_MB_CODE_RX_EMPTY);
115 }
116
117 static void fc_tx(hwaddr ba, int mbidx, const FcTestFrame *frame)
118 {
119 g_assert_cmpuint(frame->len, <=, 8);
120
121 writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_INACTIVE);
122 uint32_t id = frame->ide ? frame->id : frame->id << 18;
123 writel(FCMB(ba, mbidx, 1), id);
124 writel(FCMB(ba, mbidx, 2), frame->data[0]);
125 writel(FCMB(ba, mbidx, 3), frame->data[1]);
126
127 uint32_t ctrl = FLEXCAN_MB_CODE_TX_DATA | FLEXCAN_MB_CNT_LENGTH(frame->len);
128 if (frame->ide) {
129 ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR;
130 }
131 if (frame->rtr) {
132 ctrl |= FLEXCAN_MB_CNT_RTR;
133 }
134 writel(FCMB(ba, mbidx, 0), ctrl);
135
136 /* check frame was transmitted */
137 g_assert_cmpuint(fc_get_irqs(ba) & ((uint64_t)1 << mbidx),
138 !=, 0);
139
140 uint32_t xpectd_ctrl = (ctrl & ~FLEXCAN_MB_CODE_MASK) |
141 FLEXCAN_MB_CODE_TX_INACTIVE;
142 g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK, ==,
143 xpectd_ctrl);
144 /* other fields should stay unchanged */
145 g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, id);
146 g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
147 g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
148 }
149
150 static void fc_rx_check(hwaddr ba, int mbidx, const FcTestFrame *frame)
151 {
152 uint32_t xpectd_ctrl = frame->expect_overrun ? FLEXCAN_MB_CODE_RX_OVERRUN
153 : FLEXCAN_MB_CODE_RX_FULL;
154 xpectd_ctrl |= FLEXCAN_MB_CNT_LENGTH(frame->len) | FLEXCAN_MB_CNT_SRR;
155 if (frame->ide) {
156 xpectd_ctrl |= FLEXCAN_MB_CNT_IDE;
157 }
158 if (frame->rtr) {
159 xpectd_ctrl |= FLEXCAN_MB_CNT_RTR;
160 }
161
162 uint32_t xpectd_id = frame->ide ? frame->id : frame->id << 18;
163
164 uint32_t ctrl = readl(FCMB(ba, mbidx, 0)) & FC_MB_CNT_USED_MASK;
165 if ((ctrl & FLEXCAN_MB_CODE_MASK) == FLEXCAN_MB_CODE_RX_EMPTY) {
166 fprintf(stderr, "expected frame (id=0x%X) not received\n", frame->id);
167 }
168
169 g_assert_cmpuint(ctrl, ==, xpectd_ctrl);
170 g_assert_cmpuint(readl(FCMB(ba, mbidx, 1)), ==, xpectd_id);
171 g_assert_cmpuint(readl(FCMB(ba, mbidx, 2)), ==, frame->data[0]);
172 g_assert_cmpuint(readl(FCMB(ba, mbidx, 3)), ==, frame->data[1]);
173 }
174
175 static void fc_check_empty_multi(hwaddr ba, int idx_count, int mbidxs[])
176 {
177 for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
178 bool check_empty = true;
179 int ctrl = readl(FCMB(ba, i, 0));
180
181 for (int j = 0; j < idx_count; j++) {
182 if (i == mbidxs[j]) {
183 check_empty = false;
184 }
185 }
186
187 if (check_empty) {
188 if (!(ctrl == FLEXCAN_MB_CODE_RX_EMPTY ||
189 ctrl == FLEXCAN_MB_CODE_RX_INACTIVE)) {
190 g_assert_cmpuint(ctrl, ==, FLEXCAN_MB_CODE_RX_INACTIVE);
191 }
192 g_assert_cmpuint(readl(FCMB(ba, i, 1)), ==, 0);
193 g_assert_cmpuint(readl(FCMB(ba, i, 2)), ==, 0);
194 g_assert_cmpuint(readl(FCMB(ba, i, 3)), ==, 0);
195 } else {
196 g_assert_cmpuint(
197 ctrl & FLEXCAN_MB_CODE_MASK, !=,
198 FLEXCAN_MB_CODE_RX_INACTIVE
199 );
200 }
201 }
202 }
203
204 static void fc_check_empty(hwaddr ba, int mbidx)
205 {
206 fc_check_empty_multi(ba, 1, &mbidx);
207 }
208
209 static void flexcan_test_linux_probe_impl(hwaddr fba)
210 {
211 /* -- test a Linux driver-like probe sequence -- */
212 /* disable */
213 writel(FCREG(fba, mcr), 0xD890000F);
214 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
215 g_assert_cmpuint(readl(FCREG(fba, ctrl)), ==, 0);
216
217 /* set bit in reserved field we do not implement (CTRL_CLK_SRC) */
218 writel(FCREG(fba, ctrl), 0x00002000);
219 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xD890000F);
220
221 /* enable in freeze mode */
222 writel(FCREG(fba, mcr), 0x5980000F);
223 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x5980000F);
224
225 /* enable Rx-FIFO */
226 writel(FCREG(fba, mcr), 0x7980000F);
227 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
228 g_assert_cmpuint(readl(FCREG(fba, ecr)), ==, 0);
229
230 /* disable */
231 writel(FCREG(fba, mcr), 0xF890000F);
232 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
233 }
234
235 static void flexcan_test_freeze_disable_interaction_impl(hwaddr fba)
236 {
237 /* -- test normal <=> freeze <=> disable transitions -- */
238
239 /* leave freeze in disabled, FRZ_ACK should stay cleared */
240 writel(FCREG(fba, mcr), 0xF890000F); /* disable */
241 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
242 writel(FCREG(fba, mcr), 0xB890000F); /* by clearing FRZ */
243 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xB890000F);
244
245 writel(FCREG(fba, mcr), 0xF890000F); /* disable */
246 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
247 writel(FCREG(fba, mcr), 0xE890000F); /* by clearing HALT */
248 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xE890000F);
249
250 writel(FCREG(fba, mcr), 0xF890000F); /* disable */
251 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xF890000F);
252 writel(FCREG(fba, mcr), 0xA890000F); /* by clearing both */
253 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0xA890000F);
254
255 /* enter and leave freeze */
256 writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
257 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
258 writel(FCREG(fba, mcr), 0x3980000F); /* leave by clearing FRZ */
259 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x3080000F);
260
261 writel(FCREG(fba, mcr), 0x7980000F); /* enable in freeze mode */
262 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x7980000F);
263 writel(FCREG(fba, mcr), 0x6980000F); /* leave by clearing HALT */
264 g_assert_cmpuint(readl(FCREG(fba, mcr)), ==, 0x6080000F);
265 }
266
267 static void flexcan_test_mailbox_io_impl(hwaddr ba_tx, hwaddr ba_rx)
268 {
269 /* -- test correct handling of mailbox IO -- */
270 const int test_1_mbidx = 0;
271 fc_reset(ba_tx,
272 FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
273 fc_reset(ba_rx,
274 FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
275
276 fc_setup_rx_mb(ba_rx, test_1_mbidx);
277 fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1_ide);
278 g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 1 << test_1_mbidx);
279 fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1_ide);
280 readl(FCREG(ba_rx, timer)); /* reset lock */
281
282 writel(FCMB(ba_rx, test_1_mbidx, 0), 0);
283 g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 0)), ==, 0);
284 writel(FCMB(ba_rx, test_1_mbidx, 1), 0x99AABBCC);
285 g_assert_cmpuint(readl(FCMB(ba_rx, test_1_mbidx, 1)), ==, 0x99AABBCC);
286 }
287
288 static void flexcan_test_dual_transmit_receive_impl(hwaddr ba_tx, hwaddr ba_rx)
289 {
290 /* -- test TX and RX between the two FlexCAN instances -- */
291 const int test_1_mbidx = 50;
292 const int test_rounds = 50;
293
294 /* self-receive enabled on tx FC */
295 fc_reset(ba_tx,
296 FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
297 fc_reset(ba_rx,
298 FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
299
300 /* tests self-receive on tx and reception on rx */
301 fc_setup_rx_mb(ba_rx, test_1_mbidx);
302 fc_check_empty(ba_rx, test_1_mbidx);
303 fc_setup_rx_mb(ba_tx, test_1_mbidx + 1);
304 fc_check_empty(ba_tx, test_1_mbidx + 1);
305 g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
306 g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
307
308 fc_tx(ba_tx, test_1_mbidx, &fc_test_frame_1);
309 fc_clear_irq(ba_tx, test_1_mbidx);
310
311 fc_rx_check(ba_rx, test_1_mbidx, &fc_test_frame_1);
312 fc_check_empty(ba_rx, test_1_mbidx);
313 fc_rx_check(ba_tx, test_1_mbidx + 1, &fc_test_frame_1);
314 int tx_non_empty_mbidxs[] = {test_1_mbidx, test_1_mbidx + 1};
315
316 fc_check_empty_multi(ba_tx, 2, tx_non_empty_mbidxs);
317 fc_clear_irq(ba_rx, test_1_mbidx);
318 fc_clear_irq(ba_tx, test_1_mbidx + 1);
319 readl(FCREG(ba_rx, timer)); /* reset lock */
320
321 for (int ridx = 0; ridx < test_rounds; ridx++) {
322 /* test extended IDs sent to all mailboxes */
323 for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
324 fc_setup_rx_mb(ba_rx, i);
325 }
326 fc_check_empty_multi(ba_rx, 0, NULL);
327 g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
328 g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
329
330 for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
331 fc_tx(ba_tx, i, &fc_test_frame_1_ide);
332 }
333 g_assert_cmpuint(fc_get_irqs(ba_rx), ==, UINT64_MAX);
334 g_assert_cmpuint(fc_get_irqs(ba_tx), ==, UINT64_MAX);
335 for (int i = 0; i < FLEXCAN_MAILBOX_COUNT; i++) {
336 fc_rx_check(ba_rx, i, &fc_test_frame_1_ide);
337 }
338
339 /* reset interrupts */
340 writel(FCREG(ba_rx, iflag1), UINT32_MAX);
341 writel(FCREG(ba_rx, iflag2), UINT32_MAX);
342 writel(FCREG(ba_tx, iflag1), UINT32_MAX);
343 writel(FCREG(ba_tx, iflag2), UINT32_MAX);
344 g_assert_cmpuint(fc_get_irqs(ba_rx), ==, 0);
345 g_assert_cmpuint(fc_get_irqs(ba_tx), ==, 0);
346 }
347 }
348
349 static void flexcan_test_tx_abort_impl(hwaddr ba)
350 {
351 /* -- test the TX abort feature -- */
352 fc_reset(ba,
353 FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_MAXMB(FLEXCAN_MAILBOX_COUNT));
354
355
356 for (int mbidx = 0; mbidx < FLEXCAN_MAILBOX_COUNT; mbidx++) {
357 fc_tx(ba, mbidx, &fc_test_frame_1);
358
359 writel(FCMB(ba, mbidx, 0), FLEXCAN_MB_CODE_TX_ABORT);
360 g_assert_cmpuint(readl(FCMB(ba, mbidx, 0)), ==,
361 FLEXCAN_MB_CODE_TX_INACTIVE);
362 }
363 }
364
365 static void flexcan_test_freeze_disable_interaction(void)
366 {
367 qtest_start(FC_QEMU_ARGS);
368 flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN1_ADDR);
369 flexcan_test_freeze_disable_interaction_impl(FSL_IMX6_CAN2_ADDR);
370 qtest_end();
371 }
372
373 static void flexcan_test_linux_probe(void)
374 {
375 qtest_start(FC_QEMU_ARGS);
376 flexcan_test_linux_probe_impl(FSL_IMX6_CAN1_ADDR);
377 flexcan_test_linux_probe_impl(FSL_IMX6_CAN2_ADDR);
378 qtest_end();
379 }
380
381 static void flexcan_test_dual_transmit_receive(void)
382 {
383 qtest_start(FC_QEMU_ARGS);
384 flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN1_ADDR,
385 FSL_IMX6_CAN2_ADDR);
386 flexcan_test_dual_transmit_receive_impl(FSL_IMX6_CAN2_ADDR,
387 FSL_IMX6_CAN1_ADDR);
388 qtest_end();
389 }
390
391 static void flexcan_test_tx_abort(void)
392 {
393 qtest_start(FC_QEMU_ARGS);
394 flexcan_test_tx_abort_impl(FSL_IMX6_CAN1_ADDR);
395 flexcan_test_tx_abort_impl(FSL_IMX6_CAN2_ADDR);
396 qtest_end();
397 }
398
399 static void flexcan_test_mailbox_io(void)
400 {
401 qtest_start(FC_QEMU_ARGS);
402 flexcan_test_mailbox_io_impl(FSL_IMX6_CAN1_ADDR, FSL_IMX6_CAN2_ADDR);
403 flexcan_test_mailbox_io_impl(FSL_IMX6_CAN2_ADDR, FSL_IMX6_CAN1_ADDR);
404 qtest_end();
405 }
406
407 int main(int argc, char **argv)
408 {
409 g_test_init(&argc, &argv, NULL);
410
411 qtest_add_func("/net/flexcan/linux_probe", flexcan_test_linux_probe);
412 qtest_add_func("/net/flexcan/freeze_disable_interaction",
413 flexcan_test_freeze_disable_interaction);
414 qtest_add_func("/net/flexcan/dual_transmit_receive",
415 flexcan_test_dual_transmit_receive);
416 qtest_add_func("/net/flexcan/tx_abort",
417 flexcan_test_tx_abort);
418 qtest_add_func("/net/flexcan/mailbox_io", flexcan_test_mailbox_io);
419
420 return g_test_run();
421 }