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)