Am 2. Januar 2026 14:53:42 UTC schrieb Bernhard Beschow <[email protected]>:
>
>
>Am 15. Dezember 2025 20:03:12 UTC schrieb "Matyáš Bobek" 
><[email protected]>:
>> This commit adds the FlexCAN2 emulator implementation and
>> plugs it into Meson and Kconfig.
>> FlexCAN2 version can be found in i.MX6 SoCs and others.
>> 
>> More information about the implementation can be found in [1].
>> 
>> [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 <[email protected]>
>> ---
>> MAINTAINERS               |    1 +
>> hw/net/Kconfig            |    5 +
>> hw/net/can/flexcan.c      | 1469 +++++++++++++++++++++++++++++++++++++
>> hw/net/can/flexcan_regs.h |  188 +++++
>> hw/net/can/meson.build    |    1 +
>> hw/net/can/trace-events   |   18 +
>> include/hw/net/flexcan.h  |  153 ++++
>> 7 files changed, 1835 insertions(+)
>> create mode 100644 hw/net/can/flexcan.c
>> create mode 100644 hw/net/can/flexcan_regs.h
>> create mode 100644 include/hw/net/flexcan.h
>> 
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 63e9ba521b..a0b152939b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -2858,6 +2858,7 @@ W: https://canbus.pages.fel.cvut.cz/
>> F: net/can/*
>> F: hw/net/can/*
>> F: include/net/can_*.h
>> +F: include/hw/net/flexcan.h
>> F: docs/system/devices/can.rst
>> 
>> OpenPIC interrupt controller
>> diff --git a/hw/net/Kconfig b/hw/net/Kconfig
>> index 2b513d6895..160c311dcd 100644
>> --- a/hw/net/Kconfig
>> +++ b/hw/net/Kconfig
>> @@ -160,3 +160,8 @@ config CAN_CTUCANFD_PCI
>>     default y if PCI_DEVICES
>>     depends on PCI && CAN_CTUCANFD
>>     select CAN_BUS
>> +
>> +config CAN_FLEXCAN
>> +    bool
>> +    default y
>
>I think the "default y" isn't needed since CONFIG_FSL_IMX6 will select it.
>
>However, I need to add a "depends on IMX" to prevent linker errors. You might 
>be able to reproduce this by enabling other architectures than arm-softmmu.
>
>> +    select CAN_BUS
>> diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c
>> new file mode 100644
>> index 0000000000..1f50dd1d5b
>> --- /dev/null
>> +++ b/hw/net/can/flexcan.c
>> @@ -0,0 +1,1469 @@
>> +/*
>> + * QEMU model of the NXP FLEXCAN device.
>> + *
>> + * This implementation is based on the following reference manual:
>> + * i.MX 6Dual/6Quad Applications Processor Reference Manual
>> + * Document Number: IMX6DQRM, Rev. 6, 05/2020
>> + *
>> + * Copyright (c) 2025 Matyas Bobek <[email protected]>
>> + *
>> + * Based on CTU CAN FD emulation implemented by Jan Charvat.
>> + *
>> + * This code is licensed under the GPL version 2 or later.  See
>> + * the COPYING file in the top-level directory.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/log.h"
>> +#include "hw/sysbus.h"
>> +#include "qapi/error.h"
>> +#include "hw/irq.h"
>> +#include "migration/vmstate.h"
>> +#include "net/can_emu.h"
>> +#include "hw/qdev-properties.h"
>> +#include "trace.h"
>> +
>> +#include "hw/net/flexcan.h"
>> +#include "flexcan_regs.h"
>> +#include "qemu/timer.h"
>> +
>> +#define USE(var) (void)var;
>> +
>> +#define DEBUG_FLEXCAN 1
>> +#ifndef DEBUG_FLEXCAN
>> +#define DEBUG_FLEXCAN 0
>> +#endif
>> +
>> +/*
>> + * Indicates MB w/ received frame has not been serviced yet
>> + * This is an emulator-only flag in position of unused (reserved) bit
>> + * of message buffer control register
>> + */
>> +#define FLEXCAN_MB_CNT_NOT_SRV          BIT(23)
>> +/**
>> + * if no MB is locked, FlexcanState.locked_mb
>> + * is set to FLEXCAN_NO_MB_LOCKED
>> + */
>> +#define FLEXCAN_NO_MB_LOCKED            -1
>> +/**
>> + * if no frame is waiting in the SMB, FlexcanState.smb_target_mbid
>> + * is set to FLEXCAN_SMB_EMPTY
>> + */
>> +#define FLEXCAN_SMB_EMPTY               -1
>> +/**
>> + * When the module is disabled or in freeze mode,
>> + * the timer is not running. That is indicated by setting
>> + * FlexcanState.timer_start to FLEXCAN_TIMER_STOPPED.
>> + */
>> +#define FLEXCAN_TIMER_STOPPED           -1
>> +
>> +/**
>> + * defines the end of the memory space of the implemented registers
>> + *
>> + * also prevents addressing memory after FlexcanRegs end
>> + */
>> +#define FLEXCAN_ADDR_SPC_END offsetof(FlexcanRegs, _reserved6)
>> +QEMU_BUILD_BUG_ON(FLEXCAN_ADDR_SPC_END > sizeof(FlexcanRegs));
>> +
>> +/* These constants are returned by flexcan_fifo_rx() and flexcan_mb_rx(), */
>> +/* Retry the other receiving mechanism (ie. message bufer or mailbox). */
>> +#define FLEXCAN_RX_SEARCH_RETRY 0
>> +/* The frame was received and stored. */
>> +#define FLEXCAN_RX_SEARCH_ACCEPT 1
>> +/* The frame was filtered out and dropped. */
>> +#define FLEXCAN_RX_SEARCH_DROPPED 2
>> +
>> +/*
>> + * These constants are returned by flexcan_mb_rx_check_mb().
>> + * See flexcan_mb_rx_check_mb() kerneldoc for details.
>> + */
>> +#define FLEXCAN_CHECK_MB_NIL 0
>> +#define FLEXCAN_CHECK_MB_MATCH 3
>> +#define FLEXCAN_CHECK_MB_MATCH_NON_FREE 1
>> +#define FLEXCAN_CHECK_MB_MATCH_LOCKED 5
>> +
>> +static const FlexcanRegs flexcan_regs_write_mask = {
>> +    .mcr = 0xF6EB337F,
>> +    .ctrl = 0xFFFFFFFF,
>> +    .timer = 0xFFFFFFFF,
>> +    .tcr = 0xFFFFFFFF,
>> +    .rxmgmask = 0xFFFFFFFF,
>> +    .rx14mask = 0xFFFFFFFF,
>> +    .rx15mask = 0xFFFFFFFF,
>> +    .ecr = 0xFFFFFFFF,
>> +    .esr = 0xFFFFFFFF,
>> +    .imask2 = 0xFFFFFFFF,
>> +    .imask1 = 0xFFFFFFFF,
>> +    .iflag2 = 0,
>> +    .iflag1 = 0,
>> +    .ctrl2 = 0xFFFFFFFF,
>> +    .esr2 = 0,
>> +    .imeur = 0,
>> +    .lrfr = 0,
>> +    .crcr = 0,
>> +    .rxfgmask = 0xFFFFFFFF,
>> +    .rxfir = 0,
>> +    .cbt = 0,
>> +    ._reserved2 = 0,
>> +    .dbg1 = 0,
>> +    .dbg2 = 0,
>> +    .mbs = { [0 ... 63] = {
>> +        .can_ctrl = 0xFFFFFFFF & ~FLEXCAN_MB_CNT_NOT_SRV,
>> +        .can_id = 0xFFFFFFFF,
>> +        .data = { 0xFFFFFFFF, 0xFFFFFFFF },
>> +    } },
>> +    ._reserved4 = {0},
>> +    .rximr = { [0 ... 63] = 0xFFFFFFFF },
>> +    ._reserved5 = {0},
>> +    .gfwr_mx6 = 0xFFFFFFFF,
>> +    ._reserved6 = {0},
>> +    ._reserved8 = {0},
>> +    .rx_smb0_raw = {0, 0, 0, 0},
>> +    .rx_smb1 = {0, 0, 0, 0},
>> +};
>> +static const FlexcanRegs flexcan_regs_reset_mask = {
>> +    .mcr = 0x80000000,
>> +    .ctrl = 0xFFFFFFFF,
>> +    .timer = 0,
>> +    .tcr = 0,
>> +    .rxmgmask = 0xFFFFFFFF,
>> +    .rx14mask = 0xFFFFFFFF,
>> +    .rx15mask = 0xFFFFFFFF,
>> +    .ecr = 0,
>> +    .esr = 0,
>> +    .imask2 = 0,
>> +    .imask1 = 0,
>> +    .iflag2 = 0,
>> +    .iflag1 = 0,
>> +    .ctrl2 = 0xFFFFFFFF,
>> +    .esr2 = 0,
>> +    .imeur = 0,
>> +    .lrfr = 0,
>> +    .crcr = 0,
>> +    .rxfgmask = 0xFFFFFFFF,
>> +    .rxfir = 0xFFFFFFFF,
>> +    .cbt = 0,
>> +    ._reserved2 = 0,
>> +    .dbg1 = 0,
>> +    .dbg2 = 0,
>> +    .mb = {0xFFFFFFFF},
>> +    ._reserved4 = {0},
>> +    .rximr = {0xFFFFFFFF},
>> +    ._reserved5 = {0},
>> +    .gfwr_mx6 = 0,
>> +    ._reserved6 = {0},
>> +    ._reserved8 = {0},
>> +    .rx_smb0_raw = {0, 0, 0, 0},
>> +    .rx_smb1 = {0, 0, 0, 0},
>> +};
>> +
>> +#if DEBUG_FLEXCAN
>> +
>> +#define DPRINTF(fmt, args...) \
>> +    fprintf(stderr, "(%p)[%s]%s: " fmt , (void *)s, TYPE_CAN_FLEXCAN, \
>> +            __func__, ##args);
>> +
>> +#else /* DEBUG_FLEXCAN */
>> +
>> +#define DPRINTF(fmt, args...) do { } while (0)
>> +
>> +#endif /* DEBUG_FLEXCAN */
>
>Please no new DPRINTF. Use tracing and logging instead.
>
>> +
>> +#define FLEXCAN_DBG_BUF_LEN 16
>> +
>> +static const char *flexcan_dbg_mb_code_strs[16] = {
>> +    "INACTIVE_RX",
>> +    "FULL",
>> +    "EMPTY",
>> +    "OVERRUN",
>> +    "INACTIVE_TX",
>> +    "RANSWER",
>> +    "DATA",
>> +    "TANSWER"
>> +};
>> +
>> +/**
>> + * flexcan_dbg_mb_code() - Get the string representation of a mailbox code
>> + * @mb_ctrl: The mailbox control register value
>> + * @buf: The buffer to store the string representation
>> + *
>> + * Return: Either constant string or string formatted into @buf
>> + */
>> +static const char *flexcan_dbg_mb_code(uint32_t mb_ctrl, char *buf)
>> +{
>> +    uint32_t code = mb_ctrl & FLEXCAN_MB_CODE_MASK;
>> +    uint32_t code_idx = code >> 24;
>> +    if (code == FLEXCAN_MB_CODE_TX_ABORT) {
>> +        return "ABORT";
>> +    }
>> +
>> +    const char *code_str = flexcan_dbg_mb_code_strs[code_idx >> 1];
>> +    if (code_idx & 1) {
>> +        g_snprintf(buf, FLEXCAN_DBG_BUF_LEN, "%s+BUSY", code_str);
>> +        return buf;
>> +    } else {
>> +        return code_str;
>> +    }
>> +}
>> +
>> +static const char *flexcan_dbg_reg_name_fixed(hwaddr addr)
>> +{
>> +    if (addr >= FLEXCAN_ADDR_SPC_END) {
>> +        return "OUT-OF-RANGE";
>> +    }
>> +
>> +    switch (addr) {
>> +    case offsetof(FlexcanRegs, mcr):
>> +        return "MCR";
>> +    case offsetof(FlexcanRegs, ctrl):
>> +        return "CTRL";
>> +    case offsetof(FlexcanRegs, timer):
>> +        return "TIMER";
>> +    case offsetof(FlexcanRegs, esr):
>> +        return "ESR";
>> +    case offsetof(FlexcanRegs, rxmgmask):
>> +        return "RXMGMASK";
>> +    case offsetof(FlexcanRegs, rx14mask):
>> +        return "RX14MASK";
>> +    case offsetof(FlexcanRegs, rx15mask):
>> +        return "RX15MASK";
>> +    case offsetof(FlexcanRegs, rxfgmask):
>> +        return "RXFGMASK";
>> +    case offsetof(FlexcanRegs, ecr):
>> +        return "ECR";
>> +    case offsetof(FlexcanRegs, ctrl2):
>> +        return "CTRL2";
>> +    case offsetof(FlexcanRegs, imask2):
>> +        return "IMASK2";
>> +    case offsetof(FlexcanRegs, imask1):
>> +        return "IMASK1";
>> +    case offsetof(FlexcanRegs, iflag2):
>> +        return "IFLAG2";
>> +    case offsetof(FlexcanRegs, iflag1):
>> +        return "IFLAG1";
>> +    }
>> +    return NULL;
>> +}
>> +
>> +static inline void flexcan_trace_mem_op(FlexcanState *s, hwaddr addr,
>> +                                        uint32_t value, int size, bool 
>> is_wr)
>> +{
>> +    if (trace_event_get_state_backends(TRACE_FLEXCAN_MEM_OP)) {
>> +        const char *reg_name = "unknown";
>> +        char reg_name_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
>> +        const char *reg_name_fixed = flexcan_dbg_reg_name_fixed(addr);
>> +        const char *op_string = is_wr ? "write" : "read";
>> +
>> +        if (reg_name_fixed) {
>> +            reg_name = reg_name_fixed;
>> +        } else if (addr >= 0x80 && addr < 0x480) {
>> +            int mbidx = (addr - 0x80) / 16;
>> +            g_snprintf(reg_name_buf, sizeof(reg_name_buf), "MB%i", mbidx);
>> +            reg_name = reg_name_buf;
>> +        } else if (addr >= 0x880 && addr < 0x9e0) {
>> +            int id = (addr - 0x880) / 4;
>> +            g_snprintf(reg_name_buf, sizeof(reg_name_buf), "RXIMR%i", id);
>> +            reg_name = reg_name_buf;
>> +        }
>> +
>> +        trace_flexcan_mem_op(s, op_string, value, addr, reg_name, size);
>
>Better use "DEVICE(s)->canonical_path" instead of the "s" parameter which 
>results in much more readable trace messages. This applies to all trace 
>functions.
>
>> +    }
>> +}
>> +
>> +static const struct MemoryRegionOps flexcan_ops = {
>> +    .read = flexcan_mem_read,
>> +    .write = flexcan_mem_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>
>Please use DEVICE_LITTLE_ENDIAN for now since DEVICE_NATIVE_ENDIAN is 
>deprecated, and checkpatch.pl actually flags it. While there may be big endian 
>variants of this IP core, we don't need to worry about these at the moment 
>since this device model already depends on the IMX clock module.
>
>> +    .valid = {
>> +        .min_access_size = 1,
>> +        .max_access_size = 4,
>> +        .unaligned = true,
>> +        .accepts = flexcan_mem_accepts
>> +    },
>> +    .impl = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +        .unaligned = false
>> +    },
>> +};
>> +
>> +static int flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *frame);
>> +static void flexcan_mb_unlock(FlexcanState *s);
>> +
>> +/* ========== Mailbox Utils ========== */
>> +
>> +/**
>> + * flexcan_mailbox_count() - Get number of enabled mailboxes
>> + * @s: FlexCAN device pointer
>> + *
>> + * Count is based on MCR[MAXMB] field. Note that some of those mailboxes
>> + * might be part of queue or queue ID filters or ordinary message buffers.
>> + */
>> +static inline int flexcan_enabled_mailbox_count(const FlexcanState *s)
>> +{
>> +    return (s->regs.mcr & FLEXCAN_MCR_MAXMB(UINT32_MAX)) + 1;
>> +}
>> +
>> +/**
>> + * flexcan_get_first_message_buffer() - Get pointer to first message buffer
>> + * @s: FlexCAN device pointer
>> + *
>> + * In context of this function, message buffer means a mailbox which is not
>> + * a queue element nor a queue filter. Note this function does not take
>> + * MCR[MAXMB] into account, meaning that the returned mailbox
>> + * might be disabled.
>> + */
>> +static FlexcanRegsMessageBuffer *flexcan_get_first_message_buffer(
>> +                                 FlexcanState *s)
>> +{
>> +    if (s->regs.mcr & FLEXCAN_MCR_FEN) {
>> +        int rffn = (s->regs.ctrl2 & FLEXCAN_CTRL2_RFFN(UINT32_MAX)) >> 24;
>> +        return s->regs.mbs + 8 + 2 * rffn;
>> +    } else {
>> +        return s->regs.mbs;
>> +    }
>> +}
>> +
>> +/**
>> + * flexcan_get_last_enabled_mailbox() - Get pointer to last enabled mailbox.
>> + * @s: FlexCAN device pointer
>> + *
>> + * When used with flexcan_get_first_message_buffer(), all mailboxes *ptr in
>> + * range `first_message_buffer() <= ptr <= last_enabled_mailbox` are valid
>> + * message buffer mailboxes.
>> + *
>> + * Return: Last enabled mailbox in MCR[MAXMB] sense. The mailbox might be
>> + * of any type.
>> + */
>> +static inline FlexcanRegsMessageBuffer *flexcan_get_last_enabled_mailbox(
>> +                                 FlexcanState *s)
>> +{
>> +    return s->regs.mbs + flexcan_enabled_mailbox_count(s);
>> +}
>> +
>> +/**
>> + * flexcan_get_first_filter_mailbox() - Get pointer to first queue filter.
>> + * @s: FlexCAN device pointer
>> + *
>> + * This function does not check if FIFO is enabled.
>> + *
>> + * Return: Pointer to first queue filter element.
>> + */
>> +static inline uint32_t *flexcan_get_first_filter_mailbox(FlexcanState *s)
>> +{
>> +    return (uint32_t *)(s->regs.mbs + 6);
>> +}
>> +
>> +/**
>> + * flexcan_get_last_filter_mailbox() - Get pointer to last queue filter.
>> + * @s: FlexCAN device pointer
>> + *
>> + * This function does not check if FIFO is enabled.
>> + * All words in range [flexcan_get_first_filter_mailbox(),
>> + * flexcan_get_last_filter_mailbox()] are queue filter elements, if queue
>> + * is enabled.
>> + *
>> + * Return: Pointer to last queue filter element.
>> + */
>> +static inline uint32_t *flexcan_get_last_filter_mailbox(FlexcanState *s)
>> +{
>> +    /* adding three to get pointer to the last word of the mailbox */
>> +    uint32_t *last_enabled_elem =
>> +        ((uint32_t *)flexcan_get_last_enabled_mailbox(s)) + 3;
>> +
>> +    int rffn = (s->regs.ctrl2 & FLEXCAN_CTRL2_RFFN(UINT32_MAX)) >> 24;
>> +    uint32_t *last_elem = (uint32_t *)(s->regs.mbs + 8 + 2 * rffn) - 1;
>> +
>> +    return MIN(last_elem, last_enabled_elem);
>> +}
>
>The above two functions seem to be unused. Please remove.
>
>> +
>> +/* ========== Free-running Timer ========== */
>> +static inline int64_t flexcan_get_time(void)
>> +{
>> +    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> +}
>> +
>> +/**
>> + * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)
>> + * @s: FlexCAN device pointer
>> + *
>> + * The bitrate is determined by FlexCAN configuration in CTRL1 register,
>> + * and CCM co
>> + */
>> +static uint32_t flexcan_get_bitrate(FlexcanState *s)
>> +{
>> +    uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 
>> 24;
>> +    uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;
>> +    uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;
>> +    uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;
>> +
>> +    /* s_clock: CAN clock from CCM divivded by the prescaler */
>> +    assert(s->ccm);
>> +    uint32_t pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);
>> +    uint32_t s_freq = pe_freq / (1 + conf_presdiv);
>> +
>> +    /* N of time quanta for segements */
>> +    uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;
>> +    uint32_t tseg2 = 1 + conf_pseg2;
>> +    uint32_t total_qpb = 1 + tseg1 + tseg2;
>> +
>> +    uint32_t bitrate = s_freq / total_qpb;
>> +
>> +    trace_flexcan_get_bitrate(s, pe_freq, 1 + conf_presdiv, s_freq, tseg1,
>> +                              tseg2, total_qpb, bitrate);
>> +    return bitrate;
>> +}
>> +
>> +/**
>> + * int128_mul_6464() - Multiply two 64-bit integers into a 128-bit one
>> + */
>> +static Int128 int128_muls_6464(int64_t ai, int64_t bi)
>> +{
>> +    uint64_t l, h;
>> +
>> +    muls64(&l, &h, ai, bi);
>> +    return int128_make128(l, h);
>> +}
>> +
>> +/**
>> + * flexcan_get_timestamp() - Get current value of the 16-bit free-running 
>> timer
>> + * @s: FlexCAN device pointer
>> + * @mk_unique: if true, make the timestamp unique by incrementing it if 
>> needed
>> + */
>> +static uint32_t flexcan_get_timestamp(FlexcanState *s, bool mk_unique)
>> +{
>> +    if (s->timer_start == FLEXCAN_TIMER_STOPPED) {
>> +        /* timer is not running, return last value */
>> +        trace_flexcan_get_timestamp(s, -1, 0, 0, 0, s->regs.timer);
>> +        return s->regs.timer;
>> +    }
>> +
>> +    int64_t current_time = flexcan_get_time();
>> +    int64_t elapsed_time_ns = current_time - s->timer_start;
>> +    int64_t elapsed_time_ms = elapsed_time_ns / 1000000;
>> +    if (elapsed_time_ns < 0) {
>> +        DPRINTF("timer overflow current_time=%li "
>> +                "timer_start=%li elapsed_time_ns=%li\n",
>> +                current_time, s->timer_start, elapsed_time_ns);
>> +        return 0xFFFF;
>> +    }
>> +
>> +    Int128 nanoseconds_in_second = int128_makes64(1000000000);
>> +    Int128 ncycles = int128_muls_6464(s->timer_freq, elapsed_time_ns);
>> +    Int128 cycles128 = int128_divs(ncycles, nanoseconds_in_second);
>> +    /* 64 bits hold for over 50k years at 10MHz */
>> +    uint64_t cycles = int128_getlo(cycles128);
>> +
>> +    uint32_t shift = 0;
>> +    if (mk_unique && cycles <= s->last_rx_timer_cycles) {
>> +        shift = 1;
>> +        cycles = s->last_rx_timer_cycles + shift;
>> +    }
>> +
>> +    s->last_rx_timer_cycles = cycles;
>> +    uint32_t rv = (uint32_t)cycles & 0xFFFF;
>> +
>> +    trace_flexcan_get_timestamp(s, elapsed_time_ms, s->timer_freq,
>> +                                cycles, shift, rv);
>> +    return rv;
>> +}
>> +
>> +/**
>> + * flexcan_timer_start() - Start the free-running timer
>> + * @s: FlexCAN device pointer
>> + *
>> + * This should be called when the module leaves freeze mode.
>> + */
>> +static void flexcan_timer_start(FlexcanState *s)
>> +{
>> +    if (s->timer_start != FLEXCAN_TIMER_STOPPED) {
>> +        DPRINTF("module brought up, but timer is already running: "
>> +                "value=%" PRIu64 "\n", s->timer_start);
>> +    }
>> +    s->timer_freq = flexcan_get_bitrate(s);
>> +    s->timer_start = flexcan_get_time();
>> +    s->last_rx_timer_cycles = 0;
>> +
>> +    trace_flexcan_timer_start(s, s->timer_freq, s->regs.timer);
>> +}
>> +
>> +/**
>> + * flexcan_timer_stop() - Stop the free-running timer
>> + * @s: FlexCAN device pointer
>> + *
>> + * This should be called when the module enters freeze mode.
>> + * Stores the current timestamp in the TIMER register.
>> + */
>> +static void flexcan_timer_stop(FlexcanState *s)
>> +{
>> +    s->regs.timer = flexcan_get_timestamp(s, false);
>> +    s->timer_start = FLEXCAN_TIMER_STOPPED;
>> +
>> +    trace_flexcan_timer_stop(s, s->timer_freq, s->regs.timer);
>> +}
>> +
>> +/* ========== IRQ handling ========== */
>> +/**
>> + * flexcan_irq_update() - Update qemu_irq line based on interrupt registers
>> + * @s: FlexCAN device pointer
>> + */
>> +static void flexcan_irq_update(FlexcanState *s)
>> +{
>> +    /* these are all interrupt sources from FlexCAN */
>> +    /* mailbox interrupt sources */
>> +    uint32_t mb_irqs1 = s->regs.iflag1 & s->regs.imask1;
>> +    uint32_t mb_irqs2 = s->regs.iflag2 & s->regs.imask2;
>> +
>> +    /**
>> +     * these interrupts aren't currently used and they can never be raised
>> +     *
>> +     * bool irq_wake_up = (s->regs.mcr & FLEXCAN_MCR_WAK_MSK) &&
>> +     *                    (s->regs.ecr & FLEXCAN_ESR_WAK_INT);
>> +     * bool irq_bus_off = (s->regs.ctrl & FLEXCAN_CTRL_BOFF_MSK) &&
>> +     *                    (s->regs.ecr & FLEXCAN_ESR_BOFF_INT);
>> +     * bool irq_error = (s->regs.ctrl & FLEXCAN_CTRL_ERR_MSK) &&
>> +     *                  (s->regs.ecr & FLEXCAN_ESR_ERR_INT);
>> +     * bool irq_tx_warn = (s->regs.ctrl & FLEXCAN_CTRL_TWRN_MSK) &&
>> +     *                    (s->regs.ecr & FLEXCAN_ESR_TWRN_INT);
>> +     * bool irq_rx_warn = (s->regs.ctrl & FLEXCAN_CTRL_RWRN_MSK) &&
>> +     *                    (s->regs.ecr & FLEXCAN_ESR_RWRN_INT);
>> +     */
>> +
>> +    int irq_setting = (mb_irqs1 | mb_irqs2) ? 1 : 0;
>> +    trace_flexcan_irq_update(s, mb_irqs1, mb_irqs2, irq_setting);
>> +
>> +    qemu_set_irq(s->irq, irq_setting);
>> +}
>> +
>> +/**
>> + * flexcan_irq_iflag_set() - Set IFLAG bit corresponding to MB mbidx
>> + * @s: FlexCAN device pointer
>> + * @mbidx: mailbox index
>> + */
>> +static void flexcan_irq_iflag_set(FlexcanState *s, int mbidx)
>> +{
>> +    if (mbidx < 32) {
>> +        s->regs.iflag1 |= BIT(mbidx);
>> +    } else {
>> +        s->regs.iflag2 |= BIT(mbidx - 32);
>> +    }
>> +}
>> +
>> +/**
>> + * flexcan_irq_iflag_clear() - Clear IFLAG bit corresponding to MB mbidx
>> + * @s: FlexCAN device pointer
>> + * @mbidx: mailbox index
>> + */
>> +static void flexcan_irq_iflag_clear(FlexcanState *s, int mbidx)
>> +{
>> +    if (mbidx < 32) {
>> +        s->regs.iflag1 &= ~BIT(mbidx);
>> +    } else {
>> +        s->regs.iflag2 &= ~BIT(mbidx - 32);
>> +    }
>> +}
>> +
>> +/* ========== RESET ========== */
>> +static void flexcan_reset_local_state(FlexcanState *s)
>> +{
>> +    uint32_t *reset_mask = (uint32_t *)&flexcan_regs_reset_mask;
>> +    for (int i = 0; i < (sizeof(FlexcanRegs) / 4); i++) {
>> +        s->regs_raw[i] &= reset_mask[i];
>> +    }
>> +
>> +    s->regs.mcr |= 0x5980000F;
>> +    s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
>> +    s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
>> +    s->timer_start = FLEXCAN_TIMER_STOPPED;
>> +
>> +    trace_flexcan_reset(s);
>> +}
>> +
>> +static void flexcan_soft_reset(FlexcanState *s)
>> +{
>> +    if (s->regs.mcr & FLEXCAN_MCR_LPM_ACK) {
>> +        g_autofree char *path = object_get_canonical_path(OBJECT(s));
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "%s: invalid soft reset request in low-power mode",
>> +                      path);
>
>Here you can also use "DEVICE(s)->canonical_path", eliminating the dynamic 
>allocation and cleanup logic entirely. Same for other cases.
>
>> +    }
>> +
>> +    flexcan_reset_local_state(s);
>> +}
>> +
>> +static void flexcan_reset_enter(Object *obj, ResetType type)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>> +
>> +    memset(&s->regs, 0, sizeof(s->regs));
>> +    flexcan_reset_local_state(s);
>> +}
>> +
>> +static void flexcan_reset_hold(Object *obj, ResetType type)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>> +
>> +    flexcan_irq_update(s);
>> +}
>> +
>> +
>> +/* ========== Operation mode control ========== */
>> +/**
>> + * flexcan_update_esr() - Update ESR based on mode and CAN bus connection 
>> state
>> + * @s: FlexCAN device pointer
>> + */
>> +static void flexcan_update_esr(FlexcanState *s)
>> +{
>> +    bool is_running = (s->regs.mcr & FLEXCAN_MCR_NOT_RDY) == 0;
>> +    /* potentially, there could be other influences on ESR[SYNCH] */
>> +
>> +    if (is_running && s->canbus) {
>> +        s->regs.esr |= FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE;
>> +    } else {
>> +        s->regs.esr &= ~(FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE);
>> +    }
>> +}
>> +
>> +/**
>> + * flexcan_update_esr() - Process MCR write
>> + * @s: FlexCAN device pointer
>> + * @pv: previously set MCR value
>> + *
>> + * This function expects the new MCR value to be already written in 
>> s->regs.mcr.
>> + */
>> +static void flexcan_set_mcr(FlexcanState *s, const uint32_t pv)
>> +{
>> +    uint32_t cv = s->regs.mcr;
>> +
>> +    /* -- module disable mode -- */
>> +    if (!(pv & FLEXCAN_MCR_MDIS) && (cv & FLEXCAN_MCR_MDIS)) {
>> +        /* transition to Module Disable mode */
>> +        cv |= FLEXCAN_MCR_LPM_ACK;
>> +    } else if ((pv & FLEXCAN_MCR_MDIS) && !(cv & FLEXCAN_MCR_MDIS)) {
>> +        /* transition from Module Disable mode */
>> +        cv &= ~FLEXCAN_MCR_LPM_ACK;
>> +    }
>> +
>> +    /* -- soft reset -- */
>> +    if (!(cv & FLEXCAN_MCR_LPM_ACK) && (cv & FLEXCAN_MCR_SOFTRST)) {
>> +        flexcan_soft_reset(s);
>> +        cv = s->regs.mcr;
>> +    }
>> +
>> +    /* -- freeze mode -- */
>> +    if (!(cv & FLEXCAN_MCR_LPM_ACK) &&
>> +        (cv & FLEXCAN_MCR_FRZ) &&
>> +        (cv & FLEXCAN_MCR_HALT)) {
>> +        cv |= FLEXCAN_MCR_FRZ_ACK;
>> +    } else {
>> +        cv &= ~FLEXCAN_MCR_FRZ_ACK;
>> +    }
>> +
>> +    /* -- fifo mode -- */
>> +    if (
>> +        ((pv & FLEXCAN_MCR_FEN) && !(cv & FLEXCAN_MCR_FEN)) ||
>> +        (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN))
>> +    ) {
>> +        /* clear iflags used by fifo */
>> +        s->regs.iflag1 &= ~(
>> +            FLEXCAN_IFLAG_RX_FIFO_AVAILABLE |
>> +            FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
>> +            FLEXCAN_IFLAG_RX_FIFO_WARN
>> +        );
>> +    }
>> +    if (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN)) {
>> +        /* zero out fifo region, we rely on zeroed can_ctrl for empty slots 
>> */
>> +        memset(s->regs.mbs, 0,
>> +               FLEXCAN_FIFO_DEPTH * sizeof(FlexcanRegsMessageBuffer));
>> +    }
>> +
>> +    /*
>> +     * assert NOT_RDY bit if in disable,
>> +     * stop (not implemented) or freeze mode
>> +     */
>> +    if ((cv & FLEXCAN_MCR_LPM_ACK) || (cv & FLEXCAN_MCR_FRZ_ACK)) {
>> +        cv |= FLEXCAN_MCR_NOT_RDY;
>> +    } else {
>> +        cv &= ~FLEXCAN_MCR_NOT_RDY;
>> +    }
>> +
>> +    if ((pv & FLEXCAN_MCR_NOT_RDY) && !(cv & FLEXCAN_MCR_NOT_RDY)) {
>> +        /* module went up, start the timer */
>> +        flexcan_timer_start(s);
>> +    } else if (!(pv & FLEXCAN_MCR_NOT_RDY) && (cv & FLEXCAN_MCR_NOT_RDY)) {
>> +        /* module went down, store the current timer value */
>> +        flexcan_timer_stop(s);
>> +    }
>> +
>> +    s->regs.mcr = cv;
>> +    flexcan_update_esr(s);
>> +    trace_flexcan_set_mcr(
>> +        s,
>> +        cv & FLEXCAN_MCR_LPM_ACK ? "DISABLED" : "ENABLED",
>> +        (cv & FLEXCAN_MCR_FRZ_ACK || cv & FLEXCAN_MCR_LPM_ACK) ?
>> +            "FROZEN" : "RUNNING",
>> +        cv & FLEXCAN_MCR_FEN ? "FIFO" : "MAILBOX",
>> +        cv & FLEXCAN_MCR_NOT_RDY ? "NOT_RDY" : "RDY",
>> +        s->regs.esr & FLEXCAN_ESR_SYNCH ? "SYNC" : "NOSYNC"
>> +    );
>> +}
>> +
>> +/* ========== TX ========== */
>> +static void flexcan_transmit(FlexcanState *s, int mbidx)
>> +{
>> +    FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
>> +    qemu_can_frame frame = {
>> +        .flags = 0,
>> +    };
>> +
>> +    if ((s->regs.ctrl & FLEXCAN_CTRL_LOM) ||
>> +        (s->regs.mcr & FLEXCAN_MCR_NOT_RDY)) {
>> +        /* no transmiting in listen-only, freeze or low-power mode */
>> +        return;
>> +    }
>> +
>> +    if (mb->can_ctrl & FLEXCAN_MB_CNT_IDE) {
>> +        /* 29b ID stored in bits [0, 29) */
>> +        uint32_t id = mb->can_id & 0x1FFFFFFF;
>> +        frame.can_id = id | QEMU_CAN_EFF_FLAG;
>> +    } else {
>> +        /* 11b ID stored in bits [18, 29) */
>> +        uint32_t id = (mb->can_id & (0x7FF << 18)) >> 18;
>> +        frame.can_id = id;
>> +    }
>> +
>> +    frame.can_dlc = (mb->can_ctrl & (0xF << 16)) >> 16;
>> +
>> +    uint32_t *frame_data = (uint32_t *)&frame.data;
>> +    for (int i = 0; i < 2; i++) {
>> +        stl_be_p(&frame_data[i], mb->data[i]);
>> +    }
>> +
>> +    if (!(s->regs.mcr & FLEXCAN_MCR_SRX_DIS)) {
>> +        /* self-reception */
>> +        flexcan_mb_rx(s, &frame);
>> +    }
>> +    if (!(s->regs.ctrl & FLEXCAN_CTRL_LPB)) {
>> +        /* send to bus if not in loopback mode */
>> +        if (s->canbus) {
>> +            can_bus_client_send(&s->bus_client, &frame, 1);
>> +        } else {
>> +            /* todo: raise error (no ack) */
>> +        }
>> +    }
>> +
>> +    uint32_t timestamp = flexcan_get_timestamp(s, true);
>> +    mb->can_ctrl &= ~(FLEXCAN_MB_CODE_MASK | FLEXCAN_MB_CNT_TIMESTAMP_MASK);
>> +    mb->can_ctrl |= FLEXCAN_MB_CODE_TX_INACTIVE |
>> +                    FLEXCAN_MB_CNT_TIMESTAMP(timestamp);
>> +
>> +    /* todo: compute the CRC */
>> +    s->regs.crcr = FLEXCAN_CRCR_TXCRC(0) | FLEXCAN_CRCR_MBCRC(mbidx);
>> +
>> +    flexcan_irq_iflag_set(s, mbidx);
>> +}
>> +
>> +static void flexcan_mb_write(FlexcanState *s, int mbid)
>> +{
>> +    FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
>> +
>> +    bool is_mailbox = (mb <= flexcan_get_last_enabled_mailbox(s)) &&
>> +                     (mb >= flexcan_get_first_message_buffer(s));
>> +
>> +    if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_WRITE)) {
>> +        char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
>> +        const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, 
>> code_str_buf);
>> +        trace_flexcan_mb_write(s, mbid, code_str, is_mailbox, mb->can_ctrl,
>> +                               mb->can_id);
>> +    }
>> +
>> +    if (!is_mailbox) {
>> +        /**
>> +         * Disabled mailbox or mailbox in region of queue filters
>> +         * was updated. Either way there is nothing to do.
>> +         */
>> +        return;
>> +    }
>> +
>> +    /* any write to message buffer clears the not_serviced flag */
>> +    mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
>> +
>> +    /**
>> +     * todo: search for active tx mbs on transition from freeze/disable mode
>> +     */
>> +    switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
>> +    case FLEXCAN_MB_CODE_TX_INACTIVE:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_INACTIVE:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_EMPTY:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_FULL:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_RANSWER:
>> +        break;
>> +
>> +    case FLEXCAN_MB_CODE_TX_DATA:
>> +        flexcan_transmit(s, mbid);
>> +        break;
>> +    case FLEXCAN_MB_CODE_TX_ABORT:
>> +        /*
>> +         * as transmission is instant, it can never be aborted
>> +         * we need to set CODE in C/S back to the previous code
>> +         */
>> +        mb->can_ctrl &= ~FLEXCAN_MB_CODE(1);
>> +        break;
>> +    case FLEXCAN_MB_CODE_TX_TANSWER:
>> +        break;
>> +    default:
>> +        /* prevent setting the busy bit */
>> +        mb->can_ctrl &= ~FLEXCAN_MB_CODE_RX_BUSY_BIT;
>> +        break;
>> +  }
>> +
>> +}
>> +
>> +/* ========== RX ========== */
>> +static void flexcan_mb_move_in(FlexcanState *s, const qemu_can_frame *frame,
>> +                               FlexcanRegsMessageBuffer *target_mb)
>> +{
>> +    memset(target_mb, 0, sizeof(FlexcanRegsMessageBuffer));
>> +
>> +    uint32_t frame_len = frame->can_dlc;
>> +    if (frame_len > 8) {
>> +        frame_len = 8;
>> +    }
>> +    uint32_t *frame_data = (uint32_t *)&frame->data;
>> +    for (int i = 0; i < 2; i++) {
>> +        target_mb->data[i] = ldl_be_p(&frame_data[i]);
>> +    }
>> +
>> +    int timestamp = flexcan_get_timestamp(s, true);
>> +    uint32_t new_code = 0;
>> +
>> +    switch (target_mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
>> +    case FLEXCAN_MB_CODE_RX_FULL:
>> +    case FLEXCAN_MB_CODE_RX_OVERRUN:
>> +        if (target_mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV) {
>> +            new_code = FLEXCAN_MB_CODE_RX_OVERRUN;
>> +        } else {
>> +            new_code = FLEXCAN_MB_CODE_RX_FULL;
>> +        }
>> +        break;
>> +    case FLEXCAN_MB_CODE_RX_RANSWER:
>> +        assert(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS);
>> +        new_code = FLEXCAN_MB_CODE_TX_TANSWER;
>> +        break;
>> +    default:
>> +        new_code = FLEXCAN_MB_CODE_RX_FULL;
>> +    }
>> +
>> +    target_mb->can_ctrl = new_code
>> +        | FLEXCAN_MB_CNT_TIMESTAMP(timestamp)
>> +        | FLEXCAN_MB_CNT_LENGTH(frame_len)
>> +        | FLEXCAN_MB_CNT_NOT_SRV
>> +        | FLEXCAN_MB_CNT_SRR; /* always set for received frames */
>> +    if (frame->can_id & QEMU_CAN_RTR_FLAG) {
>> +        target_mb->can_ctrl |= FLEXCAN_MB_CNT_RTR;
>> +    }
>> +
>> +    if (frame->can_id & QEMU_CAN_EFF_FLAG) {
>> +        target_mb->can_ctrl |= FLEXCAN_MB_CNT_IDE;
>> +        target_mb->can_id |= frame->can_id & QEMU_CAN_EFF_MASK;
>> +    } else {
>> +        target_mb->can_id |= (frame->can_id & QEMU_CAN_SFF_MASK) << 18;
>> +    }
>> +}
>> +static void flexcan_mb_lock(FlexcanState *s, int mbidx)
>> +{
>> +    FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];
>> +    if ((mb > flexcan_get_last_enabled_mailbox(s)) ||
>> +        (mb < flexcan_get_first_message_buffer(s))) {
>> +        return;
>> +    }
>> +    switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
>> +    case FLEXCAN_MB_CODE_RX_FULL:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_OVERRUN:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_RANSWER:
>> +        /* continue */
>> +        trace_flexcan_mb_lock(s, mbidx, 1);
>> +        break;
>> +    default:
>> +        trace_flexcan_mb_lock(s, mbidx, 0);
>> +        return;
>> +    }
>> +
>> +    s->locked_mbidx = mbidx;
>> +}
>> +
>> +static void flexcan_mb_unlock(FlexcanState *s)
>> +{
>> +    if (s->locked_mbidx == FLEXCAN_NO_MB_LOCKED) {
>> +        return;
>> +    }
>> +
>> +    int locked_mbidx = s->locked_mbidx;
>
>All variable declarations should be at the top of a block. I'm not sure why 
>but this is a common request, see e.g. 
>https://lore.kernel.org/qemu-devel/[email protected]/
> .
>
>> +    assert(locked_mbidx >= 0 && locked_mbidx < FLEXCAN_MAILBOX_COUNT);
>> +    FlexcanRegsMessageBuffer *locked_mb = &s->regs.mbs[locked_mbidx];
>> +    s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;
>> +
>> +    if (locked_mb >= flexcan_get_first_message_buffer(s) &&
>> +        locked_mb <= flexcan_get_last_enabled_mailbox(s)
>> +    ) {
>> +        /* mark the message buffer as serviced */
>> +        locked_mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;
>> +    }
>> +
>> +    /* try move in from SMB */
>> +    bool has_pending_frame = locked_mbidx == s->smb_target_mbidx;
>> +    trace_flexcan_mb_unlock(s, locked_mbidx,
>> +                            has_pending_frame ? " PENDING FRAME IN SMB" : 
>> "");
>> +
>> +    /* todo: in low-power modes, this should be postponed until exit */
>> +    if (has_pending_frame) {
>> +        FlexcanRegsMessageBuffer *target_mb = &s->regs.mbs[locked_mbidx];
>> +        memcpy(target_mb, &s->regs.rx_smb0, 
>> sizeof(FlexcanRegsMessageBuffer));
>> +
>> +        memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
>> +        s->locked_mbidx = FLEXCAN_SMB_EMPTY;
>> +
>> +        flexcan_irq_iflag_set(s, locked_mbidx);
>> +    }
>> +}
>> +
>> +bool flexcan_can_receive(CanBusClientState *client)
>> +{
>> +    FlexcanState *s = container_of(client, FlexcanState, bus_client);
>> +    return !(s->regs.mcr & FLEXCAN_MCR_NOT_RDY);
>> +}
>> +
>> +/* --------- RX FIFO ---------- */
>> +
>> +/**
>> + * flexcan_fifo_pop() - Pop message from FIFO and update IRQs
>> + * @s: FlexCAN device pointer
>> + *
>> + * Does not require the queue to be non-empty.
>> + */
>> +static void flexcan_fifo_pop(FlexcanState *s)
>> +{
>> +    if (s->regs.fifo.mb_back.can_ctrl != 0) {
>> +        /* move queue elements forward */
>> +        memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0],
>> +                sizeof(s->regs.fifo.mbs_queue));
>> +
>> +        /* clear the first-in slot */
>> +        memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0,
>> +               sizeof(FlexcanRegsMessageBuffer));
>> +
>> +        trace_flexcan_fifo_pop(s, 1, s->regs.fifo.mb_back.can_ctrl != 0);
>> +    } else {
>> +        trace_flexcan_fifo_pop(s, 0, 0);
>> +    }
>> +
>> +    if (s->regs.fifo.mb_back.can_ctrl != 0) {
>> +        flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
>> +    } else {
>> +        flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE);
>> +    }
>> +}
>> +
>> +/**
>> + * flexcan_fifo_find_free_slot() - Find the first free slot in the FIFO
>> + * @s: FlexCAN device pointer
>> + *
>> + * Return: Pointer to the first free slot in the FIFO,
>> + *         or NULL if the queue is full.
>> + */
>> +static FlexcanRegsMessageBuffer *flexcan_fifo_find_free_slot(FlexcanState 
>> *s)
>> +{
>> +    for (int i = 0; i < FLEXCAN_FIFO_DEPTH; i++) {
>> +        FlexcanRegsMessageBuffer *mb = &s->regs.mbs[i];
>> +        if (mb->can_ctrl == 0) {
>> +            return mb;
>> +        }
>> +    }
>> +    return NULL;
>> +}
>> +
>> +/**
>> + * flexcan_fifo_push() - Update FIFO IRQs after frame move-in
>> + * @s: FlexCAN device pointer
>> + * @slot: Target FIFO slot
>> + *
>> + * The usage is as follows:
>> + * 1. Get free slot pointer using flexcan_fifo_find_free_slot()
>> + * 2. Move the frame in if not NULL
>> + * 3. Call flexcan_fifo_push() regardless of the NULL pointer
>> + */
>> +static void flexcan_fifo_push(FlexcanState *s, FlexcanRegsMessageBuffer 
>> *slot)
>> +{
>> +    if (slot) {
>> +        int n_occupied = slot - s->regs.mbs;
>> +        if (n_occupied == 4) { /* 4 means the 5th slot was filled in */
>> +            /*
>> +             * fifo occupancy increased from 4 to 5,
>> +             * raising FIFO_WARN interrupt
>> +             */
>> +            flexcan_irq_iflag_set(s, I_FIFO_WARN);
>> +        }
>> +        flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);
>> +
>> +        trace_flexcan_fifo_push(s, n_occupied);
>> +    } else {
>> +        flexcan_irq_iflag_set(s, I_FIFO_OVERFLOW);
>> +
>> +        trace_flexcan_fifo_push(s, -1);
>> +    }
>> +}
>> +
>> +static int flexcan_fifo_rx(FlexcanState *s, const qemu_can_frame *buf)
>> +{
>> +    /* todo: filtering. return FLEXCAN_FIFO_RX_RETRY if filtered out */
>> +    if ((s->regs.mcr & FLEXCAN_MCR_IDAM_MASK) == FLEXCAN_MCR_IDAM_D) {
>> +        /* all frames rejected */
>> +        return FLEXCAN_RX_SEARCH_RETRY;
>> +    }
>> +
>> +    /* push message to queue if not full */
>> +    FlexcanRegsMessageBuffer *slot = flexcan_fifo_find_free_slot(s);
>> +    if (slot) {
>> +        flexcan_mb_move_in(s, buf, slot);
>> +    }
>> +    flexcan_fifo_push(s, slot);
>> +
>> +    return slot ? FLEXCAN_RX_SEARCH_ACCEPT : FLEXCAN_RX_SEARCH_DROPPED;
>> +}
>> +
>> +/* --------- RX message buffer ---------- */
>> +
>> +/**
>> + * flexcan_mb_rx_check_mb() - Check if a message buffer matches a received 
>> frame
>> + * @s: FlexCAN device pointer
>> + * @buf: Frame to be received from CAN subsystem
>> + * @mbid: Target mailbox index. The mailbox must be a valid message buffer.
>> + *
>> + * Return: FLEXCAN_CHECK_MB_NIL if the message buffer does not match.
>> + *         FLEXCAN_CHECK_MB_MATCH if the message buffer matches the received
>> + *                                frame and is free-to-receive,
>> + *         FLEXCAN_CHECK_MB_MATCH_LOCKED if the message buffer matches,
>> + *                                       but is locked,
>> + *         FLEXCAN_CHECK_MB_MATCH_NON_FREE if the message buffer matches,
>> + *                                         but is not free-to-receive
>> + *                                         for some other reason.
>> + */
>> +static int flexcan_mb_rx_check_mb(FlexcanState *s, const qemu_can_frame 
>> *buf,
>> +                                  int mbid)
>> +{
>> +    FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];
>> +    const bool is_rtr = !!(buf->can_id & QEMU_CAN_RTR_FLAG);
>> +    const bool is_serviced = !(mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV);
>> +    const bool is_locked = s->locked_mbidx == mbid;
>> +
>> +    bool is_free_to_receive = false;
>> +    bool is_matched = false;
>> +
>> +    switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {
>> +    case FLEXCAN_MB_CODE_RX_RANSWER:
>> +        if (is_rtr && !(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS)) {
>> +            /* todo: do the actual matching/filtering and RTR answer */
>> +            is_matched = true;
>> +        }
>> +        break;
>> +    case FLEXCAN_MB_CODE_RX_FULL:
>> +        QEMU_FALLTHROUGH;
>> +    case FLEXCAN_MB_CODE_RX_OVERRUN:
>> +        is_free_to_receive = is_serviced;
>> +        /* todo: do the actual matching/filtering */
>> +        is_matched = true;
>> +        break;
>> +    case FLEXCAN_MB_CODE_RX_EMPTY:
>> +        is_free_to_receive = true;
>> +        /* todo: do the actual matching/filtering */
>> +        is_matched = true;
>> +        break;
>> +    default:
>> +    break;
>> +    }
>> +
>> +    if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_RX_CHECK_MB)) {
>> +        char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };
>> +        const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, 
>> code_str_buf);
>> +        trace_flexcan_mb_rx_check_mb(s, mbid, code_str, is_matched,
>> +                                     is_free_to_receive, is_serviced,
>> +                                     is_locked);
>> +    }
>> +
>> +    if (is_matched && is_free_to_receive && !is_locked) {
>> +        return FLEXCAN_CHECK_MB_MATCH;
>> +    } else if (is_matched && !is_locked) {
>> +        return FLEXCAN_CHECK_MB_MATCH_NON_FREE;
>> +    } else if (is_matched) {
>> +        return FLEXCAN_CHECK_MB_MATCH_LOCKED;
>> +    } else {
>> +        return FLEXCAN_CHECK_MB_NIL;
>> +    }
>> +}
>> +static int flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf)
>> +{
>> +    int last_not_free_to_receive_mbid = -1;
>> +    bool last_not_free_to_receive_locked = false;
>> +
>> +    FlexcanRegsMessageBuffer *first_mb = 
>> flexcan_get_first_message_buffer(s);
>> +    FlexcanRegsMessageBuffer *last_mb = flexcan_get_last_enabled_mailbox(s);
>> +    for (FlexcanRegsMessageBuffer *mb = first_mb;
>> +         mb <= last_mb; mb++) {
>> +        int mbid = mb - s->regs.mbs;
>> +        int r = flexcan_mb_rx_check_mb(s, buf, mbid);
>> +        if (r == FLEXCAN_CHECK_MB_MATCH) {
>> +            flexcan_mb_move_in(s, buf, mb);
>> +            flexcan_irq_iflag_set(s, mbid);
>> +            return FLEXCAN_RX_SEARCH_ACCEPT;
>> +        } else if (r == FLEXCAN_CHECK_MB_MATCH_NON_FREE) {
>> +            last_not_free_to_receive_mbid = mbid;
>> +            last_not_free_to_receive_locked = false;
>> +        } else if (r == FLEXCAN_CHECK_MB_MATCH_LOCKED) {
>> +            /*
>> +             * message buffer is locked,
>> +             * we can move in the message after it's unlocked
>> +             */
>> +            last_not_free_to_receive_mbid = mbid;
>> +            last_not_free_to_receive_locked = true;
>> +        }
>> +    }
>> +
>> +    if (last_not_free_to_receive_mbid >= -1) {
>> +        if (last_not_free_to_receive_locked) {
>> +            /*
>> +             * copy to temporary mailbox (SMB)
>> +             * it will be moved in when the mailbox is unlocked
>> +             */
>> +            s->regs.rx_smb0.can_ctrl =
>> +                s->regs.mbs[last_not_free_to_receive_mbid].can_id;
>> +            flexcan_mb_move_in(s, buf, &s->regs.rx_smb0);
>> +            s->smb_target_mbidx = last_not_free_to_receive_mbid;
>> +            return FLEXCAN_RX_SEARCH_ACCEPT;
>> +        } else if (s->regs.mcr & FLEXCAN_MCR_IRMQ) {
>> +            flexcan_mb_move_in(s, buf,
>> +                               &s->regs.mbs[last_not_free_to_receive_mbid]);
>> +            flexcan_irq_iflag_set(s, last_not_free_to_receive_mbid);
>> +            return FLEXCAN_RX_SEARCH_ACCEPT;
>> +        }
>> +    }
>> +
>> +    return FLEXCAN_RX_SEARCH_RETRY;
>> +}
>> +
>> +ssize_t flexcan_receive(CanBusClientState *client, const qemu_can_frame 
>> *frames,
>> +                        size_t frames_cnt)
>> +{
>> +    FlexcanState *s = container_of(client, FlexcanState, bus_client);
>> +    trace_flexcan_receive(s, frames_cnt);
>> +
>> +    if (frames_cnt <= 0) {
>> +        g_autofree char *path = object_get_canonical_path(OBJECT(s));
>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n",
>> +                      path);
>> +        return 0;
>> +    }
>> +
>> +    /* clear the SMB, as it would be overriden in hardware */
>> +    memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));
>> +    s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;
>> +
>> +    for (size_t i = 0; i < frames_cnt; i++) {
>> +        int r;
>> +        const qemu_can_frame *frame = &frames[i];
>> +        if (frame->can_id & QEMU_CAN_ERR_FLAG) {
>> +            /* todo: error frame handling */
>> +            continue;
>> +        } else if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {
>> +            /* CAN FD supported only in later FlexCAN version */
>> +            continue;
>> +        }
>> +
>> +        /* todo: this order logic is not complete and needs further work */
>> +        if (s->regs.mcr & FLEXCAN_MCR_FEN &&
>> +            s->regs.ctrl2 & FLEXCAN_CTRL2_MRP) {
>> +            r = flexcan_mb_rx(s, frame);
>> +            if (r != FLEXCAN_RX_SEARCH_RETRY) {
>> +                continue;
>> +            }
>> +            flexcan_fifo_rx(s, frame);
>
>Why not invert the condition and move the call inside the if statement, 
>eliminating the "continue;"?
>
>> +        } else if (s->regs.mcr & FLEXCAN_MCR_FEN) {
>> +            r = flexcan_fifo_rx(s, frame);
>> +            if (r != FLEXCAN_RX_SEARCH_RETRY) {
>> +                continue;
>> +            }
>> +            flexcan_mb_rx(s, frame);
>
>Same here.
>
>> +        } else {
>> +            flexcan_mb_rx(s, frame);
>> +        }
>> +    }
>> +
>> +    flexcan_irq_update(s);
>> +    return 1;
>> +}
>> +
>> +/* ========== I/O handling ========== */
>> +static void flexcan_reg_write(FlexcanState *s, hwaddr addr, uint32_t val)
>> +{
>> +    uint32_t write_mask = ((const uint32_t *)
>> +        &flexcan_regs_write_mask)[addr / 4];
>> +    uint32_t old_value = s->regs_raw[addr / 4];
>> +
>> +    /*
>> +     * 0 for bits that can "only be written in Freeze mode as it is blocked
>> +     * by hardware in other modes"
>> +     */
>> +    const uint32_t freeze_mask_mcr = 0xDF54CC80;
>> +    const uint32_t freeze_mask_ctrl1 = 0x0000E740;
>> +
>> +    switch (addr) {
>> +    case offsetof(FlexcanRegs, mcr):
>> +        if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
>> +            write_mask &= freeze_mask_mcr;
>> +        }
>> +        s->regs.mcr = (val & write_mask) | (old_value & ~write_mask);
>> +        flexcan_set_mcr(s, old_value);
>> +        break;
>> +    case offsetof(FlexcanRegs, ctrl):
>> +        if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
>> +            write_mask &= freeze_mask_ctrl1;
>> +        }
>> +        s->regs.ctrl = (val & write_mask) | (old_value & ~write_mask);
>> +        break;
>> +    case offsetof(FlexcanRegs, iflag1):
>> +        s->regs.iflag1 &= ~val;
>> +        if ((s->regs.mcr & FLEXCAN_MCR_FEN) &&
>> +            (val & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) {
>> +            flexcan_fifo_pop(s);
>> +        }
>> +        break;
>> +    case offsetof(FlexcanRegs, iflag2):
>> +        s->regs.iflag2 &= ~val;
>> +        break;
>> +    case offsetof(FlexcanRegs, ctrl2):
>> +        QEMU_FALLTHROUGH;
>> +    case offsetof(FlexcanRegs, ecr):
>> +        QEMU_FALLTHROUGH;
>> +    case offsetof(FlexcanRegs, rxmgmask):
>> +        QEMU_FALLTHROUGH;
>> +    case offsetof(FlexcanRegs, rx14mask):
>> +        QEMU_FALLTHROUGH;
>> +    case offsetof(FlexcanRegs, rx15mask):
>> +        QEMU_FALLTHROUGH;
>> +    case offsetof(FlexcanRegs, rxfgmask):
>> +        /* these registers can only be written in freeze mode */
>> +        if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
>> +            break;
>> +        }
>> +        QEMU_FALLTHROUGH;
>> +    default:
>> +        /* RXIMRn can only be written in freeze mode */
>> +        if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK) &&
>> +            addr >= offsetof(FlexcanRegs, rximr) &&
>> +            addr < offsetof(FlexcanRegs, _reserved5)) {
>> +            break;
>> +        }
>
>I think it should be possible to eliminate this if statement by adding another 
>case before the default:
>    "case offsetof(FlexcanRegs, rximr[0]) ... offsetof(FlexcanRegs, 
> rximr[63]):"
>
>> +
>> +        s->regs_raw[addr / 4] = (val & write_mask) | (old_value & 
>> ~write_mask);
>> +
>> +        if (addr >= offsetof(FlexcanRegs, mb) &&
>> +            addr < offsetof(FlexcanRegs, _reserved4)) {
>> +            /* access to mailbox */
>> +            int mbid = (addr - offsetof(FlexcanRegs, mb)) /
>> +                            sizeof(FlexcanRegsMessageBuffer);
>> +
>> +            if (s->locked_mbidx == mbid) {
>> +                flexcan_mb_unlock(s);
>> +            }
>> +
>> +            /* check for invalid writes into FIFO region */
>> +            if (s->regs.mcr & FLEXCAN_MCR_FEN && mbid < FLEXCAN_FIFO_DEPTH) 
>> {
>> +                g_autofree char *path = 
>> object_get_canonical_path(OBJECT(s));
>> +                qemu_log_mask(LOG_GUEST_ERROR,
>> +                              "%s: Invalid write to Rx-FIFO structure", 
>> path);
>> +                return;
>> +            }
>> +
>> +            /* run mailbox processing function on write to control word */
>> +            if ((addr & 0xF) == 0) {
>> +                flexcan_mb_write(s, mbid);
>> +            }
>> +        }
>> +        break;
>> +    }
>> +
>> +    flexcan_irq_update(s);
>> +}
>> +
>> +void flexcan_mem_write(void *obj, hwaddr addr, uint64_t val, unsigned size)
>
>The convention in QEMU is usually to name these void pointers "opaque". The 
>name "obj" suggests a QOM Object * and is therefore midleading. Please rename 
>to "opaque".
>
>Same for the other flexcan_ops callbacks.
>
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>
>C actually handles void pointers special, so you don't need to cast here.
>
>Same for the other flexcan_ops callbacks.
>
>> +    flexcan_trace_mem_op(s, addr, val, size, true);
>> +
>> +    if (addr < FLEXCAN_ADDR_SPC_END) {
>> +        flexcan_reg_write(s, addr, (uint32_t)val);
>> +    } else {
>> +        DPRINTF("warn: write outside of defined address space\n");
>> +    }
>> +}
>> +uint64_t flexcan_mem_read(void *obj, hwaddr addr, unsigned size)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>> +
>> +    if (addr < FLEXCAN_ADDR_SPC_END) {
>> +        uint32_t rv = s->regs_raw[addr >> 2];
>> +
>> +        if (addr >= offsetof(FlexcanRegs, mb) &&
>> +            addr < offsetof(FlexcanRegs, _reserved4)) {
>> +            /* reading from mailbox */
>> +            hwaddr offset = addr - offsetof(FlexcanRegs, mb);
>> +            int mbid = offset / sizeof(FlexcanRegsMessageBuffer);
>> +
>> +            if (addr % 16 == 0 && s->locked_mbidx != mbid) {
>> +                /* reading control word locks the mailbox */
>> +                flexcan_mb_unlock(s);
>> +                flexcan_mb_lock(s, mbid);
>> +                flexcan_irq_update(s);
>> +                rv = s->regs.mbs[mbid].can_ctrl & ~FLEXCAN_MB_CNT_NOT_SRV;
>> +            }
>> +        } else if (addr == offsetof(FlexcanRegs, timer)) {
>> +            flexcan_mb_unlock(s);
>> +            flexcan_irq_update(s);
>> +            rv = flexcan_get_timestamp(s, false);
>> +        }
>> +
>> +        flexcan_trace_mem_op(s, addr, rv, size, false);
>> +        return rv;
>> +    } else {
>> +        g_autofree char *path = object_get_canonical_path(OBJECT(s));
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "%s: Invalid write outside valid I/O space", path);
>> +
>> +        flexcan_trace_mem_op(s, addr, 0, size, false);
>> +        return 0;
>> +    }
>> +}
>> +bool flexcan_mem_accepts(void *obj, hwaddr addr,
>> +                                unsigned size, bool is_write,
>> +                                MemTxAttrs attrs)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>> +
>> +    if ((s->regs.ctrl2 & FLEXCAN_CTRL2_WRMFRZ) &&
>> +        (s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {
>> +        /* unrestricted access to FlexCAN memory in freeze mode */
>> +        return true;
>> +    } else if (attrs.user && (s->regs.mcr & FLEXCAN_MCR_SUPV)) {
>> +        goto denied;
>> +    } else if (is_write && attrs.user && addr < 4) {
>> +        /* illegal user write to MCR */
>> +        goto denied;
>> +    } else if (addr >= FLEXCAN_ADDR_SPC_END) {
>> +        /* illegal write to non-existent register */
>> +        goto denied;
>> +    }
>
>The gotos can be avoided by turning the comments into guest error logs and 
>returning early. Bonus points for avoiding else after return for readability 
>reasons: 
>https://clang.llvm.org/extra/clang-tidy/checks/readability/else-after-return.html
>
>> +
>> +    return true;
>> +denied:
>> +    trace_flexcan_mem_accepts(s, addr, size, is_write, !attrs.user);
>> +    return false;
>> +}
>> +
>> +static CanBusClientInfo flexcan_bus_client_info = {
>> +    .can_receive = flexcan_can_receive,
>> +    .receive = flexcan_receive,
>> +};
>> +
>> +static int flexcan_connect_to_bus(FlexcanState *s, CanBusState *bus)
>> +{
>> +    s->bus_client.info = &flexcan_bus_client_info;
>> +
>> +    if (can_bus_insert_client(bus, &s->bus_client) < 0) {
>> +        return -1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +void flexcan_init(Object *obj)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(obj);
>> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>> +
>> +    USE(s);
>> +    USE(sbd);
>
>No unused variables, please. And no "USE" macros which basically circumvent 
>compiler warnings that are there for a purpose.
>
>To avoid an empty method, you could actually perform the initialization of 
>s->iomem here instead of in the realize method. This would also prepare for 
>adding flexcan 3 support (see below).
>
>> +}
>> +
>> +static void flexcan_realize(DeviceState *dev, Error **errp)
>> +{
>> +    FlexcanState *s = CAN_FLEXCAN(dev);
>> +
>> +    if (s->canbus) {
>> +        if (flexcan_connect_to_bus(s, s->canbus) < 0) {
>> +            g_autofree char *path = object_get_canonical_path(OBJECT(s));
>> +
>> +            error_setg(errp, "%s: flexcan_connect_to_bus"
>> +                        " failed.", path);
>> +            return;
>> +        }
>> +    }
>> +
>> +    flexcan_reset_local_state(s);
>
>QEMU's reset logic will invoke this method automatically before starting the 
>machine, rendering this call redundant. Please remove.
>
>> +
>> +    memory_region_init_io(
>> +        &s->iomem, OBJECT(dev), &flexcan_ops, s, TYPE_CAN_FLEXCAN, 0x4000
>
>Instead of passing 0x4000 (which likely contains SoC-specific padding) as the 
>size, you could pass FLEXCAN_ADDR_SPC_END instead. This avoids quite a few 
>error cases in the code since you can eliminate all bounds checks against 
>FLEXCAN_ADDR_SPC_END. In fact, you could eliminate FLEXCAN_ADDR_SPC_END 
>entirely and pass "offsetof(FlexcanRegs, _reserved6)" as the size directly. If 
>you then initialize s->iomem in the init method, like suggested above, we 
>could later add a flexcan3 init method with a different size, keeping code 
>churn at a minimum. See my imx8mp-flexcan branch as a working example.
>
>> +    );
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
>> +    sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);
>> +}
>> +
>> +static const VMStateDescription vmstate_can = {
>> +    .name = TYPE_CAN_FLEXCAN,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (const VMStateField[]) {
>> +        VMSTATE_INT64(timer_start, FlexcanState),
>> +        VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 
>> 4),
>> +        VMSTATE_INT32(locked_mbidx, FlexcanState),
>> +        VMSTATE_INT32(smb_target_mbidx, FlexcanState),
>> +        VMSTATE_END_OF_LIST(),
>> +    },
>> +};
>> +
>> +static const Property flexcan_properties[] = {
>> +    DEFINE_PROP_LINK("canbus", FlexcanState, canbus, TYPE_CAN_BUS,
>> +                     CanBusState *),
>> +};
>> +
>> +static void flexcan_class_init(ObjectClass *klass, const void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
>> +
>> +    rc->phases.enter = flexcan_reset_enter;
>> +    rc->phases.hold = flexcan_reset_hold;
>> +    dc->realize = &flexcan_realize;
>> +    device_class_set_props(dc, flexcan_properties);
>> +    dc->vmsd = &vmstate_can;
>> +    dc->desc = "i.MX FLEXCAN Controller";
>> +}
>> +
>> +static const TypeInfo flexcan_info = {
>> +    .name          = TYPE_CAN_FLEXCAN,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(FlexcanState),
>> +    .class_init    = flexcan_class_init,
>> +    .instance_init = flexcan_init,
>> +};
>> +
>> +static void can_register_types(void)
>> +{
>> +    type_register_static(&flexcan_info);
>> +}
>> +type_init(can_register_types)
>> diff --git a/hw/net/can/flexcan_regs.h b/hw/net/can/flexcan_regs.h
>> new file mode 100644
>> index 0000000000..8e976b2c9e
>> --- /dev/null
>> +++ b/hw/net/can/flexcan_regs.h
>> @@ -0,0 +1,188 @@
>> +/*
>> + * Field bitmasks and register structs definitions for FlexCAN
>> + *
>> + * This implementation is based on the following datasheet:
>> + * i.MX 6Dual/6Quad Applications Processor Reference Manual
>> + * Document Number: IMX6DQRM, Rev. 6, 05/2020
>> + *
>> + * Copyright (c) 2025 Matyas Bobek <[email protected]>
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +#include "qemu/bitops.h"
>> +
>> +#ifndef HW_CAN_FLEXCAN_REGS_H
>> +#define HW_CAN_FLEXCAN_REGS_H
>> +
>> +#define FLEXCAN_GENMASK(h, l) (((~(uint32_t)0) >> (31 - (h) + (l))) << (l))
>> +
>> +/* FLEXCAN module configuration register (CANMCR) bits */
>> +#define FLEXCAN_MCR_MDIS                BIT(31)
>> +#define FLEXCAN_MCR_FRZ                 BIT(30)
>> +#define FLEXCAN_MCR_FEN                 BIT(29)
>> +#define FLEXCAN_MCR_HALT                BIT(28)
>> +#define FLEXCAN_MCR_NOT_RDY             BIT(27)
>> +#define FLEXCAN_MCR_WAK_MSK             BIT(26)
>> +#define FLEXCAN_MCR_SOFTRST             BIT(25)
>> +#define FLEXCAN_MCR_FRZ_ACK             BIT(24)
>> +#define FLEXCAN_MCR_SUPV                BIT(23)
>> +#define FLEXCAN_MCR_SLF_WAK             BIT(22)
>> +#define FLEXCAN_MCR_WRN_EN              BIT(21)
>> +#define FLEXCAN_MCR_LPM_ACK             BIT(20)
>> +#define FLEXCAN_MCR_WAK_SRC             BIT(19)
>> +#define FLEXCAN_MCR_DOZE                BIT(18)
>> +#define FLEXCAN_MCR_SRX_DIS             BIT(17)
>> +#define FLEXCAN_MCR_IRMQ                BIT(16)
>> +#define FLEXCAN_MCR_LPRIO_EN            BIT(13)
>> +#define FLEXCAN_MCR_AEN                 BIT(12)
>> +#define FLEXCAN_MCR_FDEN                BIT(11)
>> +#define FLEXCAN_MCR_MAXMB(x)            ((x) & 0x7f)
>> +#define FLEXCAN_MCR_IDAM_A              (0x0 << 8)
>> +#define FLEXCAN_MCR_IDAM_B              (0x1 << 8)
>> +#define FLEXCAN_MCR_IDAM_C              (0x2 << 8)
>> +#define FLEXCAN_MCR_IDAM_D              (0x3 << 8)
>> +#define FLEXCAN_MCR_IDAM_MASK           (0x3 << 8)
>> +
>> +/* FLEXCAN control register (CANCTRL) bits */
>> +#define FLEXCAN_CTRL_PRESDIV(x)         (((x) & 0xFF) << 24)
>> +#define FLEXCAN_CTRL_PRESDIV_MASK       FLEXCAN_CTRL_PRESDIV(UINT32_MAX)
>> +#define FLEXCAN_CTRL_RJW(x)             (((x) & 0x03) << 22)
>> +#define FLEXCAN_CTRL_RJW_MASK           FLEXCAN_CTRL_RJW(UINT32_MAX)
>> +#define FLEXCAN_CTRL_PSEG1(x)           (((x) & 0x07) << 19)
>> +#define FLEXCAN_CTRL_PSEG1_MASK         FLEXCAN_CTRL_PSEG1(UINT32_MAX)
>> +#define FLEXCAN_CTRL_PSEG2(x)           (((x) & 0x07) << 16)
>> +#define FLEXCAN_CTRL_PSEG2_MASK         FLEXCAN_CTRL_PSEG2(UINT32_MAX)
>> +#define FLEXCAN_CTRL_BOFF_MSK           BIT(15)
>> +#define FLEXCAN_CTRL_ERR_MSK            BIT(14)
>> +#define FLEXCAN_CTRL_CLK_SRC            BIT(13)
>> +#define FLEXCAN_CTRL_LPB                BIT(12)
>> +#define FLEXCAN_CTRL_TWRN_MSK           BIT(11)
>> +#define FLEXCAN_CTRL_RWRN_MSK           BIT(10)
>> +#define FLEXCAN_CTRL_SMP                BIT(7)
>> +#define FLEXCAN_CTRL_BOFF_REC           BIT(6)
>> +#define FLEXCAN_CTRL_TSYN               BIT(5)
>> +#define FLEXCAN_CTRL_LBUF               BIT(4)
>> +#define FLEXCAN_CTRL_LOM                BIT(3)
>> +#define FLEXCAN_CTRL_PROPSEG(x)         ((x) & 0x07)
>> +#define FLEXCAN_CTRL_PROPSEG_MASK       FLEXCAN_CTRL_PROPSEG(UINT32_MAX)
>> +#define FLEXCAN_CTRL_ERR_BUS            (FLEXCAN_CTRL_ERR_MSK)
>> +#define FLEXCAN_CTRL_ERR_STATE \
>> +        (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \
>> +         FLEXCAN_CTRL_BOFF_MSK)
>> +#define FLEXCAN_CTRL_ERR_ALL \
>> +        (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
>> +
>> +/* FLEXCAN control register 2 (CTRL2) bits */
>> +#define FLEXCAN_CTRL2_ECRWRE            BIT(29)
>> +#define FLEXCAN_CTRL2_WRMFRZ            BIT(28)
>> +#define FLEXCAN_CTRL2_RFFN(x)           (((x) & 0x0f) << 24)
>> +#define FLEXCAN_CTRL2_TASD(x)           (((x) & 0x1f) << 19)
>> +#define FLEXCAN_CTRL2_MRP               BIT(18)
>> +#define FLEXCAN_CTRL2_RRS               BIT(17)
>> +#define FLEXCAN_CTRL2_EACEN             BIT(16)
>> +#define FLEXCAN_CTRL2_ISOCANFDEN        BIT(12)
>> +
>> +/* FLEXCAN memory error control register (MECR) bits */
>> +#define FLEXCAN_MECR_ECRWRDIS           BIT(31)
>> +#define FLEXCAN_MECR_HANCEI_MSK         BIT(19)
>> +#define FLEXCAN_MECR_FANCEI_MSK         BIT(18)
>> +#define FLEXCAN_MECR_CEI_MSK            BIT(16)
>> +#define FLEXCAN_MECR_HAERRIE            BIT(15)
>> +#define FLEXCAN_MECR_FAERRIE            BIT(14)
>> +#define FLEXCAN_MECR_EXTERRIE           BIT(13)
>> +#define FLEXCAN_MECR_RERRDIS            BIT(9)
>> +#define FLEXCAN_MECR_ECCDIS             BIT(8)
>> +#define FLEXCAN_MECR_NCEFAFRZ           BIT(7)
>> +
>> +/* FLEXCAN error and status register (ESR) bits */
>> +#define FLEXCAN_ESR_SYNCH               BIT(18)
>> +#define FLEXCAN_ESR_TWRN_INT            BIT(17)
>> +#define FLEXCAN_ESR_RWRN_INT            BIT(16)
>> +#define FLEXCAN_ESR_BIT1_ERR            BIT(15)
>> +#define FLEXCAN_ESR_BIT0_ERR            BIT(14)
>> +#define FLEXCAN_ESR_ACK_ERR             BIT(13)
>> +#define FLEXCAN_ESR_CRC_ERR             BIT(12)
>> +#define FLEXCAN_ESR_FRM_ERR             BIT(11)
>> +#define FLEXCAN_ESR_STF_ERR             BIT(10)
>> +#define FLEXCAN_ESR_TX_WRN              BIT(9)
>> +#define FLEXCAN_ESR_RX_WRN              BIT(8)
>> +#define FLEXCAN_ESR_IDLE                BIT(7)
>> +#define FLEXCAN_ESR_BOFF_INT            BIT(2)
>> +#define FLEXCAN_ESR_ERR_INT             BIT(1)
>> +#define FLEXCAN_ESR_WAK_INT             BIT(0)
>> +
>> +/* FLEXCAN Bit Timing register (CBT) bits */
>> +#define FLEXCAN_CBT_BTF                 BIT(31)
>> +#define FLEXCAN_CBT_EPRESDIV_MASK       FLEXCAN_GENMASK(30, 21)
>> +#define FLEXCAN_CBT_ERJW_MASK           FLEXCAN_GENMASK(20, 16)
>> +#define FLEXCAN_CBT_EPROPSEG_MASK       FLEXCAN_GENMASK(15, 10)
>> +#define FLEXCAN_CBT_EPSEG1_MASK         FLEXCAN_GENMASK(9, 5)
>> +#define FLEXCAN_CBT_EPSEG2_MASK         FLEXCAN_GENMASK(4, 0)
>> +
>> +/* FLEXCAN FD control register (FDCTRL) bits */
>> +#define FLEXCAN_FDCTRL_FDRATE           BIT(31)
>> +#define FLEXCAN_FDCTRL_MBDSR1           FLEXCAN_GENMASK(20, 19)
>> +#define FLEXCAN_FDCTRL_MBDSR0           FLEXCAN_GENMASK(17, 16)
>> +#define FLEXCAN_FDCTRL_MBDSR_8          0x0
>> +#define FLEXCAN_FDCTRL_MBDSR_12         0x1
>> +#define FLEXCAN_FDCTRL_MBDSR_32         0x2
>> +#define FLEXCAN_FDCTRL_MBDSR_64         0x3
>> +#define FLEXCAN_FDCTRL_TDCEN            BIT(15)
>> +#define FLEXCAN_FDCTRL_TDCFAIL          BIT(14)
>> +#define FLEXCAN_FDCTRL_TDCOFF           FLEXCAN_GENMASK(12, 8)
>> +#define FLEXCAN_FDCTRL_TDCVAL           FLEXCAN_GENMASK(5, 0)
>> +
>> +/* FLEXCAN FD Bit Timing register (FDCBT) bits */
>> +#define FLEXCAN_FDCBT_FPRESDIV_MASK     FLEXCAN_GENMASK(29, 20)
>> +#define FLEXCAN_FDCBT_FRJW_MASK         FLEXCAN_GENMASK(18, 16)
>> +#define FLEXCAN_FDCBT_FPROPSEG_MASK     FLEXCAN_GENMASK(14, 10)
>> +#define FLEXCAN_FDCBT_FPSEG1_MASK       FLEXCAN_GENMASK(7, 5)
>> +#define FLEXCAN_FDCBT_FPSEG2_MASK       FLEXCAN_GENMASK(2, 0)
>> +
>> +/* FLEXCAN CRC Register (CRCR) bits */
>> +#define FLEXCAN_CRCR_MBCRC_MASK         FLEXCAN_GENMASK(22, 16)
>> +#define FLEXCAN_CRCR_MBCRC(x)           (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 
>> 16)
>> +#define FLEXCAN_CRCR_TXCRC_MASK         FLEXCAN_GENMASK(14, 0)
>> +#define FLEXCAN_CRCR_TXCRC(x)           ((x) & FLEXCAN_CRCR_TXCRC_MASK)
>> +
>> +/* FLEXCAN interrupt flag register (IFLAG) bits */
>> +/* Errata ERR005829 step7: Reserve first valid MB */
>> +#define I_FIFO_OVERFLOW  7
>> +#define I_FIFO_WARN      6
>> +#define I_FIFO_AVAILABLE 5
>> +
>> +#define FLEXCAN_TX_MB_RESERVED_RX_FIFO  8
>> +#define FLEXCAN_TX_MB_RESERVED_RX_MAILBOX       0
>> +#define FLEXCAN_RX_MB_RX_MAILBOX_FIRST  (FLEXCAN_TX_MB_RESERVED_RX_MAILBOX 
>> + 1)
>> +#define FLEXCAN_IFLAG_MB(x)             BIT_ULL(x)
>> +#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW  BIT(I_FIFO_OVERFLOW)
>> +#define FLEXCAN_IFLAG_RX_FIFO_WARN      BIT(I_FIFO_WARN)
>> +#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(I_FIFO_AVAILABLE)
>> +
>> +/* FLEXCAN message buffers */
>> +#define FLEXCAN_MB_CODE_RX_BUSY_BIT     (0x1 << 24)
>> +#define FLEXCAN_MB_CODE_RX_INACTIVE     (0x0 << 24)
>> +#define FLEXCAN_MB_CODE_RX_EMPTY        (0x4 << 24)
>> +#define FLEXCAN_MB_CODE_RX_FULL         (0x2 << 24)
>> +#define FLEXCAN_MB_CODE_RX_OVERRUN      (0x6 << 24)
>> +#define FLEXCAN_MB_CODE_RX_RANSWER      (0xa << 24)
>> +
>> +#define FLEXCAN_MB_CODE_TX_INACTIVE     (0x8 << 24)
>> +#define FLEXCAN_MB_CODE_TX_ABORT        (0x9 << 24)
>> +#define FLEXCAN_MB_CODE_TX_DATA         (0xc << 24)
>> +#define FLEXCAN_MB_CODE_TX_TANSWER      (0xe << 24)
>> +
>> +#define FLEXCAN_MB_CODE(x)              (((x) & 0xF) << 24)
>> +#define FLEXCAN_MB_CODE_MASK            FLEXCAN_MB_CODE(UINT32_MAX)
>> +
>> +#define FLEXCAN_MB_CNT_EDL              BIT(31)
>> +#define FLEXCAN_MB_CNT_BRS              BIT(30)
>> +#define FLEXCAN_MB_CNT_ESI              BIT(29)
>> +#define FLEXCAN_MB_CNT_SRR              BIT(22)
>> +#define FLEXCAN_MB_CNT_IDE              BIT(21)
>> +#define FLEXCAN_MB_CNT_RTR              BIT(20)
>> +#define FLEXCAN_MB_CNT_LENGTH(x)        (((x) & 0xF) << 16)
>> +#define FLEXCAN_MB_CNT_TIMESTAMP(x)     ((x) & 0xFFFF)
>> +#define FLEXCAN_MB_CNT_TIMESTAMP_MASK   FLEXCAN_MB_CNT_TIMESTAMP(UINT32_MAX)
>> +
>> +#endif
>> diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build
>> index 7382344628..401afde2e4 100644
>> --- a/hw/net/can/meson.build
>> +++ b/hw/net/can/meson.build
>> @@ -6,3 +6,4 @@ system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: 
>> files('ctucan_core.c'))
>> system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: 
>> files('ctucan_pci.c'))
>> system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: 
>> files('xlnx-zynqmp-can.c'))
>> system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: 
>> files('xlnx-versal-canfd.c'))
>> +system_ss.add(when: 'CONFIG_CAN_FLEXCAN', if_true: files('flexcan.c'))
>> diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events
>> index de64ac1b31..83f574e81c 100644
>> --- a/hw/net/can/trace-events
>> +++ b/hw/net/can/trace-events
>> @@ -1,3 +1,21 @@
>> +# flexcan.c
>> +flexcan_irq_update(void *inst, uint32_t mb_irqs1, uint32_t mb_irqs2, int 
>> setting) "%p: irqs1 0x%08x irqs2 0x%08x request %i"
>> +flexcan_set_mcr(void* inst, const char *enabled, const char *freeze, const 
>> char *fifo, const char *rdy, const char *sync) "%p: %s %s %s %s %s"
>> +flexcan_mb_write(void *inst, int mbidx, const char *code, int is_mailbox, 
>> uint32_t ctrl, uint32_t id) "%p: mbidx %i code %s is_mailbox %i ctrl 0x%08x 
>> id 0x%08x"
>> +flexcan_mb_lock(void *inst, int mbidx, int had_rx_code) "%p: mbidx %i 
>> had_rx_code %i"
>> +flexcan_mb_unlock(void *inst, int mbidx, const char *pending_frame) "%p: 
>> mbidx %i%s"
>> +flexcan_fifo_pop(void *inst, int non_empty_before, int non_empty_after) 
>> "%p: non_empty before %i non_empty_after %i"
>> +flexcan_fifo_push(void *inst, int n_occupied) "%p: n_slots_occupied %i"
>> +flexcan_reset(void* inst) "%p: resetting"
>> +flexcan_mem_op(void *inst, const char *op, uint32_t v, int offset, const 
>> char *reg_name, int size) "%p: %s 0x%08x at offset %i register %s size %i"
>> +flexcan_mem_accepts(void *inst, int offset, int size, int is_write, int 
>> is_supv) "%p: denied access: offset %i size %i is_write %i is_supv %i"
>> +flexcan_get_timestamp(void *inst, int64_t time_elapsed_ms, uint32_t 
>> bitrate, uint64_t cycles, uint32_t shift, uint32_t timestamp) "%p: 
>> time_elapsed %" PRIi64 "ms bitrate %ub/s cycles %" PRIu64 " shift %u 
>> timestamp 0x%04x"
>> +flexcan_get_bitrate(void *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) "%p: pe_freq %uHz prescaler %u s_freq %uHz tseg1 %uq tseg2 %uq 
>> total %uq/b bitrate %ub/s"
>> +flexcan_timer_start(void *inst, uint32_t bitrate, uint32_t value) "%p: 
>> bitrate %ub/s value 0x%04x"
>> +flexcan_timer_stop(void *inst, uint32_t bitrate, uint32_t value) "%p: 
>> bitrate %ub/s value 0x%04x"
>> +flexcan_mb_rx_check_mb(void *inst, int mbidx, const char *code, int 
>> is_matched, int is_ftr, int is_serviced, int is_locked) "%p: checking mb %i 
>> code %s is_matched %i is_free_to_receive %i is_serviced %i is_locked %i"
>> +flexcan_receive(void *inst, size_t n_frames) "%p: received %zu frames"
>> +
>> # xlnx-zynqmp-can.c
>> xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) "ISR: 0x%08x 
>> IER: 0x%08x IRQ: 0x%08x"
>> xlnx_can_reset(uint32_t val) "Resetting controller with value = 0x%08x"
>> diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h
>> new file mode 100644
>> index 0000000000..eceb6a7920
>> --- /dev/null
>> +++ b/include/hw/net/flexcan.h
>> @@ -0,0 +1,153 @@
>> +/*
>> + * QEMU model of the NXP FLEXCAN device.
>> + *
>> + * Copyright (c) 2025 Matyas Bobek <[email protected]>
>> + *
>> + * Based on CTU CAN FD emulation implemented by Jan Charvat.
>> + *
>> + * This code is licensed under the GPL version 2 or later.  See
>> + * the COPYING file in the top-level directory.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef HW_CAN_FLEXCAN_H
>> +#define HW_CAN_FLEXCAN_H
>> +
>> +#include "hw/sysbus.h"
>> +#include "exec/hwaddr.h"
>> +#include "net/can_emu.h"
>> +#include "qom/object.h"
>> +#include "hw/misc/imx_ccm.h"
>> +
>> +#define FLEXCAN_FIFO_DEPTH 6
>> +#define FLEXCAN_MAILBOX_COUNT 64
>> +
>> +/* view of single message buffer registers */
>> +typedef struct FlexcanRegsMessageBuffer {
>> +    uint32_t can_ctrl;
>> +    uint32_t can_id;
>> +    uint32_t data[2];
>> +} FlexcanRegsMessageBuffer;
>> +
>> +/* RX FIFO view of message buffer registers */
>> +typedef struct FlexcanRegsRXFifo {
>> +    /* 6 message buffer deep queue, queue back first */
>> +    FlexcanRegsMessageBuffer mb_back;
>> +    FlexcanRegsMessageBuffer mbs_queue[5];
>> +
>> +    /* number of filter elements depends on ctrl2 | FLEXCAN_CTRL2_RFFN */
>> +    uint32_t                 filter_table_els[128];
>> +} FlexcanRegsRXFifo;
>> +
>
>The following structure looks like being based on Linux. Does this affect the 
>copyright of this file?
>
>Shall we reuse constants like FLEXCAN_MAILBOX_COUNT and FLEXCAN_FIFO_DEPTH in 
>the structure definition? Up to you.
>
>> +/* Structure of the hardware registers */
>> +typedef struct FlexcanRegs {
>> +    uint32_t mcr;                /* 0x00 */
>> +    uint32_t ctrl;               /* 0x04 - not affected by soft reset */
>> +    uint32_t timer;              /* 0x08 */
>> +    uint32_t tcr;                /* 0x0C */
>> +    uint32_t rxmgmask;           /* 0x10 - not affected by soft reset */
>> +    uint32_t rx14mask;           /* 0x14 - not affected by soft reset */
>> +    uint32_t rx15mask;           /* 0x18 - not affected by soft reset */
>> +    uint32_t ecr;                /* 0x1C */
>> +    uint32_t esr;                /* 0x20 */
>> +    uint32_t imask2;             /* 0x24 */
>> +    uint32_t imask1;             /* 0x28 */
>> +    uint32_t iflag2;             /* 0x2C */
>> +    uint32_t iflag1;             /* 0x30 */
>> +    union {                      /* 0x34 */
>> +        uint32_t gfwr_mx28;      /* MX28, MX53 */
>> +        uint32_t ctrl2;          /* MX6, VF610 - not affected by soft reset 
>> */
>> +    };
>> +    uint32_t esr2;               /* 0x38 */
>> +    uint32_t imeur;              /* 0x3C, unused */
>> +    uint32_t lrfr;               /* 0x40, unused */
>> +    uint32_t crcr;               /* 0x44 */
>> +    uint32_t rxfgmask;           /* 0x48 */
>> +    uint32_t rxfir;              /* 0x4C - not affected by soft reset */
>> +    uint32_t cbt;                /* 0x50, unused - not affected by soft 
>> reset */
>> +    uint32_t _reserved2;         /* 0x54 */
>> +    uint32_t dbg1;               /* 0x58, unused */
>> +    uint32_t dbg2;               /* 0x5C, unused */
>> +    uint32_t _reserved3[8];      /* 0x60 */
>> +    union {                      /* 0x80 - not affected by soft reset */
>> +        uint32_t mb[256];
>> +        FlexcanRegsMessageBuffer mbs[64];
>> +        FlexcanRegsRXFifo fifo;
>> +    };
>> +    uint32_t _reserved4[256];    /* 0x480 */
>> +    uint32_t rximr[64];          /* 0x880 - not affected by soft reset */
>> +    uint32_t _reserved5[24];     /* 0x980 */
>> +    uint32_t gfwr_mx6;           /* 0x9E0 - MX6 */
>> +
>> +    /* the rest is unused except for SMB */
>> +    uint32_t _reserved6[39];     /* 0x9E4 */
>> +    uint32_t _rxfir[6];          /* 0xA80 */
>> +    uint32_t _reserved8[2];      /* 0xA98 */
>> +    uint32_t _rxmgmask;          /* 0xAA0 */
>> +    uint32_t _rxfgmask;          /* 0xAA4 */
>> +    uint32_t _rx14mask;          /* 0xAA8 */
>> +    uint32_t _rx15mask;          /* 0xAAC */
>> +    uint32_t tx_smb[4];          /* 0xAB0 */
>> +    union {                      /* 0xAC0, used for SMB emulation */
>> +        uint32_t rx_smb0_raw[4];
>> +        FlexcanRegsMessageBuffer rx_smb0;
>> +    };
>> +    uint32_t rx_smb1[4];         /* 0xAD0 */
>> +    uint32_t mecr;               /* 0xAE0 */
>> +    uint32_t erriar;             /* 0xAE4 */
>> +    uint32_t erridpr;            /* 0xAE8 */
>> +    uint32_t errippr;            /* 0xAEC */
>> +    uint32_t rerrar;             /* 0xAF0 */
>> +    uint32_t rerrdr;             /* 0xAF4 */
>> +    uint32_t rerrsynr;           /* 0xAF8 */
>> +    uint32_t errsr;              /* 0xAFC */
>> +    uint32_t _reserved7[64];     /* 0xB00 */
>> +    uint32_t fdctrl;             /* 0xC00 - not affected by soft reset */
>> +    uint32_t fdcbt;              /* 0xC04 - not affected by soft reset */
>> +    uint32_t fdcrc;              /* 0xC08 */
>> +    uint32_t _reserved9[199];    /* 0xC0C */
>> +    uint32_t tx_smb_fd[18];      /* 0xF28 */
>> +    uint32_t rx_smb0_fd[18];     /* 0xF70 */
>> +    uint32_t rx_smb1_fd[18];     /* 0xFB8 */
>> +} FlexcanRegs;
>> +
>> +typedef struct FlexcanState {
>> +    SysBusDevice        parent_obj;
>
>Let's add a blank line here to visually separate the base class.
>
>> +    MemoryRegion        iomem;
>> +    IMXCCMState        *ccm;
>> +    qemu_irq            irq;
>> +
>> +    CanBusState        *canbus;
>> +    CanBusClientState   bus_client;
>> +
>> +    union {
>> +        FlexcanRegs     regs;
>> +        uint32_t        regs_raw[sizeof(FlexcanRegs) / 4];
>> +    };
>> +    int64_t             timer_start;
>> +    uint64_t            last_rx_timer_cycles;
>> +    int32_t             locked_mbidx;
>> +    int32_t             smb_target_mbidx;
>> +    uint32_t            timer_freq;
>> +} FlexcanState;
>> +
>> +#define TYPE_CAN_FLEXCAN "flexcan"
>> +
>> +OBJECT_DECLARE_SIMPLE_TYPE(FlexcanState, CAN_FLEXCAN);
>
>The folling symbols...
>
>> +
>> +void flexcan_init(Object *obj);
>> +void flexcan_hardware_reset(FlexcanState *s);
>> +
>> +bool flexcan_can_receive(CanBusClientState *s);
>> +ssize_t flexcan_receive(CanBusClientState *s, const qemu_can_frame *frames,
>> +                        size_t frames_cnt);
>> +
>> +void flexcan_mem_write(void *obj, hwaddr addr, uint64_t val, unsigned size);
>> +uint64_t flexcan_mem_read(void *obj, hwaddr addr, unsigned size);
>> +bool flexcan_mem_accepts(void *obj, hwaddr addr, unsigned size, bool 
>> is_write,
>> +                         MemTxAttrs attrs);
>> +
>> +extern const VMStateDescription vmstate_flexcan;
>
>... aren't used outside the .c file, so please keep them private. You may need 
>to move the "flexcan_ops" definition in the .c file further down to avoid 
>forward declarations.
>
>Quite a lot of comments from my side, I hope you don'f feel intimidated! I'm 
>looking forward to seeing FlexCAN support upstreamed. If you have any 
>questions please feel free to ask.

Ping

Any help needed?

Best regards,
Bernhard

>
>Best regards,
>Bernhard
>
>> +
>> +#endif


Reply via email to