@samitouri / QOSamiQemu / commits / 3c4e23cbb5

hw/net/can/flexcan: NXP FlexCAN core emulation

Added the FlexCAN2 emulator implementation core, with CAN_FLEXCAN Kconfig flag and MAINTAINERS entry. FlexCAN2 version can be found in i.MX6 SoCs and others. More information about the implementation can be found in [1]. Some macro and struct defintions were borrowed from the Linux kernel. The original authors agreed with relicensing them to GPL-2.0-or-later on the qemu-devel mailing list. [1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf Signed-off-by: Matyáš Bobek <matyas.bobek@gmail.com> Signed-off-by: Pavel Pisa <pisa@fel.cvut.cz> Tested-by: Pavel Pisa <pisa@fel.cvut.cz> Reviewed-by: Bernhard Beschow <shentey@gmail.com> Reviewed-by: Pavel Pisa <pisa@fel.cvut.cz> Message-id: 03dc62ff8013bb946aab8f64e51638b810629529.1782140438.git.matyas.bobek@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Matyáš Bobek committed Jun 22, 2026 at 18:08 UTC 3c4e23cbb597d299a1b6e6e8918e0489f40d8024
7 files changed +1770
MAINTAINERS
+8
@@ -2107,6 +2107,14 @@ F: hw/net/can/xlnx-*
2107 F: include/hw/net/xlnx-*
2108 F: tests/qtest/xlnx-can*-test*
2109
2110 +FlexCAN
2111 +M: Matyas Bobek <matyas.bobek@gmail.com>
2112 +M: Pavel Pisa <pisa@cmp.felk.cvut.cz>
2113 +S: Maintained
2114 +F: hw/net/can/flexcan.c
2115 +F: hw/net/can/flexcan_regs.h
2116 +F: include/hw/net/flexcan.h
2117 +
2118 EDU
2119 M: Jiri Slaby <jslaby@suse.cz>
2120 S: Maintained
hw/net/Kconfig
+5
@@ -157,3 +157,8 @@ config CAN_CTUCANFD_PCI
157 default y if PCI_DEVICES
158 depends on PCI && CAN_CTUCANFD
159 select CAN_BUS
160 +
161 +config CAN_FLEXCAN
162 + bool
163 + depends on IMX
164 + select CAN_BUS
hw/net/can/flexcan.c new
+1396
@@ -0,0 +1,1396 @@
1 +/*
2 + * QEMU model of the NXP FLEXCAN device.
3 + *
4 + * This implementation is based on the following reference manual:
5 + * i.MX 6Dual/6Quad Applications Processor Reference Manual
6 + * Document Number: IMX6DQRM, Rev. 6, 05/2020
7 + *
8 + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
9 + *
10 + * Based on CTU CAN FD emulation implemented by Jan Charvat.
11 + *
12 + * SPDX-License-Identifier: GPL-2.0-or-later
13 + */
14 +
15 +#include "qemu/osdep.h"
16 +#include "qemu/log.h"
17 +#include "hw/core/sysbus.h"
18 +#include "qapi/error.h"
19 +#include "hw/core/irq.h"
20 +#include "migration/vmstate.h"
21 +#include "net/can_emu.h"
22 +#include "hw/core/qdev-properties.h"
23 +#include "trace.h"
24 +
25 +#include "hw/net/flexcan.h"
26 +#include "flexcan_regs.h"
27 +#include "qemu/timer.h"
28 +
29 +/*
30 + * Indicates MB w/ received frame has not been serviced yet
31 + * This is an emulator-only flag in position of unused (reserved) bit
32 + * of message buffer control register
33 + */
34 +#define FLEXCAN_MB_CNT_NOT_SRV BIT(23)
35 +/**
36 + * if no MB is locked, FlexcanState.locked_mb
37 + * is set to FLEXCAN_NO_MB_LOCKED
38 + */
39 +#define FLEXCAN_NO_MB_LOCKED -1
40 +/**
41 + * if no frame is waiting in the SMB, FlexcanState.smb_target_mbid
42 + * is set to FLEXCAN_SMB_EMPTY
43 + */
44 +#define FLEXCAN_SMB_EMPTY -1
45 +/**
46 + * When the module is disabled or in freeze mode,
47 + * the timer is not running. That is indicated by setting
48 + * FlexcanState.timer_start to FLEXCAN_TIMER_STOPPED.
49 + */
50 +#define FLEXCAN_TIMER_STOPPED -1
51 +
52 +/* These constants are returned by flexcan_fifo_rx() and flexcan_mb_rx(), */
53 +enum FlexcanRx {
54 +/* Retry the other receiving mechanism (ie. message bufer or mailbox). */
55 + FLEXCAN_RX_SEARCH_RETRY,
56 +/* The frame was received and stored. */
57 + FLEXCAN_RX_SEARCH_ACCEPT,
58 +/* The frame was filtered out and dropped. */
59 + FLEXCAN_RX_SEARCH_DROPPED,
60 +};
61 +
62 +/*
63 + * These constants are returned by flexcan_mb_rx_check_mb().
64 + * See flexcan_mb_rx_check_mb() kerneldoc for details.
65 + */
66 +enum FlexcanCheck {
67 + FLEXCAN_CHECK_MB_NIL = 0,
68 + FLEXCAN_CHECK_MB_MATCH = 3,
69 + FLEXCAN_CHECK_MB_MATCH_NON_FREE = 1,
70 + FLEXCAN_CHECK_MB_MATCH_LOCKED = 5,
71 +};
72 +
73 +static const FlexcanRegs flexcan_regs_write_mask = {
74 + .mcr = 0xF6EB337F,
75 + .ctrl = 0xFFFFFFFF,
76 + .timer = 0xFFFFFFFF,
77 + .tcr = 0xFFFFFFFF,
78 + .rxmgmask = 0xFFFFFFFF,
79 + .rx14mask = 0xFFFFFFFF,
80 + .rx15mask = 0xFFFFFFFF,
81 + .ecr = 0xFFFFFFFF,
82 + .esr = 0xFFFFFFFF,
83 + .imask2 = 0xFFFFFFFF,
84 + .imask1 = 0xFFFFFFFF,
85 + .iflag2 = 0,
86 + .iflag1 = 0,
87 + .ctrl2 = 0xFFFFFFFF,
88 + .esr2 = 0,
89 + .imeur = 0,
90 + .lrfr = 0,
91 + .crcr = 0,
92 + .rxfgmask = 0xFFFFFFFF,
93 + .rxfir = 0,
94 + .cbt = 0,
95 + ._reserved2 = 0,
96 + .dbg1 = 0,
97 + .dbg2 = 0,
98 + .mbs = { [0 ... 63] = {
99 + .can_ctrl = 0xFFFFFFFF & ~FLEXCAN_MB_CNT_NOT_SRV,
100 + .can_id = 0xFFFFFFFF,
101 + .data = { 0xFFFFFFFF, 0xFFFFFFFF },
102 + } },
103 + ._reserved4 = {0},
104 + .rximr = { [0 ... 63] = 0xFFFFFFFF },
105 + ._reserved5 = {0},
106 + .gfwr_mx6 = 0xFFFFFFFF,
107 + ._reserved6 = {0},
108 + ._reserved8 = {0},
109 + .rx_smb0_raw = {0, 0, 0, 0},
110 + .rx_smb1 = {0, 0, 0, 0},
111 +};
112 +static const FlexcanRegs flexcan_regs_reset_mask = {
113 + .mcr = 0x80000000,
114 + .ctrl = 0xFFFFFFFF,
115 + .timer = 0,
116 + .tcr = 0,
117 + .rxmgmask = 0xFFFFFFFF,
118 + .rx14mask = 0xFFFFFFFF,
119 + .rx15mask = 0xFFFFFFFF,
120 + .ecr = 0,
121 + .esr = 0,
122 + .imask2 = 0,
123 + .imask1 = 0,
124 + .iflag2 = 0,
125 + .iflag1 = 0,
126 + .ctrl2 = 0xFFFFFFFF,
127 + .esr2 = 0,
128 + .imeur = 0,
129 + .lrfr = 0,
130 + .crcr = 0,
131 + .rxfgmask = 0xFFFFFFFF,
132 + .rxfir = 0xFFFFFFFF,
133 + .cbt = 0,
134 + ._reserved2 = 0,
135 + .dbg1 = 0,
136 + .dbg2 = 0,
137 + .mb = {0xFFFFFFFF},
138 + ._reserved4 = {0},
139 + .rximr = {0xFFFFFFFF},
140 + ._reserved5 = {0},
141 + .gfwr_mx6 = 0,
142 + ._reserved6 = {0},
143 + ._reserved8 = {0},
144 + .rx_smb0_raw = {0, 0, 0, 0},
145 + .rx_smb1 = {0, 0, 0, 0},
146 +};
147 +
148 +/* length of buffer used to format register names in trace output */
149 +#define FLEXCAN_DBG_BUF_LEN 16
150 +
151 +/**
152 + * flexcan_dbg_mb_code_strs - Readable names for CODE field codes
153 + *
154 + * Readable names for possible values of CODE field in message buffer
155 + * control word.
156 + */
157 +static const char *flexcan_dbg_mb_code_strs[16] = {
158 + "INACTIVE_RX",
159 + "FULL",
160 + "EMPTY",
161 + "OVERRUN",
162 + "INACTIVE_TX",
163 + "RANSWER",
164 + "DATA",
165 + "TANSWER"
166 +};
167 +
168 +/**
169 + * flexcan_dbg_mb_code() - Get the string representation of a mailbox code
170 + * @mb_ctrl: The mailbox control register value
171 + * @buf: The buffer to store the string representation
172 + *
173 + * Return: Either constant string or string formatted into @buf
174 + */
175 +static const char *flexcan_dbg_mb_code(uint32_t mb_ctrl, char *buf)
176 +{
177 + uint32_t code = mb_ctrl & FLEXCAN_MB_CODE_MASK;
178 + uint32_t code_idx = code >> 24;
179 + if (code == FLEXCAN_MB_CODE_TX_ABORT) {
180 + return "ABORT";
181 + } else {
182 + const char *code_str = flexcan_dbg_mb_code_strs[code_idx >> 1];
183 + if (code_idx & 1) {
184 + g_snprintf(buf, FLEXCAN_DBG_BUF_LEN, "%s+BUSY", code_str);
185 + return buf;
186 + }
187 +
188 + return code_str;
189 + }
190 +}
191 +
192 +static const char *flexcan_dbg_reg_name_fixed(hwaddr addr)
193 +{
194 + switch (addr) {
195 + case offsetof(FlexcanRegs, mcr):
196 + return "MCR";
197 + case offsetof(FlexcanRegs, ctrl):
198 + return "CTRL";
199 + case offsetof(FlexcanRegs, timer):
200 + return "TIMER";
201 + case offsetof(FlexcanRegs, esr):
202 + return "ESR";
203 + case offsetof(FlexcanRegs, rxmgmask):
204 + return "RXMGMASK";
205 + case offsetof(FlexcanRegs, rx14mask):
206 + return "RX14MASK";
207 + case offsetof(FlexcanRegs, rx15mask):
208 + return "RX15MASK";
209 + case offsetof(FlexcanRegs, rxfgmask):
210 + return "RXFGMASK";
211 + case offsetof(FlexcanRegs, ecr):
212 + return "ECR";
213 + case offsetof(FlexcanRegs, ctrl2):
214 + return "CTRL2";
215 + case offsetof(FlexcanRegs, imask2):
216 + return "IMASK2";
217 + case offsetof(FlexcanRegs, imask1):
218 + return "IMASK1";
219 + case offsetof(FlexcanRegs, iflag2):
220 + return "IFLAG2";
221 + case offsetof(FlexcanRegs, iflag1):
222 + return "IFLAG1";
223 + }
224 + return NULL;
225 +}
226 +
227 +static inline void flexcan_trace_mem_op(FlexcanState *s, hwaddr addr,
228 + uint32_t value, int size, bool is_wr)
229 +{
230 + if (trace_event_get_state_backends(TRACE_FLEXCAN_MEM_OP)) {
231 + const char *reg_name = "unknown";
232 + char reg_name_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
233 + const char *reg_name_fixed = flexcan_dbg_reg_name_fixed(addr);
234 + const char *op_string = is_wr ? "write" : "read";
235 +
236 + if (reg_name_fixed) {
237 + reg_name = reg_name_fixed;
238 + } else if (addr >= 0x80 && addr < 0x480) {
239 + int mbidx = (addr - 0x80) / 16;
240 + g_snprintf(reg_name_buf, sizeof(reg_name_buf), "MB%i", mbidx);
241 + reg_name = reg_name_buf;
242 + } else if (addr >= 0x880 && addr < 0x9e0) {
243 + int id = (addr - 0x880) / 4;
244 + g_snprintf(reg_name_buf, sizeof(reg_name_buf), "RXIMR%i", id);
245 + reg_name = reg_name_buf;
246 + }
247 +
248 + trace_flexcan_mem_op(DEVICE(s)->canonical_path, op_string, value, addr,
249 + reg_name, size);
250 + }
251 +}
252 +
253 +static enum FlexcanRx flexcan_mb_rx(FlexcanState *s,
254 + const qemu_can_frame *frame);
255 +static void flexcan_mb_unlock(FlexcanState *s);
256 +
257 +/* ========== Mailbox Utils ========== */
258 +
259 +/**
260 + * flexcan_mailbox_count() - Get number of enabled mailboxes
261 + * @s: FlexCAN device pointer
262 + *
263 + * Count is based on MCR[MAXMB] field. Note that some of those mailboxes
264 + * might be part of queue or queue ID filters or ordinary message buffers.
265 + */
266 +static inline int flexcan_enabled_mailbox_count(const FlexcanState *s)
267 +{
268 + return MIN((s->regs.mcr & FLEXCAN_MCR_MAXMB(UINT32_MAX)) + 1,
269 + FLEXCAN_MAILBOX_COUNT);
270 +}
271 +
272 +/**
273 + * flexcan_get_first_message_buffer() - Get pointer to first message buffer
274 + * @s: FlexCAN device pointer
275 + *
276 + * In context of this function, message buffer means a mailbox which is not
277 + * a queue element nor a queue filter. Note this function does not take
278 + * MCR[MAXMB] into account, meaning that the returned mailbox
279 + * might be disabled.
280 + */
281 +static FlexcanRegsMessageBuffer *flexcan_get_first_message_buffer(
282 + FlexcanState *s)
283 +{
284 + if (s->regs.mcr & FLEXCAN_MCR_FEN) {
285 + int rffn = (s->regs.ctrl2 & FLEXCAN_CTRL2_RFFN(UINT32_MAX)) >> 24;
286 + return s->regs.mbs + 8 + 2 * rffn;
287 + }
288 +
289 + return s->regs.mbs;
290 +}
291 +
292 +/**
293 + * flexcan_get_last_enabled_mailbox() - Get pointer to last enabled mailbox.
294 + * @s: FlexCAN device pointer
295 + *
296 + * When used with flexcan_get_first_message_buffer(), all mailboxes *ptr in
297 + * range `first_message_buffer() <= ptr <= last_enabled_mailbox` are valid
298 + * message buffer mailboxes.
299 + *
300 + * Return: Last enabled mailbox in MCR[MAXMB] sense. The mailbox might be
301 + * of any type.
302 + */
303 +static inline FlexcanRegsMessageBuffer *flexcan_get_last_enabled_mailbox(
304 + FlexcanState *s)
305 +{
306 + return s->regs.mbs + flexcan_enabled_mailbox_count(s);
307 +}
308 +
309 +/* ========== Free-running Timer ========== */
310 +static inline int64_t flexcan_get_time(void)
311 +{
312 + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
313 +}
314 +
315 +/**
316 + * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)
317 + * @s: FlexCAN device pointer
318 + *
319 + * The bitrate is determined by FlexCAN configuration in CTRL1 register,
320 + * and CCM co
321 + */
322 +static uint32_t flexcan_get_bitrate(FlexcanState *s)
323 +{
324 + uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 24;
325 + uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;
326 + uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;
327 + uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;
328 +
329 + /* N of time quanta for segments */
330 + uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;
331 + uint32_t tseg2 = 1 + conf_pseg2;
332 + uint32_t total_qpb = 1 + tseg1 + tseg2;
333 +
334 + uint32_t pe_freq, s_freq, bitrate;
335 +
336 + assert(s->ccm);
337 +
338 + /* s_freq: CAN clock from CCM divided by the prescaler */
339 + pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);
340 + s_freq = pe_freq / (1 + conf_presdiv);
341 + bitrate = s_freq / total_qpb;
342 +
343 + trace_flexcan_get_bitrate(DEVICE(s)->canonical_path, pe_freq,
344 + 1 + conf_presdiv, s_freq, tseg1, tseg2, total_qpb,
345 + bitrate);
346 + return bitrate;
347 +}
348 +
349 +/**
350 + * int128_mul_6464() - Multiply two 64-bit integers into a 128-bit one
351 + */
352 +static Int128 int128_muls_6464(int64_t ai, int64_t bi)
353 +{
354 + uint64_t l, h;
355 +
356 + muls64(&l, &h, ai, bi);
357 + return int128_make128(l, h);
358 +}
359 +
360 +/**
361 + * flexcan_get_timestamp() - Get current value of the 16-bit free-running timer
362 + * @s: FlexCAN device pointer
363 + * @mk_unique: if true, make the timestamp unique by incrementing it if needed
364 + */
365 +static uint32_t flexcan_get_timestamp(FlexcanState *s, bool mk_unique)
366 +{
367 + const Int128 nanoseconds_in_second = int128_makes64((int64_t)1e9);
368 + Int128 ncycles, cycles128;
369 + int64_t current_time, elapsed_time_ns;
370 + uint64_t cycles;
371 + uint32_t rv, shift = 0;
372 +
373 + if (s->timer_start == FLEXCAN_TIMER_STOPPED) {
374 + /* timer is not running, return last value */
375 + trace_flexcan_get_timestamp(DEVICE(s)->canonical_path, -1, 0, 0, 0,
376 + s->regs.timer);
377 + return s->regs.timer;
378 + }
379 +
380 + current_time = flexcan_get_time();
381 + elapsed_time_ns = current_time - s->timer_start;
382 + if (elapsed_time_ns < 0) {
383 + trace_flexcan_timer_overflow(DEVICE(s)->canonical_path, current_time,
384 + s->timer_start, elapsed_time_ns);
385 + return 0xFFFF;
386 + }
387 +
388 + ncycles = int128_muls_6464(s->timer_freq, elapsed_time_ns);
389 + cycles128 = int128_divs(ncycles, nanoseconds_in_second);
390 + /* 64 bits hold for over 50k years at 10MHz */
391 + cycles = int128_getlo(cycles128);
392 +
393 + if (mk_unique && cycles <= s->last_rx_timer_cycles) {
394 + shift = 1;
395 + cycles = s->last_rx_timer_cycles + shift;
396 + }
397 +
398 + s->last_rx_timer_cycles = cycles;
399 + rv = (uint32_t)cycles & 0xFFFF;
400 +
401 + trace_flexcan_get_timestamp(DEVICE(s)->canonical_path,
402 + elapsed_time_ns / (uint32_t)1e6,
403 + s->timer_freq, cycles, shift, rv);
404 + return rv;
405 +}
406 +
407 +/**
408 + * flexcan_timer_start() - Start the free-running timer
409 + * @s: FlexCAN device pointer
410 + *
411 + * This should be called when the module leaves freeze mode.
412 + */
413 +static void flexcan_timer_start(FlexcanState *s)
414 +{
415 + s->timer_freq = flexcan_get_bitrate(s);
416 + s->timer_start = flexcan_get_time();
417 + s->last_rx_timer_cycles = 0;
418 +
419 + trace_flexcan_timer_start(DEVICE(s)->canonical_path, s->timer_freq,
420 + s->regs.timer);
421 +}
422 +
423 +/**
424 + * flexcan_timer_stop() - Stop the free-running timer
425 + * @s: FlexCAN device pointer
426 + *
427 + * This should be called when the module enters freeze mode.
428 + * Stores the current timestamp in the TIMER register.
429 + */
430 +static void flexcan_timer_stop(FlexcanState *s)
431 +{
432 + s->regs.timer = flexcan_get_timestamp(s, false);
433 + s->timer_start = FLEXCAN_TIMER_STOPPED;
434 +
435 + trace_flexcan_timer_stop(DEVICE(s)->canonical_path, s->timer_freq,
436 + s->regs.timer);
437 +}
438 +
439 +/* ========== IRQ handling ========== */
440 +/**
441 + * flexcan_irq_update() - Update qemu_irq line based on interrupt registers
442 + * @s: FlexCAN device pointer
443 + */
444 +static void flexcan_irq_update(FlexcanState *s)
445 +{
446 + uint32_t mb_irqs[2];
447 + int irq_pending;
448 + /* these are all interrupt sources from FlexCAN */
449 + /* mailbox interrupt sources */
450 + mb_irqs[0] = s->regs.iflag1 & s->regs.imask1;
451 + mb_irqs[1] = s->regs.iflag2 & s->regs.imask2;
452 +
453 + /**
454 + * these interrupts aren't currently used and they can never be raised
455 + *
456 + * bool irq_wake_up = (s->regs.mcr & FLEXCAN_MCR_WAK_MSK) &&
457 + * (s->regs.ecr & FLEXCAN_ESR_WAK_INT);
458 + * bool irq_bus_off = (s->regs.ctrl & FLEXCAN_CTRL_BOFF_MSK) &&
459 + * (s->regs.ecr & FLEXCAN_ESR_BOFF_INT);
460 + * bool irq_error = (s->regs.ctrl & FLEXCAN_CTRL_ERR_MSK) &&
461 + * (s->regs.ecr & FLEXCAN_ESR_ERR_INT);
462 + * bool irq_tx_warn = (s->regs.ctrl & FLEXCAN_CTRL_TWRN_MSK) &&
463 + * (s->regs.ecr & FLEXCAN_ESR_TWRN_INT);
464 + * bool irq_rx_warn = (s->regs.ctrl & FLEXCAN_CTRL_RWRN_MSK) &&
465 + * (s->regs.ecr & FLEXCAN_ESR_RWRN_INT);
466 + */
467 +
468 + irq_pending = (mb_irqs[0] || mb_irqs[1]) ? 1 : 0;
469 + trace_flexcan_irq_update(DEVICE(s)->canonical_path, mb_irqs[0], mb_irqs[1],
470 + irq_pending);
471 +
472 + qemu_set_irq(s->irq, irq_pending);
473 +}
474 +
475 +/**
476 + * flexcan_irq_iflag_set() - Set IFLAG bit corresponding to MB mbidx
477 + * @s: FlexCAN device pointer
478 + * @mbidx: mailbox index
479 + */
480 +static void flexcan_irq_iflag_set(FlexcanState *s, int mbidx)
481 +{
482 + if (mbidx < 32) {
483 + s->regs.iflag1 |= BIT(mbidx);
484 + } else {
485 + s->regs.iflag2 |= BIT(mbidx - 32);
486 + }
487 +}
488 +
489 +/**
490 + * flexcan_irq_iflag_clear() - Clear IFLAG bit corresponding to MB mbidx
491 + * @s: FlexCAN device pointer
492 + * @mbidx: mailbox index
493 + */
494 +static void flexcan_irq_iflag_clear(FlexcanState *s, int mbidx)
495 +{
496 + if (mbidx < 32) {
497 + s->regs.iflag1 &= ~BIT(mbidx);
498 + } else {
499 + s->regs.iflag2 &= ~BIT(mbidx - 32);
500 + }
501 +}
502 +
503 +/* ========== RESET ========== */
504 +static void flexcan_reset_local_state(FlexcanState *s)
505 +{
506 + uint32_t *reset_mask = (uint32_t *)&flexcan_regs_reset_mask;
507 + for (int i = 0; i < (sizeof(FlexcanRegs) / 4); i++) {
508 + s->regs_raw[i] &= reset_mask[i];
509 + }
510 +
511 + s->regs.mcr |= 0x5980000F;
512 + s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
513 + s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
514 + s->timer_start = FLEXCAN_TIMER_STOPPED;
515 +
516 + trace_flexcan_reset(DEVICE(s)->canonical_path);
517 +}
518 +
519 +static void flexcan_reset_enter(Object *obj, ResetType type)
520 +{
521 + FlexcanState *s = CAN_FLEXCAN(obj);
522 +
523 + memset(&s->regs, 0, sizeof(s->regs));
524 + flexcan_reset_local_state(s);
525 +}
526 +
527 +static void flexcan_reset_hold(Object *obj, ResetType type)
528 +{
529 + FlexcanState *s = CAN_FLEXCAN(obj);
530 +
531 + flexcan_irq_update(s);
532 +}
533 +
534 +
535 +/* ========== Operation mode control ========== */
536 +/**
537 + * flexcan_update_esr() - Update ESR based on mode and CAN bus connection state
538 + * @s: FlexCAN device pointer
539 + */
540 +static void flexcan_update_esr(FlexcanState *s)
541 +{
542 + bool is_running = (s->regs.mcr & FLEXCAN_MCR_NOT_RDY) == 0;
543 + /* potentially, there could be other influences on ESR[SYNCH] */
544 +
545 + if (is_running && s->canbus) {
546 + s->regs.esr |= FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE;
547 + } else {
548 + s->regs.esr &= ~(FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE);
549 + }
550 +}
551 +
552 +/**
553 + * flexcan_update_esr() - Process MCR write
554 + * @s: FlexCAN device pointer
555 + * @pv: previously set MCR value
556 + *
557 + * This function expects the new MCR value to be already written in s->regs.mcr.
558 + */
559 +static void flexcan_set_mcr(FlexcanState *s, const uint32_t pv)
560 +{
561 + uint32_t cv = s->regs.mcr;
562 +
563 + /* -- module disable mode -- */
564 + if (!(pv & FLEXCAN_MCR_MDIS) && (cv & FLEXCAN_MCR_MDIS)) {
565 + /* transition to Module Disable mode */
566 + cv |= FLEXCAN_MCR_LPM_ACK;
567 + } else if ((pv & FLEXCAN_MCR_MDIS) && !(cv & FLEXCAN_MCR_MDIS)) {
568 + /* transition from Module Disable mode */
569 + cv &= ~FLEXCAN_MCR_LPM_ACK;
570 + }
571 +
572 + /* -- soft reset -- */
573 + if (!(cv & FLEXCAN_MCR_LPM_ACK) && (cv & FLEXCAN_MCR_SOFTRST)) {
574 + if (s->regs.mcr & FLEXCAN_MCR_LPM_ACK) {
575 + qemu_log_mask(LOG_GUEST_ERROR,
576 + "%s: invalid soft reset request in low-power mode",
577 + DEVICE(s)->canonical_path);
578 + }
579 +
580 + flexcan_reset_local_state(s);
581 + cv = s->regs.mcr;
582 + }
583 +
584 + /* -- freeze mode -- */
585 + if (!(cv & FLEXCAN_MCR_LPM_ACK) &&
586 + (cv & FLEXCAN_MCR_FRZ) &&
587 + (cv & FLEXCAN_MCR_HALT)) {
588 + cv |= FLEXCAN_MCR_FRZ_ACK;
589 + } else {
590 + cv &= ~FLEXCAN_MCR_FRZ_ACK;
591 + }
592 +
593 + /* -- fifo mode -- */
594 + if (
595 + ((pv & FLEXCAN_MCR_FEN) && !(cv & FLEXCAN_MCR_FEN)) ||
596 + (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN))
597 + ) {
598 + /* clear iflags used by fifo */
599 + s->regs.iflag1 &= ~(
600 + FLEXCAN_IFLAG_RX_FIFO_AVAILABLE |
601 + FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
602 + FLEXCAN_IFLAG_RX_FIFO_WARN
603 + );
604 + }
605 + if (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN)) {
606 + /* zero out fifo region, we rely on zeroed can_ctrl for empty slots */
607 + memset(s->regs.mbs, 0,
608 + FLEXCAN_FIFO_DEPTH * sizeof(FlexcanRegsMessageBuffer));
609 + }
610 +
611 + /*
612 + * assert NOT_RDY bit if in disable,
613 + * stop (not implemented) or freeze mode
614 + */
615 + if ((cv & FLEXCAN_MCR_LPM_ACK) || (cv & FLEXCAN_MCR_FRZ_ACK)) {
616 + cv |= FLEXCAN_MCR_NOT_RDY;
617 + } else {
618 + cv &= ~FLEXCAN_MCR_NOT_RDY;
619 + }
620 +
621 + if ((pv & FLEXCAN_MCR_NOT_RDY) && !(cv & FLEXCAN_MCR_NOT_RDY)) {
622 + /* module went up, start the timer */
623 + flexcan_timer_start(s);
624 + } else if (!(pv & FLEXCAN_MCR_NOT_RDY) && (cv & FLEXCAN_MCR_NOT_RDY)) {
625 + /* module went down, store the current timer value */
626 + flexcan_timer_stop(s);
627 + }
628 +
629 + s->regs.mcr = cv;
630 + flexcan_update_esr(s);
631 + trace_flexcan_set_mcr(
632 + DEVICE(s)->canonical_path,
633 + cv & FLEXCAN_MCR_LPM_ACK ? "DISABLED" : "ENABLED",
634 + (cv & FLEXCAN_MCR_FRZ_ACK || cv & FLEXCAN_MCR_LPM_ACK) ?
635 + "FROZEN" : "RUNNING",
636 + cv & FLEXCAN_MCR_FEN ? "FIFO" : "MAILBOX",
637 + cv & FLEXCAN_MCR_NOT_RDY ? "NOT_RDY" : "RDY",
638 + s->regs.esr & FLEXCAN_ESR_SYNCH ? "SYNC" : "NOSYNC"
639 + );
640 +}
641 +
642 +/* ========== TX ========== */
643 +static void flexcan_transmit(FlexcanState *s, int mbidx)
644 +{
645 + FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
646 + qemu_can_frame frame = {
647 + .flags = 0,
648 + };
649 + uint32_t *frame_data = (uint32_t *)&frame.data;
650 + uint32_t timestamp = flexcan_get_timestamp(s, true);
651 +
652 + if ((s->regs.ctrl & FLEXCAN_CTRL_LOM) ||
653 + (s->regs.mcr & FLEXCAN_MCR_NOT_RDY)) {
654 + /* no transmiting in listen-only, freeze or low-power mode */
655 + return;
656 + }
657 +
658 + if (mb->can_ctrl & FLEXCAN_MB_CNT_IDE) {
659 + /* 29b ID stored in bits [0, 29) */
660 + uint32_t id = mb->can_id & 0x1FFFFFFF;
661 + frame.can_id = id | QEMU_CAN_EFF_FLAG;
662 + } else {
663 + /* 11b ID stored in bits [18, 29) */
664 + uint32_t id = (mb->can_id & (0x7FF << 18)) >> 18;
665 + frame.can_id = id;
666 + }
667 +
668 + frame.can_dlc = (mb->can_ctrl & (0xF << 16)) >> 16;
669 +
670 + for (int i = 0; i < 2; i++) {
671 + stl_be_p(&frame_data[i], mb->data[i]);
672 + }
673 +
674 + if (!(s->regs.mcr & FLEXCAN_MCR_SRX_DIS)) {
675 + /* self-reception */
676 + flexcan_mb_rx(s, &frame);
677 + }
678 + if (!(s->regs.ctrl & FLEXCAN_CTRL_LPB)) {
679 + /* send to bus if not in loopback mode */
680 + if (s->canbus) {
681 + can_bus_client_send(&s->bus_client, &frame, 1);
682 + } else {
683 + /* todo: raise error (no ack) */
684 + }
685 + }
686 +
687 + mb->can_ctrl &= ~(FLEXCAN_MB_CODE_MASK | FLEXCAN_MB_CNT_TIMESTAMP_MASK);
688 + mb->can_ctrl |= FLEXCAN_MB_CODE_TX_INACTIVE |
689 + FLEXCAN_MB_CNT_TIMESTAMP(timestamp);
690 +
691 + /* todo: compute the CRC */
692 + s->regs.crcr = FLEXCAN_CRCR_TXCRC(0) | FLEXCAN_CRCR_MBCRC(mbidx);
693 +
694 + flexcan_irq_iflag_set(s, mbidx);
695 +}
696 +
697 +static void flexcan_mb_write(FlexcanState *s, int mbid)
698 +{
699 + FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
700 +
701 + bool is_mailbox = (mb <= flexcan_get_last_enabled_mailbox(s)) &&
702 + (mb >= flexcan_get_first_message_buffer(s));
703 +
704 + if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_WRITE)) {
705 + char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
706 + const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);
707 + trace_flexcan_mb_write(DEVICE(s)->canonical_path, mbid, code_str,
708 + is_mailbox, mb->can_ctrl, mb->can_id);
709 + }
710 +
711 + if (!is_mailbox) {
712 + /**
713 + * Disabled mailbox or mailbox in region of queue filters
714 + * was updated. Either way there is nothing to do.
715 + */
716 + return;
717 + }
718 +
719 + /* any write to message buffer clears the not_serviced flag */
720 + mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
721 +
722 + /**
723 + * todo: search for active tx mbs on transition from freeze/disable mode
724 + */
725 + switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
726 + case FLEXCAN_MB_CODE_TX_INACTIVE:
727 + QEMU_FALLTHROUGH;
728 + case FLEXCAN_MB_CODE_RX_INACTIVE:
729 + QEMU_FALLTHROUGH;
730 + case FLEXCAN_MB_CODE_RX_EMPTY:
731 + QEMU_FALLTHROUGH;
732 + case FLEXCAN_MB_CODE_RX_FULL:
733 + QEMU_FALLTHROUGH;
734 + case FLEXCAN_MB_CODE_RX_RANSWER:
735 + break;
736 +
737 + case FLEXCAN_MB_CODE_TX_DATA:
738 + flexcan_transmit(s, mbid);
739 + break;
740 + case FLEXCAN_MB_CODE_TX_ABORT:
741 + /*
742 + * as transmission is instant, it can never be aborted
743 + * we need to set CODE in C/S back to the previous code
744 + */
745 + mb->can_ctrl &= ~FLEXCAN_MB_CODE(1);
746 + break;
747 + case FLEXCAN_MB_CODE_TX_TANSWER:
748 + break;
749 + default:
750 + /* prevent setting the busy bit */
751 + mb->can_ctrl &= ~FLEXCAN_MB_CODE_RX_BUSY_BIT;
752 + break;
753 + }
754 +
755 +}
756 +
757 +/* ========== RX ========== */
758 +static void flexcan_mb_move_in(FlexcanState *s, const qemu_can_frame *frame,
759 + FlexcanRegsMessageBuffer *target_mb)
760 +{
761 + uint32_t frame_len = frame->can_dlc;
762 + uint32_t *frame_data = (uint32_t *)&frame->data;
763 + int timestamp = flexcan_get_timestamp(s, true);
764 + uint32_t new_code = 0;
765 +
766 + memset(target_mb, 0, sizeof(FlexcanRegsMessageBuffer));
767 +
768 + if (frame_len > 8) {
769 + frame_len = 8;
770 + }
771 + for (int i = 0; i < 2; i++) {
772 + target_mb->data[i] = ldl_be_p(&frame_data[i]);
773 + }
774 +
775 + switch (target_mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
776 + case FLEXCAN_MB_CODE_RX_FULL:
777 + case FLEXCAN_MB_CODE_RX_OVERRUN:
778 + if (target_mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV) {
779 + new_code = FLEXCAN_MB_CODE_RX_OVERRUN;
780 + } else {
781 + new_code = FLEXCAN_MB_CODE_RX_FULL;
782 + }
783 + break;
784 + case FLEXCAN_MB_CODE_RX_RANSWER:
785 + assert(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS);
786 + new_code = FLEXCAN_MB_CODE_TX_TANSWER;
787 + break;
788 + default:
789 + new_code = FLEXCAN_MB_CODE_RX_FULL;
790 + }
791 +
792 + target_mb->can_ctrl = new_code
793 + | FLEXCAN_MB_CNT_TIMESTAMP(timestamp)
794 + | FLEXCAN_MB_CNT_LENGTH(frame_len)
795 + | FLEXCAN_MB_CNT_NOT_SRV
796 + | FLEXCAN_MB_CNT_SRR; /* always set for received frames */
797 + if (frame->can_id & QEMU_CAN_RTR_FLAG) {
798 + target_mb->can_ctrl |= FLEXCAN_MB_CNT_RTR;
799 + }
800 +
801 + if (frame->can_id & QEMU_CAN_EFF_FLAG) {
802 + target_mb->can_ctrl |= FLEXCAN_MB_CNT_IDE;
803 + target_mb->can_id |= frame->can_id & QEMU_CAN_EFF_MASK;
804 + } else {
805 + target_mb->can_id |= (frame->can_id & QEMU_CAN_SFF_MASK) << 18;
806 + }
807 +}
808 +static void flexcan_mb_lock(FlexcanState *s, int mbidx)
809 +{
810 + FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
811 + if ((mb > flexcan_get_last_enabled_mailbox(s)) ||
812 + (mb < flexcan_get_first_message_buffer(s))) {
813 + return;
814 + }
815 + switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
816 + case FLEXCAN_MB_CODE_RX_FULL:
817 + QEMU_FALLTHROUGH;
818 + case FLEXCAN_MB_CODE_RX_OVERRUN:
819 + QEMU_FALLTHROUGH;
820 + case FLEXCAN_MB_CODE_RX_RANSWER:
821 + /* continue */
822 + trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 1);
823 + break;
824 + default:
825 + trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 0);
826 + return;
827 + }
828 +
829 + s->locked_mbidx = mbidx;
830 +}
831 +
832 +static void flexcan_mb_unlock(FlexcanState *s)
833 +{
834 + int locked_mbidx = s->locked_mbidx;
835 + bool has_pending_frame = locked_mbidx == s->smb_target_mbidx;
836 +
837 + if (s->locked_mbidx == FLEXCAN_NO_MB_LOCKED) {
838 + return;
839 + }
840 +
841 + assert(locked_mbidx >= 0 && locked_mbidx < FLEXCAN_MAILBOX_COUNT);
842 + FlexcanRegsMessageBuffer *locked_mb = &s->regs.mbs[locked_mbidx];
843 + s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
844 +
845 + if (locked_mb >= flexcan_get_first_message_buffer(s) &&
846 + locked_mb <= flexcan_get_last_enabled_mailbox(s)
847 + ) {
848 + /* mark the message buffer as serviced */
849 + locked_mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
850 + }
851 +
852 + /* try move in from SMB */
853 + trace_flexcan_mb_unlock(DEVICE(s)->canonical_path, locked_mbidx,
854 + has_pending_frame ? " PENDING FRAME IN SMB" : "");
855 +
856 + /* todo: in low-power modes, this should be postponed until exit */
857 + if (has_pending_frame) {
858 + FlexcanRegsMessageBuffer *target_mb = &s->regs.mbs[locked_mbidx];
859 + memcpy(target_mb, &s->regs.rx_smb0, sizeof(FlexcanRegsMessageBuffer));
860 +
861 + memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
862 + s->locked_mbidx = FLEXCAN_SMB_EMPTY;
863 +
864 + flexcan_irq_iflag_set(s, locked_mbidx);
865 + }
866 +}
867 +
868 +static bool flexcan_can_receive(CanBusClientState *client)
869 +{
870 + FlexcanState *s = container_of(client, FlexcanState, bus_client);
871 + return !(s->regs.mcr & FLEXCAN_MCR_NOT_RDY);
872 +}
873 +
874 +/* --------- RX FIFO ---------- */
875 +
876 +/**
877 + * flexcan_fifo_pop() - Pop message from FIFO and update IRQs
878 + * @s: FlexCAN device pointer
879 + *
880 + * Does not require the queue to be non-empty.
881 + */
882 +static void flexcan_fifo_pop(FlexcanState *s)
883 +{
884 + if (s->regs.fifo.mb_back.can_ctrl != 0) {
885 + /* move queue elements forward */
886 + memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0],
887 + sizeof(s->regs.fifo.mbs_queue));
888 +
889 + /* clear the first-in slot */
890 + memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0,
891 + sizeof(FlexcanRegsMessageBuffer));
892 +
893 + trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1,
894 + s->regs.fifo.mb_back.can_ctrl != 0);
895 + } else {
896 + trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0);
897 + }
898 +
899 + if (s->regs.fifo.mb_back.can_ctrl != 0) {
900 + flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
901 + } else {
902 + flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE);
903 + }
904 +}
905 +
906 +/**
907 + * flexcan_fifo_find_free_slot() - Find the first free slot in the FIFO
908 + * @s: FlexCAN device pointer
909 + *
910 + * Return: Pointer to the first free slot in the FIFO,
911 + * or NULL if the queue is full.
912 + */
913 +static FlexcanRegsMessageBuffer *flexcan_fifo_find_free_slot(FlexcanState *s)
914 +{
915 + for (int i = 0; i < FLEXCAN_FIFO_DEPTH; i++) {
916 + FlexcanRegsMessageBuffer *mb = &s->regs.mbs[i];
917 + if (mb->can_ctrl == 0) {
918 + return mb;
919 + }
920 + }
921 + return NULL;
922 +}
923 +
924 +/**
925 + * flexcan_fifo_push() - Update FIFO IRQs after frame move-in
926 + * @s: FlexCAN device pointer
927 + * @slot: Target FIFO slot
928 + *
929 + * The usage is as follows:
930 + * 1. Get free slot pointer using flexcan_fifo_find_free_slot()
931 + * 2. Move the frame in if not NULL
932 + * 3. Call flexcan_fifo_push() regardless of the NULL pointer
933 + */
934 +static void flexcan_fifo_push(FlexcanState *s, FlexcanRegsMessageBuffer *slot)
935 +{
936 + if (slot) {
937 + int n_occupied = slot - s->regs.mbs;
938 + if (n_occupied == 4) { /* 4 means the 5th slot was filled in */
939 + /*
940 + * fifo occupancy increased from 4 to 5,
941 + * raising FIFO_WARN interrupt
942 + */
943 + flexcan_irq_iflag_set(s, I_FIFO_WARN);
944 + }
945 + flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
946 +
947 + trace_flexcan_fifo_push(DEVICE(s)->canonical_path, n_occupied);
948 + } else {
949 + flexcan_irq_iflag_set(s, I_FIFO_OVERFLOW);
950 +
951 + trace_flexcan_fifo_push(DEVICE(s)->canonical_path, -1);
952 + }
953 +}
954 +
955 +static enum FlexcanRx flexcan_fifo_rx(FlexcanState *s,
956 + const qemu_can_frame *buf)
957 +{
958 + /* todo: filtering. return FLEXCAN_FIFO_RX_RETRY if filtered out */
959 + if ((s->regs.mcr & FLEXCAN_MCR_IDAM_MASK) == FLEXCAN_MCR_IDAM_D) {
960 + /* all frames rejected */
961 + return FLEXCAN_RX_SEARCH_RETRY;
962 + } else {
963 + /* push message to queue if not full */
964 + FlexcanRegsMessageBuffer *slot = flexcan_fifo_find_free_slot(s);
965 + if (slot) {
966 + flexcan_mb_move_in(s, buf, slot);
967 + }
968 + flexcan_fifo_push(s, slot);
969 +
970 + return slot ? FLEXCAN_RX_SEARCH_ACCEPT : FLEXCAN_RX_SEARCH_DROPPED;
971 + }
972 +}
973 +
974 +/* --------- RX message buffer ---------- */
975 +
976 +/**
977 + * flexcan_mb_rx_check_mb() - Check if a mb matches a received frame
978 + * @s: FlexCAN device pointer
979 + * @buf: Frame to be received from CAN subsystem
980 + * @mbid: Target mailbox index. The mailbox must be a valid message buffer.
981 + *
982 + * Return: FLEXCAN_CHECK_MB_NIL if the message buffer does not match.
983 + * FLEXCAN_CHECK_MB_MATCH if the message buffer matches the received
984 + * frame and is free-to-receive,
985 + * FLEXCAN_CHECK_MB_MATCH_LOCKED if the message buffer matches,
986 + * but is locked,
987 + * FLEXCAN_CHECK_MB_MATCH_NON_FREE if the message buffer matches,
988 + * but is not free-to-receive
989 + * for some other reason.
990 + */
991 +static enum FlexcanCheck flexcan_mb_rx_check_mb(FlexcanState *s,
992 + const qemu_can_frame *buf,
993 + int mbid)
994 +{
995 + FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
996 + const bool is_rtr = !!(buf->can_id & QEMU_CAN_RTR_FLAG);
997 + const bool is_serviced = !(mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV);
998 + const bool is_locked = s->locked_mbidx == mbid;
999 +
1000 + bool is_free_to_receive = false;
1001 + bool is_matched = false;
1002 +
1003 + switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
1004 + case FLEXCAN_MB_CODE_RX_RANSWER:
1005 + if (is_rtr && !(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS)) {
1006 + /* todo: do the actual matching/filtering and RTR answer */
1007 + is_matched = true;
1008 + }
1009 + break;
1010 + case FLEXCAN_MB_CODE_RX_FULL:
1011 + QEMU_FALLTHROUGH;
1012 + case FLEXCAN_MB_CODE_RX_OVERRUN:
1013 + is_free_to_receive = is_serviced;
1014 + /* todo: do the actual matching/filtering */
1015 + is_matched = true;
1016 + break;
1017 + case FLEXCAN_MB_CODE_RX_EMPTY:
1018 + is_free_to_receive = true;
1019 + /* todo: do the actual matching/filtering */
1020 + is_matched = true;
1021 + break;
1022 + default:
1023 + break;
1024 + }
1025 +
1026 + if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_RX_CHECK_MB)) {
1027 + char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
1028 + const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);
1029 + trace_flexcan_mb_rx_check_mb(DEVICE(s)->canonical_path, mbid, code_str,
1030 + is_matched, is_free_to_receive,
1031 + is_serviced, is_locked);
1032 + }
1033 +
1034 + if (!is_matched) {
1035 + return FLEXCAN_CHECK_MB_NIL;
1036 + }
1037 +
1038 + if (is_locked) {
1039 + return FLEXCAN_CHECK_MB_MATCH_LOCKED;
1040 + }
1041 +
1042 + if (is_free_to_receive) {
1043 + return FLEXCAN_CHECK_MB_MATCH;
1044 + }
1045 +
1046 + return FLEXCAN_CHECK_MB_MATCH_NON_FREE;
1047 +}
1048 +
1049 +static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf)
1050 +{
1051 + int last_not_free_to_receive_mbid = -1;
1052 + bool last_not_free_to_receive_locked = false;
1053 +
1054 + FlexcanRegsMessageBuffer *first_mb = flexcan_get_first_message_buffer(s);
1055 + FlexcanRegsMessageBuffer *last_mb = flexcan_get_last_enabled_mailbox(s);
1056 +
1057 + for (FlexcanRegsMessageBuffer *mb = first_mb;
1058 + mb <= last_mb; mb++) {
1059 + int mbid = mb - s->regs.mbs;
1060 + enum FlexcanCheck r = flexcan_mb_rx_check_mb(s, buf, mbid);
1061 + if (r == FLEXCAN_CHECK_MB_MATCH) {
1062 + flexcan_mb_move_in(s, buf, mb);
1063 + flexcan_irq_iflag_set(s, mbid);
1064 + return FLEXCAN_RX_SEARCH_ACCEPT;
1065 + }
1066 +
1067 + if (r == FLEXCAN_CHECK_MB_MATCH_NON_FREE) {
1068 + last_not_free_to_receive_mbid = mbid;
1069 + last_not_free_to_receive_locked = false;
1070 + } else if (r == FLEXCAN_CHECK_MB_MATCH_LOCKED) {
1071 + /*
1072 + * message buffer is locked,
1073 + * we can move in the message after it's unlocked
1074 + */
1075 + last_not_free_to_receive_mbid = mbid;
1076 + last_not_free_to_receive_locked = true;
1077 + }
1078 + }
1079 +
1080 + if (last_not_free_to_receive_mbid >= -1) {
1081 + if (last_not_free_to_receive_locked) {
1082 + /*
1083 + * copy to temporary mailbox (SMB)
1084 + * it will be moved in when the mailbox is unlocked
1085 + */
1086 + s->regs.rx_smb0.can_ctrl =
1087 + s->regs.mbs[last_not_free_to_receive_mbid].can_id;
1088 + flexcan_mb_move_in(s, buf, &s->regs.rx_smb0);
1089 + s->smb_target_mbidx = last_not_free_to_receive_mbid;
1090 + return FLEXCAN_RX_SEARCH_ACCEPT;
1091 + }
1092 +
1093 + if (s->regs.mcr & FLEXCAN_MCR_IRMQ) {
1094 + flexcan_mb_move_in(s, buf,
1095 + &s->regs.mbs[last_not_free_to_receive_mbid]);
1096 + flexcan_irq_iflag_set(s, last_not_free_to_receive_mbid);
1097 + return FLEXCAN_RX_SEARCH_ACCEPT;
1098 + }
1099 + }
1100 +
1101 + return FLEXCAN_RX_SEARCH_RETRY;
1102 +}
1103 +
1104 +static ssize_t flexcan_receive(CanBusClientState *client,
1105 + const qemu_can_frame *frames, size_t frames_cnt)
1106 +{
1107 + FlexcanState *s = container_of(client, FlexcanState, bus_client);
1108 + trace_flexcan_receive(DEVICE(s)->canonical_path, frames_cnt);
1109 +
1110 + if (frames_cnt == 0) {
1111 + return 0;
1112 + }
1113 +
1114 + /* clear the SMB, as it would be overriden in hardware */
1115 + memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
1116 + s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
1117 +
1118 + for (size_t i = 0; i < frames_cnt; i++) {
1119 + int r;
1120 + const qemu_can_frame *frame = &frames[i];
1121 + if (frame->can_id & QEMU_CAN_ERR_FLAG) {
1122 + /* todo: error frame handling */
1123 + continue;
1124 + }
1125 + if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {
1126 + /* CAN FD supported only in later FlexCAN version */
1127 + continue;
1128 + }
1129 +
1130 + /* todo: this order logic is not complete and needs further work */
1131 + if (s->regs.mcr & FLEXCAN_MCR_FEN &&
1132 + s->regs.ctrl2 & FLEXCAN_CTRL2_MRP) {
1133 + r = flexcan_mb_rx(s, frame);
1134 + if (r == FLEXCAN_RX_SEARCH_RETRY) {
1135 + flexcan_fifo_rx(s, frame);
1136 + }
1137 + } else if (s->regs.mcr & FLEXCAN_MCR_FEN) {
1138 + r = flexcan_fifo_rx(s, frame);
1139 + if (r == FLEXCAN_RX_SEARCH_RETRY) {
1140 + flexcan_mb_rx(s, frame);
1141 + }
1142 + } else {
1143 + flexcan_mb_rx(s, frame);
1144 + }
1145 + }
1146 +
1147 + flexcan_irq_update(s);
1148 + return 1;
1149 +}
1150 +
1151 +/* ========== I/O handling ========== */
1152 +static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val,
1153 + unsigned size)
1154 +{
1155 + FlexcanState *s = opaque;
1156 + uint32_t write_mask = ((const uint32_t *)
1157 + &flexcan_regs_write_mask)[addr / 4];
1158 + uint32_t old_value = s->regs_raw[addr / 4];
1159 +
1160 + /*
1161 + * 0 for bits that can "only be written in Freeze mode as it is blocked
1162 + * by hardware in other modes"
1163 + */
1164 + const uint32_t freeze_mask_mcr = 0xDF54CC80;
1165 + const uint32_t freeze_mask_ctrl1 = 0x0000E740;
1166 +
1167 + flexcan_trace_mem_op(s, addr, val, size, true);
1168 + switch (addr) {
1169 + case offsetof(FlexcanRegs, mcr):
1170 + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
1171 + write_mask &= freeze_mask_mcr;
1172 + }
1173 + s->regs.mcr = (val & write_mask) | (old_value & ~write_mask);
1174 + flexcan_set_mcr(s, old_value);
1175 + break;
1176 + case offsetof(FlexcanRegs, ctrl):
1177 + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
1178 + write_mask &= freeze_mask_ctrl1;
1179 + }
1180 + s->regs.ctrl = (val & write_mask) | (old_value & ~write_mask);
1181 + break;
1182 + case offsetof(FlexcanRegs, iflag1):
1183 + s->regs.iflag1 &= ~val;
1184 + if ((s->regs.mcr & FLEXCAN_MCR_FEN) &&
1185 + (val & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) {
1186 + flexcan_fifo_pop(s);
1187 + }
1188 + break;
1189 + case offsetof(FlexcanRegs, iflag2):
1190 + s->regs.iflag2 &= ~val;
1191 + break;
1192 + case offsetof(FlexcanRegs, ctrl2):
1193 + QEMU_FALLTHROUGH;
1194 + case offsetof(FlexcanRegs, ecr):
1195 + QEMU_FALLTHROUGH;
1196 + case offsetof(FlexcanRegs, rxmgmask):
1197 + QEMU_FALLTHROUGH;
1198 + case offsetof(FlexcanRegs, rx14mask):
1199 + QEMU_FALLTHROUGH;
1200 + case offsetof(FlexcanRegs, rx15mask):
1201 + QEMU_FALLTHROUGH;
1202 + case offsetof(FlexcanRegs, rxfgmask):
1203 + QEMU_FALLTHROUGH;
1204 + case offsetof(FlexcanRegs, rximr[0]) ... offsetof(FlexcanRegs, rximr[63]):
1205 + /* these registers can only be written in freeze mode */
1206 + if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
1207 + break;
1208 + }
1209 + QEMU_FALLTHROUGH;
1210 + default:
1211 + s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask);
1212 +
1213 + if (addr >= offsetof(FlexcanRegs, mb) &&
1214 + addr < offsetof(FlexcanRegs, _reserved4)) {
1215 + /* access to mailbox */
1216 + int mbid = (addr - offsetof(FlexcanRegs, mb)) /
1217 + sizeof(FlexcanRegsMessageBuffer);
1218 +
1219 + if (s->locked_mbidx == mbid) {
1220 + flexcan_mb_unlock(s);
1221 + }
1222 +
1223 + /* check for invalid writes into FIFO region */
1224 + if (s->regs.mcr & FLEXCAN_MCR_FEN && mbid < FLEXCAN_FIFO_DEPTH) {
1225 + qemu_log_mask(LOG_GUEST_ERROR,
1226 + "%s: Invalid write to Rx-FIFO structure",
1227 + DEVICE(s)->canonical_path);
1228 + return;
1229 + }
1230 +
1231 + /* run mailbox processing function on write to control word */
1232 + if ((addr & 0xF) == 0) {
1233 + flexcan_mb_write(s, mbid);
1234 + }
1235 + }
1236 + break;
1237 + }
1238 +
1239 + flexcan_irq_update(s);
1240 +}
1241 +
1242 +static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size)
1243 +{
1244 + FlexcanState *s = opqaue;
1245 + uint32_t rv = s->regs_raw[addr >> 2];
1246 +
1247 + if (addr >= offsetof(FlexcanRegs, mb) &&
1248 + addr < offsetof(FlexcanRegs, _reserved4)) {
1249 + /* reading from mailbox */
1250 + hwaddr offset = addr - offsetof(FlexcanRegs, mb);
1251 + int mbid = offset / sizeof(FlexcanRegsMessageBuffer);
1252 +
1253 + if (addr % 16 == 0 && s->locked_mbidx != mbid) {
1254 + /* reading control word locks the mailbox */
1255 + flexcan_mb_unlock(s);
1256 + flexcan_mb_lock(s, mbid);
1257 + flexcan_irq_update(s);
1258 + rv = s->regs.mbs[mbid].can_ctrl & ~FLEXCAN_MB_CNT_NOT_SRV;
1259 + }
1260 + } else if (addr == offsetof(FlexcanRegs, timer)) {
1261 + flexcan_mb_unlock(s);
1262 + flexcan_irq_update(s);
1263 + rv = flexcan_get_timestamp(s, false);
1264 + }
1265 +
1266 + flexcan_trace_mem_op(s, addr, rv, size, false);
1267 + return rv;
1268 +}
1269 +
1270 +static bool flexcan_mem_accepts(void *opaque, hwaddr addr,
1271 + unsigned size, bool is_write,
1272 + MemTxAttrs attrs)
1273 +{
1274 + FlexcanState *s = opaque;
1275 +
1276 + if ((s->regs.ctrl2 & FLEXCAN_CTRL2_WRMFRZ) &&
1277 + (s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
1278 + /* unrestricted access to FlexCAN memory in freeze mode */
1279 + return true;
1280 + } else if (attrs.user && (s->regs.mcr & FLEXCAN_MCR_SUPV)) {
1281 + qemu_log_mask(LOG_GUEST_ERROR,
1282 + "%s: Invalid user-mode access to restricted register",
1283 + DEVICE(s)->canonical_path);
1284 + return false;
1285 + } else if (attrs.user && is_write && addr < 4) {
1286 + qemu_log_mask(LOG_GUEST_ERROR,
1287 + "%s: Invalid user-mode access to MCR",
1288 + DEVICE(s)->canonical_path);
1289 + return false;
1290 + }
1291 +
1292 + return true;
1293 +}
1294 +
1295 +static const struct MemoryRegionOps flexcan_ops = {
1296 + .read = flexcan_mem_read,
1297 + .write = flexcan_mem_write,
1298 + .endianness = DEVICE_LITTLE_ENDIAN,
1299 + .valid = {
1300 + .min_access_size = 1,
1301 + .max_access_size = 4,
1302 + .unaligned = true,
1303 + .accepts = flexcan_mem_accepts
1304 + },
1305 + .impl = {
1306 + .min_access_size = 4,
1307 + .max_access_size = 4,
1308 + .unaligned = false
1309 + },
1310 +};
1311 +
1312 +static CanBusClientInfo flexcan_bus_client_info = {
1313 + .can_receive = flexcan_can_receive,
1314 + .receive = flexcan_receive,
1315 +};
1316 +
1317 +static int flexcan_connect_to_bus(FlexcanState *s, CanBusState *bus)
1318 +{
1319 + s->bus_client.info = &flexcan_bus_client_info;
1320 +
1321 + if (can_bus_insert_client(bus, &s->bus_client) < 0) {
1322 + return -1;
1323 + }
1324 + return 0;
1325 +}
1326 +
1327 +static void flexcan_init(Object *obj)
1328 +{
1329 + FlexcanState *s = CAN_FLEXCAN(obj);
1330 +
1331 + memory_region_init_io(
1332 + &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN,
1333 + offsetof(FlexcanRegs, _reserved6)
1334 + );
1335 +}
1336 +
1337 +static void flexcan_realize(DeviceState *dev, Error **errp)
1338 +{
1339 + FlexcanState *s = CAN_FLEXCAN(dev);
1340 +
1341 + if (s->canbus) {
1342 + if (flexcan_connect_to_bus(s, s->canbus) < 0) {
1343 + error_setg(errp, "%s: flexcan_connect_to_bus failed",
1344 + dev->canonical_path);
1345 + return;
1346 + }
1347 + }
1348 +
1349 + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
1350 + sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);
1351 +}
1352 +
1353 +static const VMStateDescription vmstate_can = {
1354 + .name = TYPE_CAN_FLEXCAN,
1355 + .version_id = 1,
1356 + .minimum_version_id = 1,
1357 + .fields = (const VMStateField[]) {
1358 + VMSTATE_INT64(timer_start, FlexcanState),
1359 + VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 4),
1360 + VMSTATE_INT32(locked_mbidx, FlexcanState),
1361 + VMSTATE_INT32(smb_target_mbidx, FlexcanState),
1362 + VMSTATE_END_OF_LIST(),
1363 + },
1364 +};
1365 +
1366 +static const Property flexcan_properties[] = {
1367 + DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS,
1368 + CanBusState *),
1369 +};
1370 +
1371 +static void flexcan_class_init(ObjectClass *klass, const void *data)
1372 +{
1373 + DeviceClass *dc = DEVICE_CLASS(klass);
1374 + ResettableClass *rc = RESETTABLE_CLASS(klass);
1375 +
1376 + rc->phases.enter = flexcan_reset_enter;
1377 + rc->phases.hold = flexcan_reset_hold;
1378 + dc->realize = flexcan_realize;
1379 + device_class_set_props(dc, flexcan_properties);
1380 + dc->vmsd = &vmstate_can;
1381 + dc->desc = "i.MX FLEXCAN Controller";
1382 +}
1383 +
1384 +static const TypeInfo flexcan_info = {
1385 + .name = TYPE_CAN_FLEXCAN,
1386 + .parent = TYPE_SYS_BUS_DEVICE,
1387 + .instance_size = sizeof(FlexcanState),
1388 + .class_init = flexcan_class_init,
1389 + .instance_init = flexcan_init,
1390 +};
1391 +
1392 +static void can_register_types(void)
1393 +{
1394 + type_register_static(&flexcan_info);
1395 +}
1396 +type_init(can_register_types)
hw/net/can/flexcan_regs.h new
+197
@@ -0,0 +1,197 @@
1 +/*
2 + * Field bitmasks and register structs definitions for FlexCAN
3 + *
4 + * This implementation is based on the following datasheet:
5 + * i.MX 6Dual/6Quad Applications Processor Reference Manual
6 + * Document Number: IMX6DQRM, Rev. 6, 05/2020
7 + *
8 + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
9 + *
10 + * SPDX-License-Identifier: GPL-2.0-or-later
11 + */
12 +#include "qemu/bitops.h"
13 +
14 +#ifndef HW_CAN_FLEXCAN_REGS_H
15 +#define HW_CAN_FLEXCAN_REGS_H
16 +
17 +#define FLEXCAN_GENMASK(h, l) (((~(uint32_t)0) >> (31 - (h) + (l))) << (l))
18 +
19 +/**
20 + * The following macros were originally written for the Linux kernel by:
21 + * Andrey Volkov <andrey@volkov.fr>
22 + * Sascha Hauer <s.hauer@pengutronix.de>
23 + * Marc Kleine-Budde <mkl@pengutronix.de>
24 + * David Jander <david@protonic.nl>
25 + * and they have agreed to license them under GPL-2.0-or-later.
26 + */
27 +
28 +/* FLEXCAN module configuration register (CANMCR) bits */
29 +#define FLEXCAN_MCR_MDIS BIT(31)
30 +#define FLEXCAN_MCR_FRZ BIT(30)
31 +#define FLEXCAN_MCR_FEN BIT(29)
32 +#define FLEXCAN_MCR_HALT BIT(28)
33 +#define FLEXCAN_MCR_NOT_RDY BIT(27)
34 +#define FLEXCAN_MCR_WAK_MSK BIT(26)
35 +#define FLEXCAN_MCR_SOFTRST BIT(25)
36 +#define FLEXCAN_MCR_FRZ_ACK BIT(24)
37 +#define FLEXCAN_MCR_SUPV BIT(23)
38 +#define FLEXCAN_MCR_SLF_WAK BIT(22)
39 +#define FLEXCAN_MCR_WRN_EN BIT(21)
40 +#define FLEXCAN_MCR_LPM_ACK BIT(20)
41 +#define FLEXCAN_MCR_WAK_SRC BIT(19)
42 +#define FLEXCAN_MCR_DOZE BIT(18)
43 +#define FLEXCAN_MCR_SRX_DIS BIT(17)
44 +#define FLEXCAN_MCR_IRMQ BIT(16)
45 +#define FLEXCAN_MCR_LPRIO_EN BIT(13)
46 +#define FLEXCAN_MCR_AEN BIT(12)
47 +#define FLEXCAN_MCR_FDEN BIT(11)
48 +#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
49 +#define FLEXCAN_MCR_IDAM_A (0x0 << 8)
50 +#define FLEXCAN_MCR_IDAM_B (0x1 << 8)
51 +#define FLEXCAN_MCR_IDAM_C (0x2 << 8)
52 +#define FLEXCAN_MCR_IDAM_D (0x3 << 8)
53 +#define FLEXCAN_MCR_IDAM_MASK (0x3 << 8)
54 +
55 +/* FLEXCAN control register (CANCTRL) bits */
56 +#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xFF) << 24)
57 +#define FLEXCAN_CTRL_PRESDIV_MASK FLEXCAN_CTRL_PRESDIV(UINT32_MAX)
58 +#define FLEXCAN_CTRL_RJW(x) (((x) & 0x03) << 22)
59 +#define FLEXCAN_CTRL_RJW_MASK FLEXCAN_CTRL_RJW(UINT32_MAX)
60 +#define FLEXCAN_CTRL_PSEG1(x) (((x) & 0x07) << 19)
61 +#define FLEXCAN_CTRL_PSEG1_MASK FLEXCAN_CTRL_PSEG1(UINT32_MAX)
62 +#define FLEXCAN_CTRL_PSEG2(x) (((x) & 0x07) << 16)
63 +#define FLEXCAN_CTRL_PSEG2_MASK FLEXCAN_CTRL_PSEG2(UINT32_MAX)
64 +#define FLEXCAN_CTRL_BOFF_MSK BIT(15)
65 +#define FLEXCAN_CTRL_ERR_MSK BIT(14)
66 +#define FLEXCAN_CTRL_CLK_SRC BIT(13)
67 +#define FLEXCAN_CTRL_LPB BIT(12)
68 +#define FLEXCAN_CTRL_TWRN_MSK BIT(11)
69 +#define FLEXCAN_CTRL_RWRN_MSK BIT(10)
70 +#define FLEXCAN_CTRL_SMP BIT(7)
71 +#define FLEXCAN_CTRL_BOFF_REC BIT(6)
72 +#define FLEXCAN_CTRL_TSYN BIT(5)
73 +#define FLEXCAN_CTRL_LBUF BIT(4)
74 +#define FLEXCAN_CTRL_LOM BIT(3)
75 +#define FLEXCAN_CTRL_PROPSEG(x) ((x) & 0x07)
76 +#define FLEXCAN_CTRL_PROPSEG_MASK FLEXCAN_CTRL_PROPSEG(UINT32_MAX)
77 +#define FLEXCAN_CTRL_ERR_BUS (FLEXCAN_CTRL_ERR_MSK)
78 +#define FLEXCAN_CTRL_ERR_STATE \
79 + (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \
80 + FLEXCAN_CTRL_BOFF_MSK)
81 +#define FLEXCAN_CTRL_ERR_ALL \
82 + (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
83 +
84 +/* FLEXCAN control register 2 (CTRL2) bits */
85 +#define FLEXCAN_CTRL2_ECRWRE BIT(29)
86 +#define FLEXCAN_CTRL2_WRMFRZ BIT(28)
87 +#define FLEXCAN_CTRL2_RFFN(x) (((x) & 0x0f) << 24)
88 +#define FLEXCAN_CTRL2_TASD(x) (((x) & 0x1f) << 19)
89 +#define FLEXCAN_CTRL2_MRP BIT(18)
90 +#define FLEXCAN_CTRL2_RRS BIT(17)
91 +#define FLEXCAN_CTRL2_EACEN BIT(16)
92 +#define FLEXCAN_CTRL2_ISOCANFDEN BIT(12)
93 +
94 +/* FLEXCAN memory error control register (MECR) bits */
95 +#define FLEXCAN_MECR_ECRWRDIS BIT(31)
96 +#define FLEXCAN_MECR_HANCEI_MSK BIT(19)
97 +#define FLEXCAN_MECR_FANCEI_MSK BIT(18)
98 +#define FLEXCAN_MECR_CEI_MSK BIT(16)
99 +#define FLEXCAN_MECR_HAERRIE BIT(15)
100 +#define FLEXCAN_MECR_FAERRIE BIT(14)
101 +#define FLEXCAN_MECR_EXTERRIE BIT(13)
102 +#define FLEXCAN_MECR_RERRDIS BIT(9)
103 +#define FLEXCAN_MECR_ECCDIS BIT(8)
104 +#define FLEXCAN_MECR_NCEFAFRZ BIT(7)
105 +
106 +/* FLEXCAN error and status register (ESR) bits */
107 +#define FLEXCAN_ESR_SYNCH BIT(18)
108 +#define FLEXCAN_ESR_TWRN_INT BIT(17)
109 +#define FLEXCAN_ESR_RWRN_INT BIT(16)
110 +#define FLEXCAN_ESR_BIT1_ERR BIT(15)
111 +#define FLEXCAN_ESR_BIT0_ERR BIT(14)
112 +#define FLEXCAN_ESR_ACK_ERR BIT(13)
113 +#define FLEXCAN_ESR_CRC_ERR BIT(12)
114 +#define FLEXCAN_ESR_FRM_ERR BIT(11)
115 +#define FLEXCAN_ESR_STF_ERR BIT(10)
116 +#define FLEXCAN_ESR_TX_WRN BIT(9)
117 +#define FLEXCAN_ESR_RX_WRN BIT(8)
118 +#define FLEXCAN_ESR_IDLE BIT(7)
119 +#define FLEXCAN_ESR_BOFF_INT BIT(2)
120 +#define FLEXCAN_ESR_ERR_INT BIT(1)
121 +#define FLEXCAN_ESR_WAK_INT BIT(0)
122 +
123 +/* FLEXCAN Bit Timing register (CBT) bits */
124 +#define FLEXCAN_CBT_BTF BIT(31)
125 +#define FLEXCAN_CBT_EPRESDIV_MASK FLEXCAN_GENMASK(30, 21)
126 +#define FLEXCAN_CBT_ERJW_MASK FLEXCAN_GENMASK(20, 16)
127 +#define FLEXCAN_CBT_EPROPSEG_MASK FLEXCAN_GENMASK(15, 10)
128 +#define FLEXCAN_CBT_EPSEG1_MASK FLEXCAN_GENMASK(9, 5)
129 +#define FLEXCAN_CBT_EPSEG2_MASK FLEXCAN_GENMASK(4, 0)
130 +
131 +/* FLEXCAN FD control register (FDCTRL) bits */
132 +#define FLEXCAN_FDCTRL_FDRATE BIT(31)
133 +#define FLEXCAN_FDCTRL_MBDSR1 FLEXCAN_GENMASK(20, 19)
134 +#define FLEXCAN_FDCTRL_MBDSR0 FLEXCAN_GENMASK(17, 16)
135 +#define FLEXCAN_FDCTRL_MBDSR_8 0x0
136 +#define FLEXCAN_FDCTRL_MBDSR_12 0x1
137 +#define FLEXCAN_FDCTRL_MBDSR_32 0x2
138 +#define FLEXCAN_FDCTRL_MBDSR_64 0x3
139 +#define FLEXCAN_FDCTRL_TDCEN BIT(15)
140 +#define FLEXCAN_FDCTRL_TDCFAIL BIT(14)
141 +#define FLEXCAN_FDCTRL_TDCOFF FLEXCAN_GENMASK(12, 8)
142 +#define FLEXCAN_FDCTRL_TDCVAL FLEXCAN_GENMASK(5, 0)
143 +
144 +/* FLEXCAN FD Bit Timing register (FDCBT) bits */
145 +#define FLEXCAN_FDCBT_FPRESDIV_MASK FLEXCAN_GENMASK(29, 20)
146 +#define FLEXCAN_FDCBT_FRJW_MASK FLEXCAN_GENMASK(18, 16)
147 +#define FLEXCAN_FDCBT_FPROPSEG_MASK FLEXCAN_GENMASK(14, 10)
148 +#define FLEXCAN_FDCBT_FPSEG1_MASK FLEXCAN_GENMASK(7, 5)
149 +#define FLEXCAN_FDCBT_FPSEG2_MASK FLEXCAN_GENMASK(2, 0)
150 +
151 +/* FLEXCAN CRC Register (CRCR) bits */
152 +#define FLEXCAN_CRCR_MBCRC_MASK FLEXCAN_GENMASK(22, 16)
153 +#define FLEXCAN_CRCR_MBCRC(x) (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 16)
154 +#define FLEXCAN_CRCR_TXCRC_MASK FLEXCAN_GENMASK(14, 0)
155 +#define FLEXCAN_CRCR_TXCRC(x) ((x) & FLEXCAN_CRCR_TXCRC_MASK)
156 +
157 +/* FLEXCAN interrupt flag register (IFLAG) bits */
158 +/* Errata ERR005829 step7: Reserve first valid MB */
159 +#define I_FIFO_OVERFLOW 7
160 +#define I_FIFO_WARN 6
161 +#define I_FIFO_AVAILABLE 5
162 +
163 +#define FLEXCAN_TX_MB_RESERVED_RX_FIFO 8
164 +#define FLEXCAN_TX_MB_RESERVED_RX_MAILBOX 0
165 +#define FLEXCAN_RX_MB_RX_MAILBOX_FIRST (FLEXCAN_TX_MB_RESERVED_RX_MAILBOX + 1)
166 +#define FLEXCAN_IFLAG_MB(x) BIT_ULL(x)
167 +#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(I_FIFO_OVERFLOW)
168 +#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(I_FIFO_WARN)
169 +#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(I_FIFO_AVAILABLE)
170 +
171 +/* FLEXCAN message buffers */
172 +#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24)
173 +#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
174 +#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
175 +#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
176 +#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24)
177 +#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)
178 +
179 +#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)
180 +#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24)
181 +#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)
182 +#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)
183 +
184 +#define FLEXCAN_MB_CODE(x) (((x) & 0xF) << 24)
185 +#define FLEXCAN_MB_CODE_MASK FLEXCAN_MB_CODE(UINT32_MAX)
186 +
187 +#define FLEXCAN_MB_CNT_EDL BIT(31)
188 +#define FLEXCAN_MB_CNT_BRS BIT(30)
189 +#define FLEXCAN_MB_CNT_ESI BIT(29)
190 +#define FLEXCAN_MB_CNT_SRR BIT(22)
191 +#define FLEXCAN_MB_CNT_IDE BIT(21)
192 +#define FLEXCAN_MB_CNT_RTR BIT(20)
193 +#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xF) << 16)
194 +#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xFFFF)
195 +#define FLEXCAN_MB_CNT_TIMESTAMP_MASK FLEXCAN_MB_CNT_TIMESTAMP(UINT32_MAX)
196 +
197 +#endif
hw/net/can/meson.build
+1
@@ -6,3 +6,4 @@ system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'))
6 system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c'))
7 system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c'))
8 system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c'))
9 +system_ss.add(when: 'CONFIG_CAN_FLEXCAN', if_true: files('flexcan.c'))
hw/net/can/trace-events
+18
@@ -1,3 +1,21 @@
1 +# flexcan.c
2 +flexcan_irq_update(const char *inst, uint32_t mb_irqs1, uint32_t mb_irqs2, int setting) "%s: irqs1 0x%08x irqs2 0x%08x request %i"
3 +flexcan_set_mcr(const char *inst, const char *enabled, const char *freeze, const char *fifo, const char *rdy, const char *sync) "%s: %s %s %s %s %s"
4 +flexcan_mb_write(const char *inst, int mbidx, const char *code, int is_mailbox, uint32_t ctrl, uint32_t id) "%s: mbidx %i code %s is_mailbox %i ctrl 0x%08x id 0x%08x"
5 +flexcan_mb_lock(const char *inst, int mbidx, int had_rx_code) "%s: mbidx %i had_rx_code %i"
6 +flexcan_mb_unlock(const char *inst, int mbidx, const char *pending_frame) "%s: mbidx %i%s"
7 +flexcan_fifo_pop(const char *inst, int non_empty_before, int non_empty_after) "%s: non_empty before %i non_empty_after %i"
8 +flexcan_fifo_push(const char *inst, int n_occupied) "%s: n_slots_occupied %i"
9 +flexcan_reset(const char *inst) "%s: resetting"
10 +flexcan_mem_op(const char *inst, const char *op, uint32_t v, int offset, const char *reg_name, int size) "%s: %s 0x%08x at offset %i register %s size %i"
11 +flexcan_get_timestamp(const char *inst, int64_t time_elapsed_ms, uint32_t bitrate, uint64_t cycles, uint32_t shift, uint32_t timestamp) "%s: time_elapsed %" PRIi64 "ms bitrate %ub/s cycles %" PRIu64 " shift %u timestamp 0x%04x"
12 +flexcan_get_bitrate(const char *inst, uint32_t pe_freq, uint32_t prediv, uint32_t s_freq, uint32_t tseg1, uint32_t tseg2, uint32_t quata_per_bit, uint32_t bitrate) "%s: pe_freq %uHz prescaler %u s_freq %uHz tseg1 %uq tseg2 %uq total %uq/b bitrate %ub/s"
13 +flexcan_timer_start(const char *inst, uint32_t bitrate, uint32_t value) "%s: bitrate %ub/s value 0x%04x"
14 +flexcan_timer_stop(const char *inst, uint32_t bitrate, uint32_t value) "%s: bitrate %ub/s value 0x%04x"
15 +flexcan_timer_overflow(const char *inst, int64_t current_time, int64_t timer_start, int64_t elapsed_ns) "%s: current_time %" PRIi64 "timer_start %" PRIi64 "elapsed_ns %" PRIi64
16 +flexcan_mb_rx_check_mb(const char *inst, int mbidx, const char *code, int is_matched, int is_ftr, int is_serviced, int is_locked) "%s: checking mb %i code %s is_matched %i is_free_to_receive %i is_serviced %i is_locked %i"
17 +flexcan_receive(const char *inst, size_t n_frames) "%s: received %zu frames"
18 +
19 # xlnx-zynqmp-can.c
20 xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) "ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x"
21 xlnx_can_reset(uint32_t val) "Resetting controller with value = 0x%08x"
include/hw/net/flexcan.h new
+145
@@ -0,0 +1,145 @@
1 +/*
2 + * QEMU model of the NXP FLEXCAN device.
3 + *
4 + * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>
5 + *
6 + * Based on CTU CAN FD emulation implemented by Jan Charvat.
7 + *
8 + * SPDX-License-Identifier: GPL-2.0-or-later
9 + */
10 +
11 +#ifndef HW_CAN_FLEXCAN_H
12 +#define HW_CAN_FLEXCAN_H
13 +
14 +#include "net/can_emu.h"
15 +#include "qom/object.h"
16 +#include "hw/misc/imx_ccm.h"
17 +
18 +#define FLEXCAN_FIFO_DEPTH 6
19 +#define FLEXCAN_MAILBOX_COUNT 64
20 +
21 +/**
22 + * Definitions of structs FlexcanRegs and FlexcanRegsMessageBuffer were
23 + * originally written for the Linux kernel by:
24 + * Andrey Volkov <andrey@volkov.fr>
25 + * Sascha Hauer <s.hauer@pengutronix.de>
26 + * Marc Kleine-Budde <mkl@pengutronix.de>
27 + * David Jander <david@protonic.nl>
28 + * and they have agreed to license them under GPL-2.0-or-later.
29 + */
30 +
31 +/* view of single message buffer registers */
32 +typedef struct FlexcanRegsMessageBuffer {
33 + uint32_t can_ctrl;
34 + uint32_t can_id;
35 + uint32_t data[2];
36 +} FlexcanRegsMessageBuffer;
37 +
38 +/* RX FIFO view of message buffer registers */
39 +typedef struct FlexcanRegsRXFifo {
40 + /* 6 message buffer deep queue, queue back first */
41 + FlexcanRegsMessageBuffer mb_back;
42 + FlexcanRegsMessageBuffer mbs_queue[FLEXCAN_FIFO_DEPTH - 1];
43 +
44 + /* number of filter elements active depends on ctrl2 | FLEXCAN_CTRL2_RFFN */
45 + uint32_t filter_table_els[128];
46 +} FlexcanRegsRXFifo;
47 +
48 +/* FlexCAN register in hw layout */
49 +typedef struct FlexcanRegs {
50 + uint32_t mcr; /* 0x00 */
51 + uint32_t ctrl; /* 0x04 - not affected by soft reset */
52 + uint32_t timer; /* 0x08 */
53 + uint32_t tcr; /* 0x0C */
54 + uint32_t rxmgmask; /* 0x10 - not affected by soft reset */
55 + uint32_t rx14mask; /* 0x14 - not affected by soft reset */
56 + uint32_t rx15mask; /* 0x18 - not affected by soft reset */
57 + uint32_t ecr; /* 0x1C */
58 + uint32_t esr; /* 0x20 */
59 + uint32_t imask2; /* 0x24 */
60 + uint32_t imask1; /* 0x28 */
61 + uint32_t iflag2; /* 0x2C */
62 + uint32_t iflag1; /* 0x30 */
63 + union { /* 0x34 */
64 + uint32_t gfwr_mx28; /* MX28, MX53 */
65 + uint32_t ctrl2; /* MX6, VF610 - not affected by soft reset */
66 + };
67 + uint32_t esr2; /* 0x38 */
68 + uint32_t imeur; /* 0x3C, unused */
69 + uint32_t lrfr; /* 0x40, unused */
70 + uint32_t crcr; /* 0x44 */
71 + uint32_t rxfgmask; /* 0x48 */
72 + uint32_t rxfir; /* 0x4C - not affected by soft reset */
73 + uint32_t cbt; /* 0x50, unused - not affected by soft reset */
74 + uint32_t _reserved2; /* 0x54 */
75 + uint32_t dbg1; /* 0x58, unused */
76 + uint32_t dbg2; /* 0x5C, unused */
77 + uint32_t _reserved3[8]; /* 0x60 */
78 + union { /* 0x80 - not affected by soft reset */
79 + uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT];
80 + FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT];
81 + FlexcanRegsRXFifo fifo;
82 + };
83 + uint32_t _reserved4[256]; /* 0x480 */
84 + uint32_t rximr[64]; /* 0x880 - not affected by soft reset */
85 + uint32_t _reserved5[24]; /* 0x980 */
86 + uint32_t gfwr_mx6; /* 0x9E0 - MX6 */
87 +
88 + /* the rest is unused except for SMB */
89 + uint32_t _reserved6[39]; /* 0x9E4 */
90 + uint32_t _rxfir[6]; /* 0xA80 */
91 + uint32_t _reserved8[2]; /* 0xA98 */
92 + uint32_t _rxmgmask; /* 0xAA0 */
93 + uint32_t _rxfgmask; /* 0xAA4 */
94 + uint32_t _rx14mask; /* 0xAA8 */
95 + uint32_t _rx15mask; /* 0xAAC */
96 + uint32_t tx_smb[4]; /* 0xAB0 */
97 + union { /* 0xAC0, used for SMB emulation */
98 + uint32_t rx_smb0_raw[4];
99 + FlexcanRegsMessageBuffer rx_smb0;
100 + };
101 + uint32_t rx_smb1[4]; /* 0xAD0 */
102 + uint32_t mecr; /* 0xAE0 */
103 + uint32_t erriar; /* 0xAE4 */
104 + uint32_t erridpr; /* 0xAE8 */
105 + uint32_t errippr; /* 0xAEC */
106 + uint32_t rerrar; /* 0xAF0 */
107 + uint32_t rerrdr; /* 0xAF4 */
108 + uint32_t rerrsynr; /* 0xAF8 */
109 + uint32_t errsr; /* 0xAFC */
110 + uint32_t _reserved7[64]; /* 0xB00 */
111 + uint32_t fdctrl; /* 0xC00 - not affected by soft reset */
112 + uint32_t fdcbt; /* 0xC04 - not affected by soft reset */
113 + uint32_t fdcrc; /* 0xC08 */
114 + uint32_t _reserved9[199]; /* 0xC0C */
115 + uint32_t tx_smb_fd[18]; /* 0xF28 */
116 + uint32_t rx_smb0_fd[18]; /* 0xF70 */
117 + uint32_t rx_smb1_fd[18]; /* 0xFB8 */
118 +} FlexcanRegs;
119 +
120 +typedef struct FlexcanState {
121 + SysBusDevice parent_obj;
122 +
123 + MemoryRegion iomem;
124 + IMXCCMState *ccm;
125 + qemu_irq irq;
126 +
127 + CanBusState *canbus;
128 + CanBusClientState bus_client;
129 +
130 + union {
131 + FlexcanRegs regs;
132 + uint32_t regs_raw[sizeof(FlexcanRegs) / 4];
133 + };
134 + int64_t timer_start;
135 + uint64_t last_rx_timer_cycles;
136 + int32_t locked_mbidx;
137 + int32_t smb_target_mbidx;
138 + uint32_t timer_freq;
139 +} FlexcanState;
140 +
141 +#define TYPE_CAN_FLEXCAN "flexcan"
142 +
143 +OBJECT_DECLARE_SIMPLE_TYPE(FlexcanState, CAN_FLEXCAN);
144 +
145 +#endif