Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2231479/?format=api
{ "id": 2231479, "url": "http://patchwork.ozlabs.org/api/patches/2231479/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/6d65c7c296fe5c7e4305cbcca9208babbee106a6.1777571962.git.matyas.bobek@gmail.com/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api", "name": "QEMU Development", "link_name": "qemu-devel", "list_id": "qemu-devel.nongnu.org", "list_email": "qemu-devel@nongnu.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<6d65c7c296fe5c7e4305cbcca9208babbee106a6.1777571962.git.matyas.bobek@gmail.com>", "list_archive_url": null, "date": "2026-04-30T18:26:06", "name": "[v3,4/7] hw/net/can/flexcan: NXP FlexCAN core emulation", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "f3237a5ba0cfe761f2e67ecb305d5308e51e2df9", "submitter": { "id": 92267, "url": "http://patchwork.ozlabs.org/api/people/92267/?format=api", "name": "Matyáš Bobek", "email": "matyas.bobek@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/6d65c7c296fe5c7e4305cbcca9208babbee106a6.1777571962.git.matyas.bobek@gmail.com/mbox/", "series": [ { "id": 502356, "url": "http://patchwork.ozlabs.org/api/series/502356/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=502356", "date": "2026-04-30T18:26:02", "name": "hw/arm/sabrelite: Add FlexCAN support", "version": 3, "mbox": "http://patchwork.ozlabs.org/series/502356/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2231479/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2231479/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=DgHB2Phz;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)" ], "Received": [ "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g62hx1cJFz1yK1\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 04:27:43 +1000 (AEST)", "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wIW6X-0006kw-UR; Thu, 30 Apr 2026 14:27:18 -0400", "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <matyas.bobek@gmail.com>)\n id 1wIW61-0006Xb-Sz\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 14:26:50 -0400", "from mail-wr1-x42e.google.com ([2a00:1450:4864:20::42e])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <matyas.bobek@gmail.com>)\n id 1wIW5i-0005Ue-3W\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 14:26:33 -0400", "by mail-wr1-x42e.google.com with SMTP id\n ffacd0b85a97d-44261378651so1557950f8f.0\n for <qemu-devel@nongnu.org>; Thu, 30 Apr 2026 11:26:24 -0700 (PDT)", "from acidburn.pod.cvut.cz (acidburn.pod.cvut.cz. [147.32.90.2])\n by smtp.gmail.com with ESMTPSA id\n ffacd0b85a97d-447b76e5c00sm14674925f8f.25.2026.04.30.11.26.21\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 30 Apr 2026 11:26:22 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777573583; x=1778178383; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=PBdg4YQJjucaXluY8oTcVoBiEDn5/59abAbeWnL88T0=;\n b=DgHB2PhzX4vVD33zXST2MXE/CAw5UBiTn11u1obPdlS6VCt0I65hTJrk5O3L8PXftu\n BPvZaKFcZOa7amJkd3y6Oh2bU32ar3a48/bMZmb5fa2EaSBzWkeTeDRYSB+AC5sC13GH\n l0C6gG5Dk2DVrsiCG5YRB8ipk6lpTfEcxjB4aI4GrUvilNLZaNSS7uusGhyeNng6w4Zn\n Vzm0wBAQzf86wZ1i5yk4VxnPjsN+7hVv2EpOMWtUz+c3y+/DN4EshTkwdZ1ihkQUdlxw\n wydIEmsAulO4CYLmCTngt74dg/NhNfc5lmvzOM0WhaGIxN67NGlxPfnqbjgcMwczCUEo\n EmQw==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777573583; x=1778178383;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=PBdg4YQJjucaXluY8oTcVoBiEDn5/59abAbeWnL88T0=;\n b=hhTQWg5Em/5gUx4n/Gmuwo4aD6LR+cDazQoKVZsbq7pdB8tTqGdnCKBJqW5meMF/Zk\n VPoER0a6Pd9xOz2CuaPQFnK7ramIbOrPBtqUtHyg87M3bgOsCOGxyvWAjxXAtEgs8inF\n r1a9/MC53NPS2aNfVMOYFlh586pyoP+zB6t1BLlimdtFx3r2IvkBfa3jpHUXOngcugW5\n wwQ4Y2XMAyFi678h7GcmJr8fvGcT5g9OielcJUIoXHdmhCbheBYjhhhpH4nXDIZnd0M3\n UiSKen/D8PZmsIeLEBwblE9viJiB/P8geLl0eyW9r24LlASL7xTxFfI7/wjldg1GEL5+\n ohRw==", "X-Gm-Message-State": "AOJu0Yw33H0wamFKbqJNJLwAng1FT/rI56I+ouA0A2zVrLIf00OieeiX\n 3vyXKfabzhQTTjgn4cYa/75+nTPYBHU6nvOHtgcIonly4as2ty3iOkZR0l4A2yZ6", "X-Gm-Gg": "AeBDievg/E4Lamh71gjSEZ5D5/cuFBtvt4AOmb1IQ8cKpCargbMVmRyQexit0HdLu4B\n gbuPr0g0Dwq/XwrYqUCQ/gLWfQYxKOjE9pIKdt6qPMs7H2+/aNus9rdYZ6d/kFQnaYRqi8ien7c\n xxxfl7uVLrauT5Dy6Hx4lJoSxLSg08dFtFI70WClB5P3BNcvMGxz4Y7zOZ1QmM8s2jQH8nuNqho\n a9UW0vOmrYs0pCm7+UZxedoj74hw0d3JONyqW1hkatYeRhypij63nwndPrdwLthImylTeulECDQ\n MasQPGyron1Go3fD54U2pJ9DjGY4Go72xDHpDkYyIyKGlL6+76OCw8yxlPkoe3c+5lgnC8wAC53\n gJsctJplGRHwAToH/ao1fFpLz73ozbCkJue/E4dz7K6/nah7JceTW4z7fHa5HXIAcs2Bdef+4I9\n T091PKhG7UbUXFJqE7UOpXpijQMjUcmnomHgKKOGqsz8xTaFDNi9najH5FCvB28D3d", "X-Received": "by 2002:a5d:5a4c:0:b0:446:e2ba:45b with SMTP id\n ffacd0b85a97d-4494e5f1c7dmr4292779f8f.11.1777573583112;\n Thu, 30 Apr 2026 11:26:23 -0700 (PDT)", "From": "=?utf-8?b?TWF0ecOhxaEgQm9iZWs=?= <matyas.bobek@gmail.com>", "To": "qemu-devel@nongnu.org, Matyas Bobek <bobekmat@fel.cvut.cz>,\n Pavel Pisa <pisa@fel.cvut.cz>, Bernhard Beschow <shentey@gmail.com>", "Cc": "qemu-arm@nongnu.org, Marc Kleine-Budde <mkl@pengutronix.de>,\n Oliver Hartkopp <socketcan@hartkopp.net>,\n Nikita Ostrenkov <n.ostrenkov@gmail.com>,\n Peter Maydell <peter.maydell@linaro.org>,\n =?utf-8?b?TWF0ecOhxaEgQm9iZWs=?= <matyas.bobek@gmail.com>", "Subject": "[PATCH v3 4/7] hw/net/can/flexcan: NXP FlexCAN core emulation", "Date": "Thu, 30 Apr 2026 20:26:06 +0200", "Message-ID": "\n <6d65c7c296fe5c7e4305cbcca9208babbee106a6.1777571962.git.matyas.bobek@gmail.com>", "X-Mailer": "git-send-email 2.53.0", "In-Reply-To": "<cover.1777571962.git.matyas.bobek@gmail.com>", "References": "<cover.1777571962.git.matyas.bobek@gmail.com>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Received-SPF": "pass client-ip=2a00:1450:4864:20::42e;\n envelope-from=matyas.bobek@gmail.com; helo=mail-wr1-x42e.google.com", "X-Spam_score_int": "-10", "X-Spam_score": "-1.1", "X-Spam_bar": "-", "X-Spam_report": "(-1.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FORGED_GMAIL_RCVD=1,\n FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=no autolearn_force=no", "X-Spam_action": "no action", "X-BeenThere": "qemu-devel@nongnu.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "qemu development <qemu-devel.nongnu.org>", "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>", "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>", "List-Post": "<mailto:qemu-devel@nongnu.org>", "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>", "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>", "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org", "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org" }, "content": "Added the FlexCAN2 emulator implementation core, with\nCAN_FLEXCAN Kconfig flag and MAINTAINERS entry.\n\nFlexCAN2 version can be found in i.MX6 SoCs and others.\n\nMore information about the implementation can be found in [1].\n\n[1] http://dspace.cvut.cz/bitstream/handle/10467/122654/F3-BP-2025-Bobek-Matyas-BP_Bobek_FlexCAN_final_4.pdf\n\nSigned-off-by: Matyáš Bobek <matyas.bobek@gmail.com>\nSigned-off-by: Pavel Pisa <pisa@fel.cvut.cz>\nTested-by: Pavel Pisa <pisa@fel.cvut.cz>\nReviewed-by: Bernhard Beschow <shentey@gmail.com>\nReviewed-by: Pavel Pisa <pisa@fel.cvut.cz>\n---\n MAINTAINERS | 8 +\n hw/net/Kconfig | 5 +\n hw/net/can/flexcan.c | 1395 +++++++++++++++++++++++++++++++++++++\n hw/net/can/flexcan_regs.h | 193 +++++\n hw/net/can/meson.build | 1 +\n hw/net/can/trace-events | 18 +\n include/hw/net/flexcan.h | 139 ++++\n 7 files changed, 1759 insertions(+)\n create mode 100644 hw/net/can/flexcan.c\n create mode 100644 hw/net/can/flexcan_regs.h\n create mode 100644 include/hw/net/flexcan.h", "diff": "diff --git a/MAINTAINERS b/MAINTAINERS\nindex f109e46172..59a3614a58 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -2091,6 +2091,14 @@ F: hw/net/can/xlnx-*\n F: include/hw/net/xlnx-*\n F: tests/qtest/xlnx-can*-test*\n \n+FlexCAN\n+M: Matyas Bobek <matyas.bobek@gmail.com>\n+M: Pavel Pisa <pisa@cmp.felk.cvut.cz>\n+S: Maintained\n+F: hw/net/can/flexcan.c\n+F: hw/net/can/flexcan_regs.h\n+F: include/hw/net/flexcan.h\n+\n EDU\n M: Jiri Slaby <jslaby@suse.cz>\n S: Maintained\ndiff --git a/hw/net/Kconfig b/hw/net/Kconfig\nindex f9a1dfb80d..b56a173eed 100644\n--- a/hw/net/Kconfig\n+++ b/hw/net/Kconfig\n@@ -157,3 +157,8 @@ config CAN_CTUCANFD_PCI\n default y if PCI_DEVICES\n depends on PCI && CAN_CTUCANFD\n select CAN_BUS\n+\n+config CAN_FLEXCAN\n+ bool\n+ depends on IMX\n+ select CAN_BUS\ndiff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c\nnew file mode 100644\nindex 0000000000..61a59e6086\n--- /dev/null\n+++ b/hw/net/can/flexcan.c\n@@ -0,0 +1,1395 @@\n+/*\n+ * QEMU model of the NXP FLEXCAN device.\n+ *\n+ * This implementation is based on the following reference manual:\n+ * i.MX 6Dual/6Quad Applications Processor Reference Manual\n+ * Document Number: IMX6DQRM, Rev. 6, 05/2020\n+ *\n+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>\n+ *\n+ * Based on CTU CAN FD emulation implemented by Jan Charvat.\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"qemu/log.h\"\n+#include \"hw/core/sysbus.h\"\n+#include \"qapi/error.h\"\n+#include \"hw/core/irq.h\"\n+#include \"migration/vmstate.h\"\n+#include \"net/can_emu.h\"\n+#include \"hw/core/qdev-properties.h\"\n+#include \"trace.h\"\n+\n+#include \"hw/net/flexcan.h\"\n+#include \"flexcan_regs.h\"\n+#include \"qemu/timer.h\"\n+\n+/*\n+ * Indicates MB w/ received frame has not been serviced yet\n+ * This is an emulator-only flag in position of unused (reserved) bit\n+ * of message buffer control register\n+ */\n+#define FLEXCAN_MB_CNT_NOT_SRV BIT(23)\n+/**\n+ * if no MB is locked, FlexcanState.locked_mb\n+ * is set to FLEXCAN_NO_MB_LOCKED\n+ */\n+#define FLEXCAN_NO_MB_LOCKED -1\n+/**\n+ * if no frame is waiting in the SMB, FlexcanState.smb_target_mbid\n+ * is set to FLEXCAN_SMB_EMPTY\n+ */\n+#define FLEXCAN_SMB_EMPTY -1\n+/**\n+ * When the module is disabled or in freeze mode,\n+ * the timer is not running. That is indicated by setting\n+ * FlexcanState.timer_start to FLEXCAN_TIMER_STOPPED.\n+ */\n+#define FLEXCAN_TIMER_STOPPED -1\n+\n+/* These constants are returned by flexcan_fifo_rx() and flexcan_mb_rx(), */\n+enum FlexcanRx {\n+/* Retry the other receiving mechanism (ie. message bufer or mailbox). */\n+ FLEXCAN_RX_SEARCH_RETRY,\n+/* The frame was received and stored. */\n+ FLEXCAN_RX_SEARCH_ACCEPT,\n+/* The frame was filtered out and dropped. */\n+ FLEXCAN_RX_SEARCH_DROPPED,\n+};\n+\n+/*\n+ * These constants are returned by flexcan_mb_rx_check_mb().\n+ * See flexcan_mb_rx_check_mb() kerneldoc for details.\n+ */\n+enum FlexcanCheck {\n+ FLEXCAN_CHECK_MB_NIL = 0,\n+ FLEXCAN_CHECK_MB_MATCH = 3,\n+ FLEXCAN_CHECK_MB_MATCH_NON_FREE = 1,\n+ FLEXCAN_CHECK_MB_MATCH_LOCKED = 5,\n+};\n+\n+static const FlexcanRegs flexcan_regs_write_mask = {\n+ .mcr = 0xF6EB337F,\n+ .ctrl = 0xFFFFFFFF,\n+ .timer = 0xFFFFFFFF,\n+ .tcr = 0xFFFFFFFF,\n+ .rxmgmask = 0xFFFFFFFF,\n+ .rx14mask = 0xFFFFFFFF,\n+ .rx15mask = 0xFFFFFFFF,\n+ .ecr = 0xFFFFFFFF,\n+ .esr = 0xFFFFFFFF,\n+ .imask2 = 0xFFFFFFFF,\n+ .imask1 = 0xFFFFFFFF,\n+ .iflag2 = 0,\n+ .iflag1 = 0,\n+ .ctrl2 = 0xFFFFFFFF,\n+ .esr2 = 0,\n+ .imeur = 0,\n+ .lrfr = 0,\n+ .crcr = 0,\n+ .rxfgmask = 0xFFFFFFFF,\n+ .rxfir = 0,\n+ .cbt = 0,\n+ ._reserved2 = 0,\n+ .dbg1 = 0,\n+ .dbg2 = 0,\n+ .mbs = { [0 ... 63] = {\n+ .can_ctrl = 0xFFFFFFFF & ~FLEXCAN_MB_CNT_NOT_SRV,\n+ .can_id = 0xFFFFFFFF,\n+ .data = { 0xFFFFFFFF, 0xFFFFFFFF },\n+ } },\n+ ._reserved4 = {0},\n+ .rximr = { [0 ... 63] = 0xFFFFFFFF },\n+ ._reserved5 = {0},\n+ .gfwr_mx6 = 0xFFFFFFFF,\n+ ._reserved6 = {0},\n+ ._reserved8 = {0},\n+ .rx_smb0_raw = {0, 0, 0, 0},\n+ .rx_smb1 = {0, 0, 0, 0},\n+};\n+static const FlexcanRegs flexcan_regs_reset_mask = {\n+ .mcr = 0x80000000,\n+ .ctrl = 0xFFFFFFFF,\n+ .timer = 0,\n+ .tcr = 0,\n+ .rxmgmask = 0xFFFFFFFF,\n+ .rx14mask = 0xFFFFFFFF,\n+ .rx15mask = 0xFFFFFFFF,\n+ .ecr = 0,\n+ .esr = 0,\n+ .imask2 = 0,\n+ .imask1 = 0,\n+ .iflag2 = 0,\n+ .iflag1 = 0,\n+ .ctrl2 = 0xFFFFFFFF,\n+ .esr2 = 0,\n+ .imeur = 0,\n+ .lrfr = 0,\n+ .crcr = 0,\n+ .rxfgmask = 0xFFFFFFFF,\n+ .rxfir = 0xFFFFFFFF,\n+ .cbt = 0,\n+ ._reserved2 = 0,\n+ .dbg1 = 0,\n+ .dbg2 = 0,\n+ .mb = {0xFFFFFFFF},\n+ ._reserved4 = {0},\n+ .rximr = {0xFFFFFFFF},\n+ ._reserved5 = {0},\n+ .gfwr_mx6 = 0,\n+ ._reserved6 = {0},\n+ ._reserved8 = {0},\n+ .rx_smb0_raw = {0, 0, 0, 0},\n+ .rx_smb1 = {0, 0, 0, 0},\n+};\n+\n+/* length of buffer used to format register names in trace output */\n+#define FLEXCAN_DBG_BUF_LEN 16\n+\n+/**\n+ * flexcan_dbg_mb_code_strs - Readable names for CODE field codes\n+ *\n+ * Readable names for possible values of CODE field in message buffer\n+ * control word.\n+ */\n+static const char *flexcan_dbg_mb_code_strs[16] = {\n+ \"INACTIVE_RX\",\n+ \"FULL\",\n+ \"EMPTY\",\n+ \"OVERRUN\",\n+ \"INACTIVE_TX\",\n+ \"RANSWER\",\n+ \"DATA\",\n+ \"TANSWER\"\n+};\n+\n+/**\n+ * flexcan_dbg_mb_code() - Get the string representation of a mailbox code\n+ * @mb_ctrl: The mailbox control register value\n+ * @buf: The buffer to store the string representation\n+ *\n+ * Return: Either constant string or string formatted into @buf\n+ */\n+static const char *flexcan_dbg_mb_code(uint32_t mb_ctrl, char *buf)\n+{\n+ uint32_t code = mb_ctrl & FLEXCAN_MB_CODE_MASK;\n+ uint32_t code_idx = code >> 24;\n+ if (code == FLEXCAN_MB_CODE_TX_ABORT) {\n+ return \"ABORT\";\n+ } else {\n+ const char *code_str = flexcan_dbg_mb_code_strs[code_idx >> 1];\n+ if (code_idx & 1) {\n+ g_snprintf(buf, FLEXCAN_DBG_BUF_LEN, \"%s+BUSY\", code_str);\n+ return buf;\n+ }\n+\n+ return code_str;\n+ }\n+}\n+\n+static const char *flexcan_dbg_reg_name_fixed(hwaddr addr)\n+{\n+ switch (addr) {\n+ case offsetof(FlexcanRegs, mcr):\n+ return \"MCR\";\n+ case offsetof(FlexcanRegs, ctrl):\n+ return \"CTRL\";\n+ case offsetof(FlexcanRegs, timer):\n+ return \"TIMER\";\n+ case offsetof(FlexcanRegs, esr):\n+ return \"ESR\";\n+ case offsetof(FlexcanRegs, rxmgmask):\n+ return \"RXMGMASK\";\n+ case offsetof(FlexcanRegs, rx14mask):\n+ return \"RX14MASK\";\n+ case offsetof(FlexcanRegs, rx15mask):\n+ return \"RX15MASK\";\n+ case offsetof(FlexcanRegs, rxfgmask):\n+ return \"RXFGMASK\";\n+ case offsetof(FlexcanRegs, ecr):\n+ return \"ECR\";\n+ case offsetof(FlexcanRegs, ctrl2):\n+ return \"CTRL2\";\n+ case offsetof(FlexcanRegs, imask2):\n+ return \"IMASK2\";\n+ case offsetof(FlexcanRegs, imask1):\n+ return \"IMASK1\";\n+ case offsetof(FlexcanRegs, iflag2):\n+ return \"IFLAG2\";\n+ case offsetof(FlexcanRegs, iflag1):\n+ return \"IFLAG1\";\n+ }\n+ return NULL;\n+}\n+\n+static inline void flexcan_trace_mem_op(FlexcanState *s, hwaddr addr,\n+ uint32_t value, int size, bool is_wr)\n+{\n+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MEM_OP)) {\n+ const char *reg_name = \"unknown\";\n+ char reg_name_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };\n+ const char *reg_name_fixed = flexcan_dbg_reg_name_fixed(addr);\n+ const char *op_string = is_wr ? \"write\" : \"read\";\n+\n+ if (reg_name_fixed) {\n+ reg_name = reg_name_fixed;\n+ } else if (addr >= 0x80 && addr < 0x480) {\n+ int mbidx = (addr - 0x80) / 16;\n+ g_snprintf(reg_name_buf, sizeof(reg_name_buf), \"MB%i\", mbidx);\n+ reg_name = reg_name_buf;\n+ } else if (addr >= 0x880 && addr < 0x9e0) {\n+ int id = (addr - 0x880) / 4;\n+ g_snprintf(reg_name_buf, sizeof(reg_name_buf), \"RXIMR%i\", id);\n+ reg_name = reg_name_buf;\n+ }\n+\n+ trace_flexcan_mem_op(DEVICE(s)->canonical_path, op_string, value, addr,\n+ reg_name, size);\n+ }\n+}\n+\n+static enum FlexcanRx flexcan_mb_rx(FlexcanState *s,\n+ const qemu_can_frame *frame);\n+static void flexcan_mb_unlock(FlexcanState *s);\n+\n+/* ========== Mailbox Utils ========== */\n+\n+/**\n+ * flexcan_mailbox_count() - Get number of enabled mailboxes\n+ * @s: FlexCAN device pointer\n+ *\n+ * Count is based on MCR[MAXMB] field. Note that some of those mailboxes\n+ * might be part of queue or queue ID filters or ordinary message buffers.\n+ */\n+static inline int flexcan_enabled_mailbox_count(const FlexcanState *s)\n+{\n+ return (s->regs.mcr & FLEXCAN_MCR_MAXMB(UINT32_MAX)) + 1;\n+}\n+\n+/**\n+ * flexcan_get_first_message_buffer() - Get pointer to first message buffer\n+ * @s: FlexCAN device pointer\n+ *\n+ * In context of this function, message buffer means a mailbox which is not\n+ * a queue element nor a queue filter. Note this function does not take\n+ * MCR[MAXMB] into account, meaning that the returned mailbox\n+ * might be disabled.\n+ */\n+static FlexcanRegsMessageBuffer *flexcan_get_first_message_buffer(\n+ FlexcanState *s)\n+{\n+ if (s->regs.mcr & FLEXCAN_MCR_FEN) {\n+ int rffn = (s->regs.ctrl2 & FLEXCAN_CTRL2_RFFN(UINT32_MAX)) >> 24;\n+ return s->regs.mbs + 8 + 2 * rffn;\n+ }\n+\n+ return s->regs.mbs;\n+}\n+\n+/**\n+ * flexcan_get_last_enabled_mailbox() - Get pointer to last enabled mailbox.\n+ * @s: FlexCAN device pointer\n+ *\n+ * When used with flexcan_get_first_message_buffer(), all mailboxes *ptr in\n+ * range `first_message_buffer() <= ptr <= last_enabled_mailbox` are valid\n+ * message buffer mailboxes.\n+ *\n+ * Return: Last enabled mailbox in MCR[MAXMB] sense. The mailbox might be\n+ * of any type.\n+ */\n+static inline FlexcanRegsMessageBuffer *flexcan_get_last_enabled_mailbox(\n+ FlexcanState *s)\n+{\n+ return s->regs.mbs + flexcan_enabled_mailbox_count(s);\n+}\n+\n+/* ========== Free-running Timer ========== */\n+static inline int64_t flexcan_get_time(void)\n+{\n+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+}\n+\n+/**\n+ * flexcan_get_bitrate() - Calculate CAN bitrate (in Hz)\n+ * @s: FlexCAN device pointer\n+ *\n+ * The bitrate is determined by FlexCAN configuration in CTRL1 register,\n+ * and CCM co\n+ */\n+static uint32_t flexcan_get_bitrate(FlexcanState *s)\n+{\n+ uint32_t conf_presdiv = (s->regs.ctrl & FLEXCAN_CTRL_PRESDIV_MASK) >> 24;\n+ uint32_t conf_pseg1 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG1_MASK) >> 19;\n+ uint32_t conf_pseg2 = (s->regs.ctrl & FLEXCAN_CTRL_PSEG2_MASK) >> 16;\n+ uint32_t conf_propseg = s->regs.ctrl & FLEXCAN_CTRL_PROPSEG_MASK;\n+\n+ /* N of time quanta for segments */\n+ uint32_t tseg1 = 2 + conf_pseg1 + conf_propseg;\n+ uint32_t tseg2 = 1 + conf_pseg2;\n+ uint32_t total_qpb = 1 + tseg1 + tseg2;\n+\n+ uint32_t pe_freq, s_freq, bitrate;\n+\n+ assert(s->ccm);\n+\n+ /* s_freq: CAN clock from CCM divided by the prescaler */\n+ pe_freq = imx_ccm_get_clock_frequency(s->ccm, CLK_CAN);\n+ s_freq = pe_freq / (1 + conf_presdiv);\n+ bitrate = s_freq / total_qpb;\n+\n+ trace_flexcan_get_bitrate(DEVICE(s)->canonical_path, pe_freq,\n+ 1 + conf_presdiv, s_freq, tseg1, tseg2, total_qpb,\n+ bitrate);\n+ return bitrate;\n+}\n+\n+/**\n+ * int128_mul_6464() - Multiply two 64-bit integers into a 128-bit one\n+ */\n+static Int128 int128_muls_6464(int64_t ai, int64_t bi)\n+{\n+ uint64_t l, h;\n+\n+ muls64(&l, &h, ai, bi);\n+ return int128_make128(l, h);\n+}\n+\n+/**\n+ * flexcan_get_timestamp() - Get current value of the 16-bit free-running timer\n+ * @s: FlexCAN device pointer\n+ * @mk_unique: if true, make the timestamp unique by incrementing it if needed\n+ */\n+static uint32_t flexcan_get_timestamp(FlexcanState *s, bool mk_unique)\n+{\n+ const Int128 nanoseconds_in_second = int128_makes64((int64_t)1e9);\n+ Int128 ncycles, cycles128;\n+ int64_t current_time, elapsed_time_ns;\n+ uint64_t cycles;\n+ uint32_t rv, shift = 0;\n+\n+ if (s->timer_start == FLEXCAN_TIMER_STOPPED) {\n+ /* timer is not running, return last value */\n+ trace_flexcan_get_timestamp(DEVICE(s)->canonical_path, -1, 0, 0, 0,\n+ s->regs.timer);\n+ return s->regs.timer;\n+ }\n+\n+ current_time = flexcan_get_time();\n+ elapsed_time_ns = current_time - s->timer_start;\n+ if (elapsed_time_ns < 0) {\n+ trace_flexcan_timer_overflow(DEVICE(s)->canonical_path, current_time,\n+ s->timer_start, elapsed_time_ns);\n+ return 0xFFFF;\n+ }\n+\n+ ncycles = int128_muls_6464(s->timer_freq, elapsed_time_ns);\n+ cycles128 = int128_divs(ncycles, nanoseconds_in_second);\n+ /* 64 bits hold for over 50k years at 10MHz */\n+ cycles = int128_getlo(cycles128);\n+\n+ if (mk_unique && cycles <= s->last_rx_timer_cycles) {\n+ shift = 1;\n+ cycles = s->last_rx_timer_cycles + shift;\n+ }\n+\n+ s->last_rx_timer_cycles = cycles;\n+ rv = (uint32_t)cycles & 0xFFFF;\n+\n+ trace_flexcan_get_timestamp(DEVICE(s)->canonical_path,\n+ elapsed_time_ns / (uint32_t)1e6,\n+ s->timer_freq, cycles, shift, rv);\n+ return rv;\n+}\n+\n+/**\n+ * flexcan_timer_start() - Start the free-running timer\n+ * @s: FlexCAN device pointer\n+ *\n+ * This should be called when the module leaves freeze mode.\n+ */\n+static void flexcan_timer_start(FlexcanState *s)\n+{\n+ s->timer_freq = flexcan_get_bitrate(s);\n+ s->timer_start = flexcan_get_time();\n+ s->last_rx_timer_cycles = 0;\n+\n+ trace_flexcan_timer_start(DEVICE(s)->canonical_path, s->timer_freq,\n+ s->regs.timer);\n+}\n+\n+/**\n+ * flexcan_timer_stop() - Stop the free-running timer\n+ * @s: FlexCAN device pointer\n+ *\n+ * This should be called when the module enters freeze mode.\n+ * Stores the current timestamp in the TIMER register.\n+ */\n+static void flexcan_timer_stop(FlexcanState *s)\n+{\n+ s->regs.timer = flexcan_get_timestamp(s, false);\n+ s->timer_start = FLEXCAN_TIMER_STOPPED;\n+\n+ trace_flexcan_timer_stop(DEVICE(s)->canonical_path, s->timer_freq,\n+ s->regs.timer);\n+}\n+\n+/* ========== IRQ handling ========== */\n+/**\n+ * flexcan_irq_update() - Update qemu_irq line based on interrupt registers\n+ * @s: FlexCAN device pointer\n+ */\n+static void flexcan_irq_update(FlexcanState *s)\n+{\n+ uint32_t mb_irqs[2];\n+ int irq_pending;\n+ /* these are all interrupt sources from FlexCAN */\n+ /* mailbox interrupt sources */\n+ mb_irqs[0] = s->regs.iflag1 & s->regs.imask1;\n+ mb_irqs[1] = s->regs.iflag2 & s->regs.imask2;\n+\n+ /**\n+ * these interrupts aren't currently used and they can never be raised\n+ *\n+ * bool irq_wake_up = (s->regs.mcr & FLEXCAN_MCR_WAK_MSK) &&\n+ * (s->regs.ecr & FLEXCAN_ESR_WAK_INT);\n+ * bool irq_bus_off = (s->regs.ctrl & FLEXCAN_CTRL_BOFF_MSK) &&\n+ * (s->regs.ecr & FLEXCAN_ESR_BOFF_INT);\n+ * bool irq_error = (s->regs.ctrl & FLEXCAN_CTRL_ERR_MSK) &&\n+ * (s->regs.ecr & FLEXCAN_ESR_ERR_INT);\n+ * bool irq_tx_warn = (s->regs.ctrl & FLEXCAN_CTRL_TWRN_MSK) &&\n+ * (s->regs.ecr & FLEXCAN_ESR_TWRN_INT);\n+ * bool irq_rx_warn = (s->regs.ctrl & FLEXCAN_CTRL_RWRN_MSK) &&\n+ * (s->regs.ecr & FLEXCAN_ESR_RWRN_INT);\n+ */\n+\n+ irq_pending = (mb_irqs[0] || mb_irqs[1]) ? 1 : 0;\n+ trace_flexcan_irq_update(DEVICE(s)->canonical_path, mb_irqs[0], mb_irqs[1],\n+ irq_pending);\n+\n+ qemu_set_irq(s->irq, irq_pending);\n+}\n+\n+/**\n+ * flexcan_irq_iflag_set() - Set IFLAG bit corresponding to MB mbidx\n+ * @s: FlexCAN device pointer\n+ * @mbidx: mailbox index\n+ */\n+static void flexcan_irq_iflag_set(FlexcanState *s, int mbidx)\n+{\n+ if (mbidx < 32) {\n+ s->regs.iflag1 |= BIT(mbidx);\n+ } else {\n+ s->regs.iflag2 |= BIT(mbidx - 32);\n+ }\n+}\n+\n+/**\n+ * flexcan_irq_iflag_clear() - Clear IFLAG bit corresponding to MB mbidx\n+ * @s: FlexCAN device pointer\n+ * @mbidx: mailbox index\n+ */\n+static void flexcan_irq_iflag_clear(FlexcanState *s, int mbidx)\n+{\n+ if (mbidx < 32) {\n+ s->regs.iflag1 &= ~BIT(mbidx);\n+ } else {\n+ s->regs.iflag2 &= ~BIT(mbidx - 32);\n+ }\n+}\n+\n+/* ========== RESET ========== */\n+static void flexcan_reset_local_state(FlexcanState *s)\n+{\n+ uint32_t *reset_mask = (uint32_t *)&flexcan_regs_reset_mask;\n+ for (int i = 0; i < (sizeof(FlexcanRegs) / 4); i++) {\n+ s->regs_raw[i] &= reset_mask[i];\n+ }\n+\n+ s->regs.mcr |= 0x5980000F;\n+ s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;\n+ s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;\n+ s->timer_start = FLEXCAN_TIMER_STOPPED;\n+\n+ trace_flexcan_reset(DEVICE(s)->canonical_path);\n+}\n+\n+static void flexcan_reset_enter(Object *obj, ResetType type)\n+{\n+ FlexcanState *s = CAN_FLEXCAN(obj);\n+\n+ memset(&s->regs, 0, sizeof(s->regs));\n+ flexcan_reset_local_state(s);\n+}\n+\n+static void flexcan_reset_hold(Object *obj, ResetType type)\n+{\n+ FlexcanState *s = CAN_FLEXCAN(obj);\n+\n+ flexcan_irq_update(s);\n+}\n+\n+\n+/* ========== Operation mode control ========== */\n+/**\n+ * flexcan_update_esr() - Update ESR based on mode and CAN bus connection state\n+ * @s: FlexCAN device pointer\n+ */\n+static void flexcan_update_esr(FlexcanState *s)\n+{\n+ bool is_running = (s->regs.mcr & FLEXCAN_MCR_NOT_RDY) == 0;\n+ /* potentially, there could be other influences on ESR[SYNCH] */\n+\n+ if (is_running && s->canbus) {\n+ s->regs.esr |= FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE;\n+ } else {\n+ s->regs.esr &= ~(FLEXCAN_ESR_SYNCH | FLEXCAN_ESR_IDLE);\n+ }\n+}\n+\n+/**\n+ * flexcan_update_esr() - Process MCR write\n+ * @s: FlexCAN device pointer\n+ * @pv: previously set MCR value\n+ *\n+ * This function expects the new MCR value to be already written in s->regs.mcr.\n+ */\n+static void flexcan_set_mcr(FlexcanState *s, const uint32_t pv)\n+{\n+ uint32_t cv = s->regs.mcr;\n+\n+ /* -- module disable mode -- */\n+ if (!(pv & FLEXCAN_MCR_MDIS) && (cv & FLEXCAN_MCR_MDIS)) {\n+ /* transition to Module Disable mode */\n+ cv |= FLEXCAN_MCR_LPM_ACK;\n+ } else if ((pv & FLEXCAN_MCR_MDIS) && !(cv & FLEXCAN_MCR_MDIS)) {\n+ /* transition from Module Disable mode */\n+ cv &= ~FLEXCAN_MCR_LPM_ACK;\n+ }\n+\n+ /* -- soft reset -- */\n+ if (!(cv & FLEXCAN_MCR_LPM_ACK) && (cv & FLEXCAN_MCR_SOFTRST)) {\n+ if (s->regs.mcr & FLEXCAN_MCR_LPM_ACK) {\n+ qemu_log_mask(LOG_GUEST_ERROR,\n+ \"%s: invalid soft reset request in low-power mode\",\n+ DEVICE(s)->canonical_path);\n+ }\n+\n+ flexcan_reset_local_state(s);\n+ cv = s->regs.mcr;\n+ }\n+\n+ /* -- freeze mode -- */\n+ if (!(cv & FLEXCAN_MCR_LPM_ACK) &&\n+ (cv & FLEXCAN_MCR_FRZ) &&\n+ (cv & FLEXCAN_MCR_HALT)) {\n+ cv |= FLEXCAN_MCR_FRZ_ACK;\n+ } else {\n+ cv &= ~FLEXCAN_MCR_FRZ_ACK;\n+ }\n+\n+ /* -- fifo mode -- */\n+ if (\n+ ((pv & FLEXCAN_MCR_FEN) && !(cv & FLEXCAN_MCR_FEN)) ||\n+ (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN))\n+ ) {\n+ /* clear iflags used by fifo */\n+ s->regs.iflag1 &= ~(\n+ FLEXCAN_IFLAG_RX_FIFO_AVAILABLE |\n+ FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |\n+ FLEXCAN_IFLAG_RX_FIFO_WARN\n+ );\n+ }\n+ if (!(pv & FLEXCAN_MCR_FEN) && (cv & FLEXCAN_MCR_FEN)) {\n+ /* zero out fifo region, we rely on zeroed can_ctrl for empty slots */\n+ memset(s->regs.mbs, 0,\n+ FLEXCAN_FIFO_DEPTH * sizeof(FlexcanRegsMessageBuffer));\n+ }\n+\n+ /*\n+ * assert NOT_RDY bit if in disable,\n+ * stop (not implemented) or freeze mode\n+ */\n+ if ((cv & FLEXCAN_MCR_LPM_ACK) || (cv & FLEXCAN_MCR_FRZ_ACK)) {\n+ cv |= FLEXCAN_MCR_NOT_RDY;\n+ } else {\n+ cv &= ~FLEXCAN_MCR_NOT_RDY;\n+ }\n+\n+ if ((pv & FLEXCAN_MCR_NOT_RDY) && !(cv & FLEXCAN_MCR_NOT_RDY)) {\n+ /* module went up, start the timer */\n+ flexcan_timer_start(s);\n+ } else if (!(pv & FLEXCAN_MCR_NOT_RDY) && (cv & FLEXCAN_MCR_NOT_RDY)) {\n+ /* module went down, store the current timer value */\n+ flexcan_timer_stop(s);\n+ }\n+\n+ s->regs.mcr = cv;\n+ flexcan_update_esr(s);\n+ trace_flexcan_set_mcr(\n+ DEVICE(s)->canonical_path,\n+ cv & FLEXCAN_MCR_LPM_ACK ? \"DISABLED\" : \"ENABLED\",\n+ (cv & FLEXCAN_MCR_FRZ_ACK || cv & FLEXCAN_MCR_LPM_ACK) ?\n+ \"FROZEN\" : \"RUNNING\",\n+ cv & FLEXCAN_MCR_FEN ? \"FIFO\" : \"MAILBOX\",\n+ cv & FLEXCAN_MCR_NOT_RDY ? \"NOT_RDY\" : \"RDY\",\n+ s->regs.esr & FLEXCAN_ESR_SYNCH ? \"SYNC\" : \"NOSYNC\"\n+ );\n+}\n+\n+/* ========== TX ========== */\n+static void flexcan_transmit(FlexcanState *s, int mbidx)\n+{\n+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];\n+ qemu_can_frame frame = {\n+ .flags = 0,\n+ };\n+ uint32_t *frame_data = (uint32_t *)&frame.data;\n+ uint32_t timestamp = flexcan_get_timestamp(s, true);\n+\n+ if ((s->regs.ctrl & FLEXCAN_CTRL_LOM) ||\n+ (s->regs.mcr & FLEXCAN_MCR_NOT_RDY)) {\n+ /* no transmiting in listen-only, freeze or low-power mode */\n+ return;\n+ }\n+\n+ if (mb->can_ctrl & FLEXCAN_MB_CNT_IDE) {\n+ /* 29b ID stored in bits [0, 29) */\n+ uint32_t id = mb->can_id & 0x1FFFFFFF;\n+ frame.can_id = id | QEMU_CAN_EFF_FLAG;\n+ } else {\n+ /* 11b ID stored in bits [18, 29) */\n+ uint32_t id = (mb->can_id & (0x7FF << 18)) >> 18;\n+ frame.can_id = id;\n+ }\n+\n+ frame.can_dlc = (mb->can_ctrl & (0xF << 16)) >> 16;\n+\n+ for (int i = 0; i < 2; i++) {\n+ stl_be_p(&frame_data[i], mb->data[i]);\n+ }\n+\n+ if (!(s->regs.mcr & FLEXCAN_MCR_SRX_DIS)) {\n+ /* self-reception */\n+ flexcan_mb_rx(s, &frame);\n+ }\n+ if (!(s->regs.ctrl & FLEXCAN_CTRL_LPB)) {\n+ /* send to bus if not in loopback mode */\n+ if (s->canbus) {\n+ can_bus_client_send(&s->bus_client, &frame, 1);\n+ } else {\n+ /* todo: raise error (no ack) */\n+ }\n+ }\n+\n+ mb->can_ctrl &= ~(FLEXCAN_MB_CODE_MASK | FLEXCAN_MB_CNT_TIMESTAMP_MASK);\n+ mb->can_ctrl |= FLEXCAN_MB_CODE_TX_INACTIVE |\n+ FLEXCAN_MB_CNT_TIMESTAMP(timestamp);\n+\n+ /* todo: compute the CRC */\n+ s->regs.crcr = FLEXCAN_CRCR_TXCRC(0) | FLEXCAN_CRCR_MBCRC(mbidx);\n+\n+ flexcan_irq_iflag_set(s, mbidx);\n+}\n+\n+static void flexcan_mb_write(FlexcanState *s, int mbid)\n+{\n+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];\n+\n+ bool is_mailbox = (mb <= flexcan_get_last_enabled_mailbox(s)) &&\n+ (mb >= flexcan_get_first_message_buffer(s));\n+\n+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_WRITE)) {\n+ char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };\n+ const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);\n+ trace_flexcan_mb_write(DEVICE(s)->canonical_path, mbid, code_str,\n+ is_mailbox, mb->can_ctrl, mb->can_id);\n+ }\n+\n+ if (!is_mailbox) {\n+ /**\n+ * Disabled mailbox or mailbox in region of queue filters\n+ * was updated. Either way there is nothing to do.\n+ */\n+ return;\n+ }\n+\n+ /* any write to message buffer clears the not_serviced flag */\n+ mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;\n+\n+ /**\n+ * todo: search for active tx mbs on transition from freeze/disable mode\n+ */\n+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {\n+ case FLEXCAN_MB_CODE_TX_INACTIVE:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_INACTIVE:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_EMPTY:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_FULL:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_RANSWER:\n+ break;\n+\n+ case FLEXCAN_MB_CODE_TX_DATA:\n+ flexcan_transmit(s, mbid);\n+ break;\n+ case FLEXCAN_MB_CODE_TX_ABORT:\n+ /*\n+ * as transmission is instant, it can never be aborted\n+ * we need to set CODE in C/S back to the previous code\n+ */\n+ mb->can_ctrl &= ~FLEXCAN_MB_CODE(1);\n+ break;\n+ case FLEXCAN_MB_CODE_TX_TANSWER:\n+ break;\n+ default:\n+ /* prevent setting the busy bit */\n+ mb->can_ctrl &= ~FLEXCAN_MB_CODE_RX_BUSY_BIT;\n+ break;\n+ }\n+\n+}\n+\n+/* ========== RX ========== */\n+static void flexcan_mb_move_in(FlexcanState *s, const qemu_can_frame *frame,\n+ FlexcanRegsMessageBuffer *target_mb)\n+{\n+ uint32_t frame_len = frame->can_dlc;\n+ uint32_t *frame_data = (uint32_t *)&frame->data;\n+ int timestamp = flexcan_get_timestamp(s, true);\n+ uint32_t new_code = 0;\n+\n+ memset(target_mb, 0, sizeof(FlexcanRegsMessageBuffer));\n+\n+ if (frame_len > 8) {\n+ frame_len = 8;\n+ }\n+ for (int i = 0; i < 2; i++) {\n+ target_mb->data[i] = ldl_be_p(&frame_data[i]);\n+ }\n+\n+ switch (target_mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {\n+ case FLEXCAN_MB_CODE_RX_FULL:\n+ case FLEXCAN_MB_CODE_RX_OVERRUN:\n+ if (target_mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV) {\n+ new_code = FLEXCAN_MB_CODE_RX_OVERRUN;\n+ } else {\n+ new_code = FLEXCAN_MB_CODE_RX_FULL;\n+ }\n+ break;\n+ case FLEXCAN_MB_CODE_RX_RANSWER:\n+ assert(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS);\n+ new_code = FLEXCAN_MB_CODE_TX_TANSWER;\n+ break;\n+ default:\n+ new_code = FLEXCAN_MB_CODE_RX_FULL;\n+ }\n+\n+ target_mb->can_ctrl = new_code\n+ | FLEXCAN_MB_CNT_TIMESTAMP(timestamp)\n+ | FLEXCAN_MB_CNT_LENGTH(frame_len)\n+ | FLEXCAN_MB_CNT_NOT_SRV\n+ | FLEXCAN_MB_CNT_SRR; /* always set for received frames */\n+ if (frame->can_id & QEMU_CAN_RTR_FLAG) {\n+ target_mb->can_ctrl |= FLEXCAN_MB_CNT_RTR;\n+ }\n+\n+ if (frame->can_id & QEMU_CAN_EFF_FLAG) {\n+ target_mb->can_ctrl |= FLEXCAN_MB_CNT_IDE;\n+ target_mb->can_id |= frame->can_id & QEMU_CAN_EFF_MASK;\n+ } else {\n+ target_mb->can_id |= (frame->can_id & QEMU_CAN_SFF_MASK) << 18;\n+ }\n+}\n+static void flexcan_mb_lock(FlexcanState *s, int mbidx)\n+{\n+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbidx];\n+ if ((mb > flexcan_get_last_enabled_mailbox(s)) ||\n+ (mb < flexcan_get_first_message_buffer(s))) {\n+ return;\n+ }\n+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {\n+ case FLEXCAN_MB_CODE_RX_FULL:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_OVERRUN:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_RANSWER:\n+ /* continue */\n+ trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 1);\n+ break;\n+ default:\n+ trace_flexcan_mb_lock(DEVICE(s)->canonical_path, mbidx, 0);\n+ return;\n+ }\n+\n+ s->locked_mbidx = mbidx;\n+}\n+\n+static void flexcan_mb_unlock(FlexcanState *s)\n+{\n+ int locked_mbidx = s->locked_mbidx;\n+ bool has_pending_frame = locked_mbidx == s->smb_target_mbidx;\n+\n+ if (s->locked_mbidx == FLEXCAN_NO_MB_LOCKED) {\n+ return;\n+ }\n+\n+ assert(locked_mbidx >= 0 && locked_mbidx < FLEXCAN_MAILBOX_COUNT);\n+ FlexcanRegsMessageBuffer *locked_mb = &s->regs.mbs[locked_mbidx];\n+ s->locked_mbidx = FLEXCAN_NO_MB_LOCKED;\n+\n+ if (locked_mb >= flexcan_get_first_message_buffer(s) &&\n+ locked_mb <= flexcan_get_last_enabled_mailbox(s)\n+ ) {\n+ /* mark the message buffer as serviced */\n+ locked_mb->can_ctrl &= ~FLEXCAN_MB_CNT_NOT_SRV;\n+ }\n+\n+ /* try move in from SMB */\n+ trace_flexcan_mb_unlock(DEVICE(s)->canonical_path, locked_mbidx,\n+ has_pending_frame ? \" PENDING FRAME IN SMB\" : \"\");\n+\n+ /* todo: in low-power modes, this should be postponed until exit */\n+ if (has_pending_frame) {\n+ FlexcanRegsMessageBuffer *target_mb = &s->regs.mbs[locked_mbidx];\n+ memcpy(target_mb, &s->regs.rx_smb0, sizeof(FlexcanRegsMessageBuffer));\n+\n+ memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));\n+ s->locked_mbidx = FLEXCAN_SMB_EMPTY;\n+\n+ flexcan_irq_iflag_set(s, locked_mbidx);\n+ }\n+}\n+\n+static bool flexcan_can_receive(CanBusClientState *client)\n+{\n+ FlexcanState *s = container_of(client, FlexcanState, bus_client);\n+ return !(s->regs.mcr & FLEXCAN_MCR_NOT_RDY);\n+}\n+\n+/* --------- RX FIFO ---------- */\n+\n+/**\n+ * flexcan_fifo_pop() - Pop message from FIFO and update IRQs\n+ * @s: FlexCAN device pointer\n+ *\n+ * Does not require the queue to be non-empty.\n+ */\n+static void flexcan_fifo_pop(FlexcanState *s)\n+{\n+ if (s->regs.fifo.mb_back.can_ctrl != 0) {\n+ /* move queue elements forward */\n+ memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0],\n+ sizeof(s->regs.fifo.mbs_queue));\n+\n+ /* clear the first-in slot */\n+ memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0,\n+ sizeof(FlexcanRegsMessageBuffer));\n+\n+ trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1,\n+ s->regs.fifo.mb_back.can_ctrl != 0);\n+ } else {\n+ trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0);\n+ }\n+\n+ if (s->regs.fifo.mb_back.can_ctrl != 0) {\n+ flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);\n+ } else {\n+ flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE);\n+ }\n+}\n+\n+/**\n+ * flexcan_fifo_find_free_slot() - Find the first free slot in the FIFO\n+ * @s: FlexCAN device pointer\n+ *\n+ * Return: Pointer to the first free slot in the FIFO,\n+ * or NULL if the queue is full.\n+ */\n+static FlexcanRegsMessageBuffer *flexcan_fifo_find_free_slot(FlexcanState *s)\n+{\n+ for (int i = 0; i < FLEXCAN_FIFO_DEPTH; i++) {\n+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[i];\n+ if (mb->can_ctrl == 0) {\n+ return mb;\n+ }\n+ }\n+ return NULL;\n+}\n+\n+/**\n+ * flexcan_fifo_push() - Update FIFO IRQs after frame move-in\n+ * @s: FlexCAN device pointer\n+ * @slot: Target FIFO slot\n+ *\n+ * The usage is as follows:\n+ * 1. Get free slot pointer using flexcan_fifo_find_free_slot()\n+ * 2. Move the frame in if not NULL\n+ * 3. Call flexcan_fifo_push() regardless of the NULL pointer\n+ */\n+static void flexcan_fifo_push(FlexcanState *s, FlexcanRegsMessageBuffer *slot)\n+{\n+ if (slot) {\n+ int n_occupied = slot - s->regs.mbs;\n+ if (n_occupied == 4) { /* 4 means the 5th slot was filled in */\n+ /*\n+ * fifo occupancy increased from 4 to 5,\n+ * raising FIFO_WARN interrupt\n+ */\n+ flexcan_irq_iflag_set(s, I_FIFO_WARN);\n+ }\n+ flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE);\n+\n+ trace_flexcan_fifo_push(DEVICE(s)->canonical_path, n_occupied);\n+ } else {\n+ flexcan_irq_iflag_set(s, I_FIFO_OVERFLOW);\n+\n+ trace_flexcan_fifo_push(DEVICE(s)->canonical_path, -1);\n+ }\n+}\n+\n+static enum FlexcanRx flexcan_fifo_rx(FlexcanState *s,\n+ const qemu_can_frame *buf)\n+{\n+ /* todo: filtering. return FLEXCAN_FIFO_RX_RETRY if filtered out */\n+ if ((s->regs.mcr & FLEXCAN_MCR_IDAM_MASK) == FLEXCAN_MCR_IDAM_D) {\n+ /* all frames rejected */\n+ return FLEXCAN_RX_SEARCH_RETRY;\n+ } else {\n+ /* push message to queue if not full */\n+ FlexcanRegsMessageBuffer *slot = flexcan_fifo_find_free_slot(s);\n+ if (slot) {\n+ flexcan_mb_move_in(s, buf, slot);\n+ }\n+ flexcan_fifo_push(s, slot);\n+\n+ return slot ? FLEXCAN_RX_SEARCH_ACCEPT : FLEXCAN_RX_SEARCH_DROPPED;\n+ }\n+}\n+\n+/* --------- RX message buffer ---------- */\n+\n+/**\n+ * flexcan_mb_rx_check_mb() - Check if a mb matches a received frame\n+ * @s: FlexCAN device pointer\n+ * @buf: Frame to be received from CAN subsystem\n+ * @mbid: Target mailbox index. The mailbox must be a valid message buffer.\n+ *\n+ * Return: FLEXCAN_CHECK_MB_NIL if the message buffer does not match.\n+ * FLEXCAN_CHECK_MB_MATCH if the message buffer matches the received\n+ * frame and is free-to-receive,\n+ * FLEXCAN_CHECK_MB_MATCH_LOCKED if the message buffer matches,\n+ * but is locked,\n+ * FLEXCAN_CHECK_MB_MATCH_NON_FREE if the message buffer matches,\n+ * but is not free-to-receive\n+ * for some other reason.\n+ */\n+static enum FlexcanCheck flexcan_mb_rx_check_mb(FlexcanState *s,\n+ const qemu_can_frame *buf,\n+ int mbid)\n+{\n+ FlexcanRegsMessageBuffer *mb = &s->regs.mbs[mbid];\n+ const bool is_rtr = !!(buf->can_id & QEMU_CAN_RTR_FLAG);\n+ const bool is_serviced = !(mb->can_ctrl & FLEXCAN_MB_CNT_NOT_SRV);\n+ const bool is_locked = s->locked_mbidx == mbid;\n+\n+ bool is_free_to_receive = false;\n+ bool is_matched = false;\n+\n+ switch (mb->can_ctrl & FLEXCAN_MB_CODE_MASK) {\n+ case FLEXCAN_MB_CODE_RX_RANSWER:\n+ if (is_rtr && !(s->regs.ctrl2 & FLEXCAN_CTRL2_RRS)) {\n+ /* todo: do the actual matching/filtering and RTR answer */\n+ is_matched = true;\n+ }\n+ break;\n+ case FLEXCAN_MB_CODE_RX_FULL:\n+ QEMU_FALLTHROUGH;\n+ case FLEXCAN_MB_CODE_RX_OVERRUN:\n+ is_free_to_receive = is_serviced;\n+ /* todo: do the actual matching/filtering */\n+ is_matched = true;\n+ break;\n+ case FLEXCAN_MB_CODE_RX_EMPTY:\n+ is_free_to_receive = true;\n+ /* todo: do the actual matching/filtering */\n+ is_matched = true;\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ if (trace_event_get_state_backends(TRACE_FLEXCAN_MB_RX_CHECK_MB)) {\n+ char code_str_buf[FLEXCAN_DBG_BUF_LEN] = { 0 };\n+ const char *code_str = flexcan_dbg_mb_code(mb->can_ctrl, code_str_buf);\n+ trace_flexcan_mb_rx_check_mb(DEVICE(s)->canonical_path, mbid, code_str,\n+ is_matched, is_free_to_receive,\n+ is_serviced, is_locked);\n+ }\n+\n+ if (!is_matched) {\n+ return FLEXCAN_CHECK_MB_NIL;\n+ }\n+\n+ if (is_locked) {\n+ return FLEXCAN_CHECK_MB_MATCH_LOCKED;\n+ }\n+\n+ if (is_free_to_receive) {\n+ return FLEXCAN_CHECK_MB_MATCH;\n+ }\n+\n+ return FLEXCAN_CHECK_MB_MATCH_NON_FREE;\n+}\n+\n+static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf)\n+{\n+ int last_not_free_to_receive_mbid = -1;\n+ bool last_not_free_to_receive_locked = false;\n+\n+ FlexcanRegsMessageBuffer *first_mb = flexcan_get_first_message_buffer(s);\n+ FlexcanRegsMessageBuffer *last_mb = flexcan_get_last_enabled_mailbox(s);\n+\n+ for (FlexcanRegsMessageBuffer *mb = first_mb;\n+ mb <= last_mb; mb++) {\n+ int mbid = mb - s->regs.mbs;\n+ enum FlexcanCheck r = flexcan_mb_rx_check_mb(s, buf, mbid);\n+ if (r == FLEXCAN_CHECK_MB_MATCH) {\n+ flexcan_mb_move_in(s, buf, mb);\n+ flexcan_irq_iflag_set(s, mbid);\n+ return FLEXCAN_RX_SEARCH_ACCEPT;\n+ }\n+\n+ if (r == FLEXCAN_CHECK_MB_MATCH_NON_FREE) {\n+ last_not_free_to_receive_mbid = mbid;\n+ last_not_free_to_receive_locked = false;\n+ } else if (r == FLEXCAN_CHECK_MB_MATCH_LOCKED) {\n+ /*\n+ * message buffer is locked,\n+ * we can move in the message after it's unlocked\n+ */\n+ last_not_free_to_receive_mbid = mbid;\n+ last_not_free_to_receive_locked = true;\n+ }\n+ }\n+\n+ if (last_not_free_to_receive_mbid >= -1) {\n+ if (last_not_free_to_receive_locked) {\n+ /*\n+ * copy to temporary mailbox (SMB)\n+ * it will be moved in when the mailbox is unlocked\n+ */\n+ s->regs.rx_smb0.can_ctrl =\n+ s->regs.mbs[last_not_free_to_receive_mbid].can_id;\n+ flexcan_mb_move_in(s, buf, &s->regs.rx_smb0);\n+ s->smb_target_mbidx = last_not_free_to_receive_mbid;\n+ return FLEXCAN_RX_SEARCH_ACCEPT;\n+ }\n+\n+ if (s->regs.mcr & FLEXCAN_MCR_IRMQ) {\n+ flexcan_mb_move_in(s, buf,\n+ &s->regs.mbs[last_not_free_to_receive_mbid]);\n+ flexcan_irq_iflag_set(s, last_not_free_to_receive_mbid);\n+ return FLEXCAN_RX_SEARCH_ACCEPT;\n+ }\n+ }\n+\n+ return FLEXCAN_RX_SEARCH_RETRY;\n+}\n+\n+static ssize_t flexcan_receive(CanBusClientState *client,\n+ const qemu_can_frame *frames, size_t frames_cnt)\n+{\n+ FlexcanState *s = container_of(client, FlexcanState, bus_client);\n+ trace_flexcan_receive(DEVICE(s)->canonical_path, frames_cnt);\n+\n+ if (frames_cnt == 0) {\n+ return 0;\n+ }\n+\n+ /* clear the SMB, as it would be overriden in hardware */\n+ memset(&s->regs.rx_smb0, 0, sizeof(FlexcanRegsMessageBuffer));\n+ s->smb_target_mbidx = FLEXCAN_SMB_EMPTY;\n+\n+ for (size_t i = 0; i < frames_cnt; i++) {\n+ int r;\n+ const qemu_can_frame *frame = &frames[i];\n+ if (frame->can_id & QEMU_CAN_ERR_FLAG) {\n+ /* todo: error frame handling */\n+ continue;\n+ }\n+ if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {\n+ /* CAN FD supported only in later FlexCAN version */\n+ continue;\n+ }\n+\n+ /* todo: this order logic is not complete and needs further work */\n+ if (s->regs.mcr & FLEXCAN_MCR_FEN &&\n+ s->regs.ctrl2 & FLEXCAN_CTRL2_MRP) {\n+ r = flexcan_mb_rx(s, frame);\n+ if (r == FLEXCAN_RX_SEARCH_RETRY) {\n+ flexcan_fifo_rx(s, frame);\n+ }\n+ } else if (s->regs.mcr & FLEXCAN_MCR_FEN) {\n+ r = flexcan_fifo_rx(s, frame);\n+ if (r == FLEXCAN_RX_SEARCH_RETRY) {\n+ flexcan_mb_rx(s, frame);\n+ }\n+ } else {\n+ flexcan_mb_rx(s, frame);\n+ }\n+ }\n+\n+ flexcan_irq_update(s);\n+ return 1;\n+}\n+\n+/* ========== I/O handling ========== */\n+static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val,\n+ unsigned size)\n+{\n+ FlexcanState *s = opaque;\n+ uint32_t write_mask = ((const uint32_t *)\n+ &flexcan_regs_write_mask)[addr / 4];\n+ uint32_t old_value = s->regs_raw[addr / 4];\n+\n+ /*\n+ * 0 for bits that can \"only be written in Freeze mode as it is blocked\n+ * by hardware in other modes\"\n+ */\n+ const uint32_t freeze_mask_mcr = 0xDF54CC80;\n+ const uint32_t freeze_mask_ctrl1 = 0x0000E740;\n+\n+ flexcan_trace_mem_op(s, addr, val, size, true);\n+ switch (addr) {\n+ case offsetof(FlexcanRegs, mcr):\n+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {\n+ write_mask &= freeze_mask_mcr;\n+ }\n+ s->regs.mcr = (val & write_mask) | (old_value & ~write_mask);\n+ flexcan_set_mcr(s, old_value);\n+ break;\n+ case offsetof(FlexcanRegs, ctrl):\n+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {\n+ write_mask &= freeze_mask_ctrl1;\n+ }\n+ s->regs.ctrl = (val & write_mask) | (old_value & ~write_mask);\n+ break;\n+ case offsetof(FlexcanRegs, iflag1):\n+ s->regs.iflag1 &= ~val;\n+ if ((s->regs.mcr & FLEXCAN_MCR_FEN) &&\n+ (val & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) {\n+ flexcan_fifo_pop(s);\n+ }\n+ break;\n+ case offsetof(FlexcanRegs, iflag2):\n+ s->regs.iflag2 &= ~val;\n+ break;\n+ case offsetof(FlexcanRegs, ctrl2):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, ecr):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, rxmgmask):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, rx14mask):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, rx15mask):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, rxfgmask):\n+ QEMU_FALLTHROUGH;\n+ case offsetof(FlexcanRegs, rximr[0]) ... offsetof(FlexcanRegs, rximr[63]):\n+ /* these registers can only be written in freeze mode */\n+ if (!(s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {\n+ break;\n+ }\n+ QEMU_FALLTHROUGH;\n+ default:\n+ s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask);\n+\n+ if (addr >= offsetof(FlexcanRegs, mb) &&\n+ addr < offsetof(FlexcanRegs, _reserved4)) {\n+ /* access to mailbox */\n+ int mbid = (addr - offsetof(FlexcanRegs, mb)) /\n+ sizeof(FlexcanRegsMessageBuffer);\n+\n+ if (s->locked_mbidx == mbid) {\n+ flexcan_mb_unlock(s);\n+ }\n+\n+ /* check for invalid writes into FIFO region */\n+ if (s->regs.mcr & FLEXCAN_MCR_FEN && mbid < FLEXCAN_FIFO_DEPTH) {\n+ qemu_log_mask(LOG_GUEST_ERROR,\n+ \"%s: Invalid write to Rx-FIFO structure\",\n+ DEVICE(s)->canonical_path);\n+ return;\n+ }\n+\n+ /* run mailbox processing function on write to control word */\n+ if ((addr & 0xF) == 0) {\n+ flexcan_mb_write(s, mbid);\n+ }\n+ }\n+ break;\n+ }\n+\n+ flexcan_irq_update(s);\n+}\n+\n+static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size)\n+{\n+ FlexcanState *s = opqaue;\n+ uint32_t rv = s->regs_raw[addr >> 2];\n+\n+ if (addr >= offsetof(FlexcanRegs, mb) &&\n+ addr < offsetof(FlexcanRegs, _reserved4)) {\n+ /* reading from mailbox */\n+ hwaddr offset = addr - offsetof(FlexcanRegs, mb);\n+ int mbid = offset / sizeof(FlexcanRegsMessageBuffer);\n+\n+ if (addr % 16 == 0 && s->locked_mbidx != mbid) {\n+ /* reading control word locks the mailbox */\n+ flexcan_mb_unlock(s);\n+ flexcan_mb_lock(s, mbid);\n+ flexcan_irq_update(s);\n+ rv = s->regs.mbs[mbid].can_ctrl & ~FLEXCAN_MB_CNT_NOT_SRV;\n+ }\n+ } else if (addr == offsetof(FlexcanRegs, timer)) {\n+ flexcan_mb_unlock(s);\n+ flexcan_irq_update(s);\n+ rv = flexcan_get_timestamp(s, false);\n+ }\n+\n+ flexcan_trace_mem_op(s, addr, rv, size, false);\n+ return rv;\n+}\n+\n+static bool flexcan_mem_accepts(void *opaque, hwaddr addr,\n+ unsigned size, bool is_write,\n+ MemTxAttrs attrs)\n+{\n+ FlexcanState *s = opaque;\n+\n+ if ((s->regs.ctrl2 & FLEXCAN_CTRL2_WRMFRZ) &&\n+ (s->regs.mcr & FLEXCAN_MCR_FRZ_ACK)) {\n+ /* unrestricted access to FlexCAN memory in freeze mode */\n+ return true;\n+ } else if (attrs.user && (s->regs.mcr & FLEXCAN_MCR_SUPV)) {\n+ qemu_log_mask(LOG_GUEST_ERROR,\n+ \"%s: Invalid user-mode access to restricted register\",\n+ DEVICE(s)->canonical_path);\n+ return false;\n+ } else if (attrs.user && is_write && addr < 4) {\n+ qemu_log_mask(LOG_GUEST_ERROR,\n+ \"%s: Invalid user-mode access to MCR\",\n+ DEVICE(s)->canonical_path);\n+ return false;\n+ }\n+\n+ return true;\n+}\n+\n+static const struct MemoryRegionOps flexcan_ops = {\n+ .read = flexcan_mem_read,\n+ .write = flexcan_mem_write,\n+ .endianness = DEVICE_LITTLE_ENDIAN,\n+ .valid = {\n+ .min_access_size = 1,\n+ .max_access_size = 4,\n+ .unaligned = true,\n+ .accepts = flexcan_mem_accepts\n+ },\n+ .impl = {\n+ .min_access_size = 4,\n+ .max_access_size = 4,\n+ .unaligned = false\n+ },\n+};\n+\n+static CanBusClientInfo flexcan_bus_client_info = {\n+ .can_receive = flexcan_can_receive,\n+ .receive = flexcan_receive,\n+};\n+\n+static int flexcan_connect_to_bus(FlexcanState *s, CanBusState *bus)\n+{\n+ s->bus_client.info = &flexcan_bus_client_info;\n+\n+ if (can_bus_insert_client(bus, &s->bus_client) < 0) {\n+ return -1;\n+ }\n+ return 0;\n+}\n+\n+static void flexcan_init(Object *obj)\n+{\n+ FlexcanState *s = CAN_FLEXCAN(obj);\n+\n+ memory_region_init_io(\n+ &s->iomem, obj, &flexcan_ops, s, TYPE_CAN_FLEXCAN,\n+ offsetof(FlexcanRegs, _reserved6)\n+ );\n+}\n+\n+static void flexcan_realize(DeviceState *dev, Error **errp)\n+{\n+ FlexcanState *s = CAN_FLEXCAN(dev);\n+\n+ if (s->canbus) {\n+ if (flexcan_connect_to_bus(s, s->canbus) < 0) {\n+ error_setg(errp, \"%s: flexcan_connect_to_bus failed\",\n+ dev->canonical_path);\n+ return;\n+ }\n+ }\n+\n+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);\n+ sysbus_init_irq(SYS_BUS_DEVICE(SYS_BUS_DEVICE(dev)), &s->irq);\n+}\n+\n+static const VMStateDescription vmstate_can = {\n+ .name = TYPE_CAN_FLEXCAN,\n+ .version_id = 1,\n+ .minimum_version_id = 1,\n+ .fields = (const VMStateField[]) {\n+ VMSTATE_INT64(timer_start, FlexcanState),\n+ VMSTATE_UINT32_ARRAY(regs_raw, FlexcanState, sizeof(FlexcanRegs) / 4),\n+ VMSTATE_INT32(locked_mbidx, FlexcanState),\n+ VMSTATE_INT32(smb_target_mbidx, FlexcanState),\n+ VMSTATE_END_OF_LIST(),\n+ },\n+};\n+\n+static const Property flexcan_properties[] = {\n+ DEFINE_PROP_LINK(\"canbus\", FlexcanState, canbus, TYPE_CAN_BUS,\n+ CanBusState *),\n+};\n+\n+static void flexcan_class_init(ObjectClass *klass, const void *data)\n+{\n+ DeviceClass *dc = DEVICE_CLASS(klass);\n+ ResettableClass *rc = RESETTABLE_CLASS(klass);\n+\n+ rc->phases.enter = flexcan_reset_enter;\n+ rc->phases.hold = flexcan_reset_hold;\n+ dc->realize = flexcan_realize;\n+ device_class_set_props(dc, flexcan_properties);\n+ dc->vmsd = &vmstate_can;\n+ dc->desc = \"i.MX FLEXCAN Controller\";\n+}\n+\n+static const TypeInfo flexcan_info = {\n+ .name = TYPE_CAN_FLEXCAN,\n+ .parent = TYPE_SYS_BUS_DEVICE,\n+ .instance_size = sizeof(FlexcanState),\n+ .class_init = flexcan_class_init,\n+ .instance_init = flexcan_init,\n+};\n+\n+static void can_register_types(void)\n+{\n+ type_register_static(&flexcan_info);\n+}\n+type_init(can_register_types)\ndiff --git a/hw/net/can/flexcan_regs.h b/hw/net/can/flexcan_regs.h\nnew file mode 100644\nindex 0000000000..8dcf1047c6\n--- /dev/null\n+++ b/hw/net/can/flexcan_regs.h\n@@ -0,0 +1,193 @@\n+/*\n+ * Field bitmasks and register structs definitions for FlexCAN\n+ *\n+ * This implementation is based on the following datasheet:\n+ * i.MX 6Dual/6Quad Applications Processor Reference Manual\n+ * Document Number: IMX6DQRM, Rev. 6, 05/2020\n+ *\n+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+#include \"qemu/bitops.h\"\n+\n+#ifndef HW_CAN_FLEXCAN_REGS_H\n+#define HW_CAN_FLEXCAN_REGS_H\n+\n+#define FLEXCAN_GENMASK(h, l) (((~(uint32_t)0) >> (31 - (h) + (l))) << (l))\n+\n+/**\n+ * These macros ware originally written for the Linux kernel\n+ * by Marc Kleine-Budde.\n+ */\n+\n+/* FLEXCAN module configuration register (CANMCR) bits */\n+#define FLEXCAN_MCR_MDIS BIT(31)\n+#define FLEXCAN_MCR_FRZ BIT(30)\n+#define FLEXCAN_MCR_FEN BIT(29)\n+#define FLEXCAN_MCR_HALT BIT(28)\n+#define FLEXCAN_MCR_NOT_RDY BIT(27)\n+#define FLEXCAN_MCR_WAK_MSK BIT(26)\n+#define FLEXCAN_MCR_SOFTRST BIT(25)\n+#define FLEXCAN_MCR_FRZ_ACK BIT(24)\n+#define FLEXCAN_MCR_SUPV BIT(23)\n+#define FLEXCAN_MCR_SLF_WAK BIT(22)\n+#define FLEXCAN_MCR_WRN_EN BIT(21)\n+#define FLEXCAN_MCR_LPM_ACK BIT(20)\n+#define FLEXCAN_MCR_WAK_SRC BIT(19)\n+#define FLEXCAN_MCR_DOZE BIT(18)\n+#define FLEXCAN_MCR_SRX_DIS BIT(17)\n+#define FLEXCAN_MCR_IRMQ BIT(16)\n+#define FLEXCAN_MCR_LPRIO_EN BIT(13)\n+#define FLEXCAN_MCR_AEN BIT(12)\n+#define FLEXCAN_MCR_FDEN BIT(11)\n+#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)\n+#define FLEXCAN_MCR_IDAM_A (0x0 << 8)\n+#define FLEXCAN_MCR_IDAM_B (0x1 << 8)\n+#define FLEXCAN_MCR_IDAM_C (0x2 << 8)\n+#define FLEXCAN_MCR_IDAM_D (0x3 << 8)\n+#define FLEXCAN_MCR_IDAM_MASK (0x3 << 8)\n+\n+/* FLEXCAN control register (CANCTRL) bits */\n+#define FLEXCAN_CTRL_PRESDIV(x) (((x) & 0xFF) << 24)\n+#define FLEXCAN_CTRL_PRESDIV_MASK FLEXCAN_CTRL_PRESDIV(UINT32_MAX)\n+#define FLEXCAN_CTRL_RJW(x) (((x) & 0x03) << 22)\n+#define FLEXCAN_CTRL_RJW_MASK FLEXCAN_CTRL_RJW(UINT32_MAX)\n+#define FLEXCAN_CTRL_PSEG1(x) (((x) & 0x07) << 19)\n+#define FLEXCAN_CTRL_PSEG1_MASK FLEXCAN_CTRL_PSEG1(UINT32_MAX)\n+#define FLEXCAN_CTRL_PSEG2(x) (((x) & 0x07) << 16)\n+#define FLEXCAN_CTRL_PSEG2_MASK FLEXCAN_CTRL_PSEG2(UINT32_MAX)\n+#define FLEXCAN_CTRL_BOFF_MSK BIT(15)\n+#define FLEXCAN_CTRL_ERR_MSK BIT(14)\n+#define FLEXCAN_CTRL_CLK_SRC BIT(13)\n+#define FLEXCAN_CTRL_LPB BIT(12)\n+#define FLEXCAN_CTRL_TWRN_MSK BIT(11)\n+#define FLEXCAN_CTRL_RWRN_MSK BIT(10)\n+#define FLEXCAN_CTRL_SMP BIT(7)\n+#define FLEXCAN_CTRL_BOFF_REC BIT(6)\n+#define FLEXCAN_CTRL_TSYN BIT(5)\n+#define FLEXCAN_CTRL_LBUF BIT(4)\n+#define FLEXCAN_CTRL_LOM BIT(3)\n+#define FLEXCAN_CTRL_PROPSEG(x) ((x) & 0x07)\n+#define FLEXCAN_CTRL_PROPSEG_MASK FLEXCAN_CTRL_PROPSEG(UINT32_MAX)\n+#define FLEXCAN_CTRL_ERR_BUS (FLEXCAN_CTRL_ERR_MSK)\n+#define FLEXCAN_CTRL_ERR_STATE \\\n+ (FLEXCAN_CTRL_TWRN_MSK | FLEXCAN_CTRL_RWRN_MSK | \\\n+ FLEXCAN_CTRL_BOFF_MSK)\n+#define FLEXCAN_CTRL_ERR_ALL \\\n+ (FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)\n+\n+/* FLEXCAN control register 2 (CTRL2) bits */\n+#define FLEXCAN_CTRL2_ECRWRE BIT(29)\n+#define FLEXCAN_CTRL2_WRMFRZ BIT(28)\n+#define FLEXCAN_CTRL2_RFFN(x) (((x) & 0x0f) << 24)\n+#define FLEXCAN_CTRL2_TASD(x) (((x) & 0x1f) << 19)\n+#define FLEXCAN_CTRL2_MRP BIT(18)\n+#define FLEXCAN_CTRL2_RRS BIT(17)\n+#define FLEXCAN_CTRL2_EACEN BIT(16)\n+#define FLEXCAN_CTRL2_ISOCANFDEN BIT(12)\n+\n+/* FLEXCAN memory error control register (MECR) bits */\n+#define FLEXCAN_MECR_ECRWRDIS BIT(31)\n+#define FLEXCAN_MECR_HANCEI_MSK BIT(19)\n+#define FLEXCAN_MECR_FANCEI_MSK BIT(18)\n+#define FLEXCAN_MECR_CEI_MSK BIT(16)\n+#define FLEXCAN_MECR_HAERRIE BIT(15)\n+#define FLEXCAN_MECR_FAERRIE BIT(14)\n+#define FLEXCAN_MECR_EXTERRIE BIT(13)\n+#define FLEXCAN_MECR_RERRDIS BIT(9)\n+#define FLEXCAN_MECR_ECCDIS BIT(8)\n+#define FLEXCAN_MECR_NCEFAFRZ BIT(7)\n+\n+/* FLEXCAN error and status register (ESR) bits */\n+#define FLEXCAN_ESR_SYNCH BIT(18)\n+#define FLEXCAN_ESR_TWRN_INT BIT(17)\n+#define FLEXCAN_ESR_RWRN_INT BIT(16)\n+#define FLEXCAN_ESR_BIT1_ERR BIT(15)\n+#define FLEXCAN_ESR_BIT0_ERR BIT(14)\n+#define FLEXCAN_ESR_ACK_ERR BIT(13)\n+#define FLEXCAN_ESR_CRC_ERR BIT(12)\n+#define FLEXCAN_ESR_FRM_ERR BIT(11)\n+#define FLEXCAN_ESR_STF_ERR BIT(10)\n+#define FLEXCAN_ESR_TX_WRN BIT(9)\n+#define FLEXCAN_ESR_RX_WRN BIT(8)\n+#define FLEXCAN_ESR_IDLE BIT(7)\n+#define FLEXCAN_ESR_BOFF_INT BIT(2)\n+#define FLEXCAN_ESR_ERR_INT BIT(1)\n+#define FLEXCAN_ESR_WAK_INT BIT(0)\n+\n+/* FLEXCAN Bit Timing register (CBT) bits */\n+#define FLEXCAN_CBT_BTF BIT(31)\n+#define FLEXCAN_CBT_EPRESDIV_MASK FLEXCAN_GENMASK(30, 21)\n+#define FLEXCAN_CBT_ERJW_MASK FLEXCAN_GENMASK(20, 16)\n+#define FLEXCAN_CBT_EPROPSEG_MASK FLEXCAN_GENMASK(15, 10)\n+#define FLEXCAN_CBT_EPSEG1_MASK FLEXCAN_GENMASK(9, 5)\n+#define FLEXCAN_CBT_EPSEG2_MASK FLEXCAN_GENMASK(4, 0)\n+\n+/* FLEXCAN FD control register (FDCTRL) bits */\n+#define FLEXCAN_FDCTRL_FDRATE BIT(31)\n+#define FLEXCAN_FDCTRL_MBDSR1 FLEXCAN_GENMASK(20, 19)\n+#define FLEXCAN_FDCTRL_MBDSR0 FLEXCAN_GENMASK(17, 16)\n+#define FLEXCAN_FDCTRL_MBDSR_8 0x0\n+#define FLEXCAN_FDCTRL_MBDSR_12 0x1\n+#define FLEXCAN_FDCTRL_MBDSR_32 0x2\n+#define FLEXCAN_FDCTRL_MBDSR_64 0x3\n+#define FLEXCAN_FDCTRL_TDCEN BIT(15)\n+#define FLEXCAN_FDCTRL_TDCFAIL BIT(14)\n+#define FLEXCAN_FDCTRL_TDCOFF FLEXCAN_GENMASK(12, 8)\n+#define FLEXCAN_FDCTRL_TDCVAL FLEXCAN_GENMASK(5, 0)\n+\n+/* FLEXCAN FD Bit Timing register (FDCBT) bits */\n+#define FLEXCAN_FDCBT_FPRESDIV_MASK FLEXCAN_GENMASK(29, 20)\n+#define FLEXCAN_FDCBT_FRJW_MASK FLEXCAN_GENMASK(18, 16)\n+#define FLEXCAN_FDCBT_FPROPSEG_MASK FLEXCAN_GENMASK(14, 10)\n+#define FLEXCAN_FDCBT_FPSEG1_MASK FLEXCAN_GENMASK(7, 5)\n+#define FLEXCAN_FDCBT_FPSEG2_MASK FLEXCAN_GENMASK(2, 0)\n+\n+/* FLEXCAN CRC Register (CRCR) bits */\n+#define FLEXCAN_CRCR_MBCRC_MASK FLEXCAN_GENMASK(22, 16)\n+#define FLEXCAN_CRCR_MBCRC(x) (((x) & FLEXCAN_CRCR_MBCRC_MASK) << 16)\n+#define FLEXCAN_CRCR_TXCRC_MASK FLEXCAN_GENMASK(14, 0)\n+#define FLEXCAN_CRCR_TXCRC(x) ((x) & FLEXCAN_CRCR_TXCRC_MASK)\n+\n+/* FLEXCAN interrupt flag register (IFLAG) bits */\n+/* Errata ERR005829 step7: Reserve first valid MB */\n+#define I_FIFO_OVERFLOW 7\n+#define I_FIFO_WARN 6\n+#define I_FIFO_AVAILABLE 5\n+\n+#define FLEXCAN_TX_MB_RESERVED_RX_FIFO 8\n+#define FLEXCAN_TX_MB_RESERVED_RX_MAILBOX 0\n+#define FLEXCAN_RX_MB_RX_MAILBOX_FIRST (FLEXCAN_TX_MB_RESERVED_RX_MAILBOX + 1)\n+#define FLEXCAN_IFLAG_MB(x) BIT_ULL(x)\n+#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(I_FIFO_OVERFLOW)\n+#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(I_FIFO_WARN)\n+#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(I_FIFO_AVAILABLE)\n+\n+/* FLEXCAN message buffers */\n+#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24)\n+#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)\n+#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)\n+#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)\n+#define FLEXCAN_MB_CODE_RX_OVERRUN (0x6 << 24)\n+#define FLEXCAN_MB_CODE_RX_RANSWER (0xa << 24)\n+\n+#define FLEXCAN_MB_CODE_TX_INACTIVE (0x8 << 24)\n+#define FLEXCAN_MB_CODE_TX_ABORT (0x9 << 24)\n+#define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)\n+#define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)\n+\n+#define FLEXCAN_MB_CODE(x) (((x) & 0xF) << 24)\n+#define FLEXCAN_MB_CODE_MASK FLEXCAN_MB_CODE(UINT32_MAX)\n+\n+#define FLEXCAN_MB_CNT_EDL BIT(31)\n+#define FLEXCAN_MB_CNT_BRS BIT(30)\n+#define FLEXCAN_MB_CNT_ESI BIT(29)\n+#define FLEXCAN_MB_CNT_SRR BIT(22)\n+#define FLEXCAN_MB_CNT_IDE BIT(21)\n+#define FLEXCAN_MB_CNT_RTR BIT(20)\n+#define FLEXCAN_MB_CNT_LENGTH(x) (((x) & 0xF) << 16)\n+#define FLEXCAN_MB_CNT_TIMESTAMP(x) ((x) & 0xFFFF)\n+#define FLEXCAN_MB_CNT_TIMESTAMP_MASK FLEXCAN_MB_CNT_TIMESTAMP(UINT32_MAX)\n+\n+#endif\ndiff --git a/hw/net/can/meson.build b/hw/net/can/meson.build\nindex 7382344628..401afde2e4 100644\n--- a/hw/net/can/meson.build\n+++ b/hw/net/can/meson.build\n@@ -6,3 +6,4 @@ system_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'))\n system_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c'))\n system_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c'))\n system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-canfd.c'))\n+system_ss.add(when: 'CONFIG_CAN_FLEXCAN', if_true: files('flexcan.c'))\ndiff --git a/hw/net/can/trace-events b/hw/net/can/trace-events\nindex de64ac1b31..7500f10d7a 100644\n--- a/hw/net/can/trace-events\n+++ b/hw/net/can/trace-events\n@@ -1,3 +1,21 @@\n+# flexcan.c\n+flexcan_irq_update(const char *inst, uint32_t mb_irqs1, uint32_t mb_irqs2, int setting) \"%s: irqs1 0x%08x irqs2 0x%08x request %i\"\n+flexcan_set_mcr(const char *inst, const char *enabled, const char *freeze, const char *fifo, const char *rdy, const char *sync) \"%s: %s %s %s %s %s\"\n+flexcan_mb_write(const char *inst, int mbidx, const char *code, int is_mailbox, uint32_t ctrl, uint32_t id) \"%s: mbidx %i code %s is_mailbox %i ctrl 0x%08x id 0x%08x\"\n+flexcan_mb_lock(const char *inst, int mbidx, int had_rx_code) \"%s: mbidx %i had_rx_code %i\"\n+flexcan_mb_unlock(const char *inst, int mbidx, const char *pending_frame) \"%s: mbidx %i%s\"\n+flexcan_fifo_pop(const char *inst, int non_empty_before, int non_empty_after) \"%s: non_empty before %i non_empty_after %i\"\n+flexcan_fifo_push(const char *inst, int n_occupied) \"%s: n_slots_occupied %i\"\n+flexcan_reset(const char *inst) \"%s: resetting\"\n+flexcan_mem_op(const char *inst, const char *op, uint32_t v, int offset, const char *reg_name, int size) \"%s: %s 0x%08x at offset %i register %s size %i\"\n+flexcan_get_timestamp(const char *inst, int64_t time_elapsed_ms, uint32_t bitrate, uint64_t cycles, uint32_t shift, uint32_t timestamp) \"%s: time_elapsed %\" PRIi64 \"ms bitrate %ub/s cycles %\" PRIu64 \" shift %u timestamp 0x%04x\"\n+flexcan_get_bitrate(const char *inst, uint32_t pe_freq, uint32_t prediv, uint32_t s_freq, uint32_t tseg1, uint32_t tseg2, uint32_t quata_per_bit, uint32_t bitrate) \"%s: pe_freq %uHz prescaler %u s_freq %uHz tseg1 %uq tseg2 %uq total %uq/b bitrate %ub/s\"\n+flexcan_timer_start(const char *inst, uint32_t bitrate, uint32_t value) \"%s: bitrate %ub/s value 0x%04x\"\n+flexcan_timer_stop(const char *inst, uint32_t bitrate, uint32_t value) \"%s: bitrate %ub/s value 0x%04x\"\n+flexcan_timer_overflow(const char *inst, int64_t current_time, int64_t timer_start, int64_t elapsed_ns) \"%s: current_time %\" PRIi64 \"timer_start %\" PRIi64 \"elapsed_ns %\" PRIi64\n+flexcan_mb_rx_check_mb(const char *inst, int mbidx, const char *code, int is_matched, int is_ftr, int is_serviced, int is_locked) \"%s: checking mb %i code %s is_matched %i is_free_to_receive %i is_serviced %i is_locked %i\"\n+flexcan_receive(const char *inst, size_t n_frames) \"%s: received %zu frames\"\n+\n # xlnx-zynqmp-can.c\n xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) \"ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x\"\n xlnx_can_reset(uint32_t val) \"Resetting controller with value = 0x%08x\"\ndiff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h\nnew file mode 100644\nindex 0000000000..18f4b29ca3\n--- /dev/null\n+++ b/include/hw/net/flexcan.h\n@@ -0,0 +1,139 @@\n+/*\n+ * QEMU model of the NXP FLEXCAN device.\n+ *\n+ * Copyright (c) 2025 Matyas Bobek <matyas.bobek@gmail.com>\n+ *\n+ * Based on CTU CAN FD emulation implemented by Jan Charvat.\n+ *\n+ * SPDX-License-Identifier: GPL-2.0-or-later\n+ */\n+\n+#ifndef HW_CAN_FLEXCAN_H\n+#define HW_CAN_FLEXCAN_H\n+\n+#include \"net/can_emu.h\"\n+#include \"qom/object.h\"\n+#include \"hw/misc/imx_ccm.h\"\n+\n+#define FLEXCAN_FIFO_DEPTH 6\n+#define FLEXCAN_MAILBOX_COUNT 64\n+\n+/* view of single message buffer registers */\n+typedef struct FlexcanRegsMessageBuffer {\n+ uint32_t can_ctrl;\n+ uint32_t can_id;\n+ uint32_t data[2];\n+} FlexcanRegsMessageBuffer;\n+\n+/* RX FIFO view of message buffer registers */\n+typedef struct FlexcanRegsRXFifo {\n+ /* 6 message buffer deep queue, queue back first */\n+ FlexcanRegsMessageBuffer mb_back;\n+ FlexcanRegsMessageBuffer mbs_queue[FLEXCAN_FIFO_DEPTH - 1];\n+\n+ /* number of filter elements active depends on ctrl2 | FLEXCAN_CTRL2_RFFN */\n+ uint32_t filter_table_els[128];\n+} FlexcanRegsRXFifo;\n+\n+/**\n+ * Structure of the hardware registers\n+ *\n+ * originally created for the Linux kernel by Marc Kleine-Budde\n+ */\n+typedef struct FlexcanRegs {\n+ uint32_t mcr; /* 0x00 */\n+ uint32_t ctrl; /* 0x04 - not affected by soft reset */\n+ uint32_t timer; /* 0x08 */\n+ uint32_t tcr; /* 0x0C */\n+ uint32_t rxmgmask; /* 0x10 - not affected by soft reset */\n+ uint32_t rx14mask; /* 0x14 - not affected by soft reset */\n+ uint32_t rx15mask; /* 0x18 - not affected by soft reset */\n+ uint32_t ecr; /* 0x1C */\n+ uint32_t esr; /* 0x20 */\n+ uint32_t imask2; /* 0x24 */\n+ uint32_t imask1; /* 0x28 */\n+ uint32_t iflag2; /* 0x2C */\n+ uint32_t iflag1; /* 0x30 */\n+ union { /* 0x34 */\n+ uint32_t gfwr_mx28; /* MX28, MX53 */\n+ uint32_t ctrl2; /* MX6, VF610 - not affected by soft reset */\n+ };\n+ uint32_t esr2; /* 0x38 */\n+ uint32_t imeur; /* 0x3C, unused */\n+ uint32_t lrfr; /* 0x40, unused */\n+ uint32_t crcr; /* 0x44 */\n+ uint32_t rxfgmask; /* 0x48 */\n+ uint32_t rxfir; /* 0x4C - not affected by soft reset */\n+ uint32_t cbt; /* 0x50, unused - not affected by soft reset */\n+ uint32_t _reserved2; /* 0x54 */\n+ uint32_t dbg1; /* 0x58, unused */\n+ uint32_t dbg2; /* 0x5C, unused */\n+ uint32_t _reserved3[8]; /* 0x60 */\n+ union { /* 0x80 - not affected by soft reset */\n+ uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT];\n+ FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT];\n+ FlexcanRegsRXFifo fifo;\n+ };\n+ uint32_t _reserved4[256]; /* 0x480 */\n+ uint32_t rximr[64]; /* 0x880 - not affected by soft reset */\n+ uint32_t _reserved5[24]; /* 0x980 */\n+ uint32_t gfwr_mx6; /* 0x9E0 - MX6 */\n+\n+ /* the rest is unused except for SMB */\n+ uint32_t _reserved6[39]; /* 0x9E4 */\n+ uint32_t _rxfir[6]; /* 0xA80 */\n+ uint32_t _reserved8[2]; /* 0xA98 */\n+ uint32_t _rxmgmask; /* 0xAA0 */\n+ uint32_t _rxfgmask; /* 0xAA4 */\n+ uint32_t _rx14mask; /* 0xAA8 */\n+ uint32_t _rx15mask; /* 0xAAC */\n+ uint32_t tx_smb[4]; /* 0xAB0 */\n+ union { /* 0xAC0, used for SMB emulation */\n+ uint32_t rx_smb0_raw[4];\n+ FlexcanRegsMessageBuffer rx_smb0;\n+ };\n+ uint32_t rx_smb1[4]; /* 0xAD0 */\n+ uint32_t mecr; /* 0xAE0 */\n+ uint32_t erriar; /* 0xAE4 */\n+ uint32_t erridpr; /* 0xAE8 */\n+ uint32_t errippr; /* 0xAEC */\n+ uint32_t rerrar; /* 0xAF0 */\n+ uint32_t rerrdr; /* 0xAF4 */\n+ uint32_t rerrsynr; /* 0xAF8 */\n+ uint32_t errsr; /* 0xAFC */\n+ uint32_t _reserved7[64]; /* 0xB00 */\n+ uint32_t fdctrl; /* 0xC00 - not affected by soft reset */\n+ uint32_t fdcbt; /* 0xC04 - not affected by soft reset */\n+ uint32_t fdcrc; /* 0xC08 */\n+ uint32_t _reserved9[199]; /* 0xC0C */\n+ uint32_t tx_smb_fd[18]; /* 0xF28 */\n+ uint32_t rx_smb0_fd[18]; /* 0xF70 */\n+ uint32_t rx_smb1_fd[18]; /* 0xFB8 */\n+} FlexcanRegs;\n+\n+typedef struct FlexcanState {\n+ SysBusDevice parent_obj;\n+\n+ MemoryRegion iomem;\n+ IMXCCMState *ccm;\n+ qemu_irq irq;\n+\n+ CanBusState *canbus;\n+ CanBusClientState bus_client;\n+\n+ union {\n+ FlexcanRegs regs;\n+ uint32_t regs_raw[sizeof(FlexcanRegs) / 4];\n+ };\n+ int64_t timer_start;\n+ uint64_t last_rx_timer_cycles;\n+ int32_t locked_mbidx;\n+ int32_t smb_target_mbidx;\n+ uint32_t timer_freq;\n+} FlexcanState;\n+\n+#define TYPE_CAN_FLEXCAN \"flexcan\"\n+\n+OBJECT_DECLARE_SIMPLE_TYPE(FlexcanState, CAN_FLEXCAN);\n+\n+#endif\n", "prefixes": [ "v3", "4/7" ] }