From nobody Wed May  7 07:55:32 2025
Delivered-To: importer@patchew.org
Authentication-Results: mx.zohomail.com;
	dkim=pass;
	spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as
 permitted sender)
  smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org;
	dmarc=pass(p=none dis=none)  header.from=linaro.org
ARC-Seal: i=1; a=rsa-sha256; t=1607601263; cv=none;
	d=zohomail.com; s=zohoarc;
	b=j6rZz1qJGrNA7ZnnIJunwE0Tb0aHPcbr7rku9uxUAIIrlAoPxuePkE3Pq+25o4Y4lLnvHGCinsDRkUncI2h2dXjmuUcykI0n8lyxVF1mUY4ySY7j7irAV2BXDnPij8IgO071rfWPjE5YkycwaBJZN2JkZ2lcdlasdkfzQTRgseo=
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com;
 s=zohoarc;
	t=1607601263;
 h=Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To;
	bh=akQLSoNN08SKAJpXldnp6iHfpPSy05zG5xxvrJS8YtU=;
	b=ipKeEjVfEwVzKU19RXCuX1xgHzjHUktzxUXRLIkCOJspQGD7jUoqh1jvz8qBNuA093Gnr9NyyS86rfNHTuSeEkhhsvf8hHG6GWJ1+2nBOmx+nftpkKRoWIq11IV3yN/unKxc0FM44y1w+ryKJt6INm//Qo5fK7EIQiVBLaG0/1g=
ARC-Authentication-Results: i=1; mx.zohomail.com;
	dkim=pass;
	spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as
 permitted sender)
  smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org;
	dmarc=pass header.from=<peter.maydell@linaro.org> (p=none dis=none)
 header.from=<peter.maydell@linaro.org>
Return-Path: <qemu-devel-bounces+importer=patchew.org@nongnu.org>
Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by
 mx.zohomail.com
	with SMTPS id 1607601263491646.5974863247541;
 Thu, 10 Dec 2020 03:54:23 -0800 (PST)
Received: from localhost ([::1]:45560 helo=lists1p.gnu.org)
	by lists.gnu.org with esmtp (Exim 4.90_1)
	(envelope-from <qemu-devel-bounces+importer=patchew.org@nongnu.org>)
	id 1knKWb-0006mI-4P
	for importer@patchew.org; Thu, 10 Dec 2020 06:54:22 -0500
Received: from eggs.gnu.org ([2001:470:142:3::10]:51174)
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <peter.maydell@linaro.org>)
 id 1knKQs-0001PU-Oi
 for qemu-devel@nongnu.org; Thu, 10 Dec 2020 06:48:28 -0500
Received: from mail-wr1-x434.google.com ([2a00:1450:4864:20::434]:45732)
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)
 (Exim 4.90_1) (envelope-from <peter.maydell@linaro.org>)
 id 1knKQW-0006yS-JE
 for qemu-devel@nongnu.org; Thu, 10 Dec 2020 06:48:26 -0500
Received: by mail-wr1-x434.google.com with SMTP id t4so5138652wrr.12
 for <qemu-devel@nongnu.org>; Thu, 10 Dec 2020 03:48:04 -0800 (PST)
Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148])
 by smtp.gmail.com with ESMTPSA id g192sm9304725wme.48.2020.12.10.03.48.00
 for <qemu-devel@nongnu.org>
 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
 Thu, 10 Dec 2020 03:48:01 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google;
 h=from:to:subject:date:message-id:in-reply-to:references:mime-version
 :content-transfer-encoding;
 bh=akQLSoNN08SKAJpXldnp6iHfpPSy05zG5xxvrJS8YtU=;
 b=k0SLX3grFErx3+xKp+eOHrLClKMVpo2dI/hOFl2mftW4MpPdRcbzXQa5SqQCZtjJ3J
 J/qDJJyIcKijyHtnSCHgvlL4g0b6LjJGiCj8FGEa/sPiq3RlBziZjnG1xmloLhXz9Y8D
 1sTmdbznXJ9ZFGUjqBsGg00zWOkHriOYWIvzWvof2h0gZLtgQX61GjsS9lN31mytv5Be
 b3AApPm2SS6NQgkRZvw+FQwDiGhSXSLUhsPN6GumWw2aO3ndRrqh6VD58NSISBdNTcPV
 PtGyY4K/IGxpEfbVbg8TMR5Kg8ZYEittuiDPK/Ojqel3tyBKFn1yWBPWcTnpuSkNV+rW
 TEIQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=1e100.net; s=20161025;
 h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to
 :references:mime-version:content-transfer-encoding;
 bh=akQLSoNN08SKAJpXldnp6iHfpPSy05zG5xxvrJS8YtU=;
 b=hhUlGMl8IGJWcFmgwx5m65WbiWklyq9BMD+7vHUXR+VzjIsNF0Hdr8aQnSHZlzVpmg
 ewBF6XcIyLRCIBjXFU3HMvSsnTZ89L9N02NQ4DUssJuFUQQNK5ukdL9Bp+j+Hv7kuq55
 S6+HW72Ec23pnWw3Q7Uk7oUF4oa/6jZHbXtegQvhbj4WeXNDzO+M+0JO3qCy3Utf0/Sb
 r2KgBbK1TDNv3RLVuovjF5BJFi7huf01GXHCg8+NYvPqhsr6uGo4pXqpP5GMx0uwXTge
 DDV627wOrJ1Obh0yOzZLWbV++n/iwfpON9njc3HvUlNi0Ibyd+4nEjkEbRx0Wu6FdOdx
 17mw==
X-Gm-Message-State: AOAM530klxHSyLN/x5yvVlKcEFa2rPzzxkVQI5Kd3NT2ihsw+TEYBJAL
 6GoYS2kgp1InEiKyKg6pjxOgSfx/AEFr2Q==
X-Google-Smtp-Source: 
 ABdhPJwrYPGNBeJ+hT4Ps9gdLZ+mY9c1gW4sq4Zfpew8IgrqnI11eEpI5efGB2NdoyuRFxlgh/piog==
X-Received: by 2002:adf:e44d:: with SMTP id t13mr7756718wrm.144.1607600881976;
 Thu, 10 Dec 2020 03:48:01 -0800 (PST)
From: Peter Maydell <peter.maydell@linaro.org>
To: qemu-devel@nongnu.org
Subject: [PULL 02/36] hw/net/can: Introduce Xilinx ZynqMP CAN controller
Date: Thu, 10 Dec 2020 11:47:22 +0000
Message-Id: <20201210114756.16501-3-peter.maydell@linaro.org>
X-Mailer: git-send-email 2.20.1
In-Reply-To: <20201210114756.16501-1-peter.maydell@linaro.org>
References: <20201210114756.16501-1-peter.maydell@linaro.org>
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17
 as permitted sender) client-ip=209.51.188.17;
 envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org;
 helo=lists.gnu.org;
Received-SPF: pass client-ip=2a00:1450:4864:20::434;
 envelope-from=peter.maydell@linaro.org; helo=mail-wr1-x434.google.com
X-Spam_score_int: -20
X-Spam_score: -2.1
X-Spam_bar: --
X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,
 RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,
 SPF_PASS=-0.001 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-BeenThere: qemu-devel@nongnu.org
X-Mailman-Version: 2.1.23
Precedence: list
List-Id: <qemu-devel.nongnu.org>
List-Unsubscribe: <https://lists.nongnu.org/mailman/options/qemu-devel>,
 <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>,
 <mailto:qemu-devel-request@nongnu.org?subject=subscribe>
Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org
Sender: "Qemu-devel" <qemu-devel-bounces+importer=patchew.org@nongnu.org>
X-ZohoMail-DKIM: pass (identity @linaro.org)
Content-Type: text/plain; charset="utf-8"

From: Vikram Garhwal <fnu.vikram@xilinx.com>

The Xilinx ZynqMP CAN controller is developed based on SocketCAN, QEMU CAN =
bus
implementation. Bus connection and socketCAN connection for each CAN module
can be set through command lines.

Example for using single CAN:
    -object can-bus,id=3Dcanbus0 \
    -machine xlnx-zcu102.canbus0=3Dcanbus0 \
    -object can-host-socketcan,id=3Dsocketcan0,if=3Dvcan0,canbus=3Dcanbus0

Example for connecting both CAN to same virtual CAN on host machine:
    -object can-bus,id=3Dcanbus0 -object can-bus,id=3Dcanbus1 \
    -machine xlnx-zcu102.canbus0=3Dcanbus0 \
    -machine xlnx-zcu102.canbus1=3Dcanbus1 \
    -object can-host-socketcan,id=3Dsocketcan0,if=3Dvcan0,canbus=3Dcanbus0 \
    -object can-host-socketcan,id=3Dsocketcan1,if=3Dvcan0,canbus=3Dcanbus1

To create virtual CAN on the host machine, please check the QEMU CAN docs:
https://github.com/qemu/qemu/blob/master/docs/can.txt

Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
Message-id: 1605728926-352690-2-git-send-email-fnu.vikram@xilinx.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 meson.build                      |    1 +
 hw/net/can/trace.h               |    1 +
 include/hw/net/xlnx-zynqmp-can.h |   78 ++
 hw/net/can/xlnx-zynqmp-can.c     | 1161 ++++++++++++++++++++++++++++++
 hw/Kconfig                       |    1 +
 hw/net/can/meson.build           |    1 +
 hw/net/can/trace-events          |    9 +
 7 files changed, 1252 insertions(+)
 create mode 100644 hw/net/can/trace.h
 create mode 100644 include/hw/net/xlnx-zynqmp-can.h
 create mode 100644 hw/net/can/xlnx-zynqmp-can.c
 create mode 100644 hw/net/can/trace-events

diff --git a/meson.build b/meson.build
index 732b29a1f37..9ea05ab49fa 100644
--- a/meson.build
+++ b/meson.build
@@ -1433,6 +1433,7 @@ if have_system
     'hw/misc',
     'hw/misc/macio',
     'hw/net',
+    'hw/net/can',
     'hw/nvram',
     'hw/pci',
     'hw/pci-host',
diff --git a/hw/net/can/trace.h b/hw/net/can/trace.h
new file mode 100644
index 00000000000..d391c649012
--- /dev/null
+++ b/hw/net/can/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_net_can.h"
diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-zynqmp-=
can.h
new file mode 100644
index 00000000000..eb1558708bb
--- /dev/null
+++ b/include/hw/net/xlnx-zynqmp-can.h
@@ -0,0 +1,78 @@
+/*
+ * QEMU model of the Xilinx ZynqMP CAN controller.
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren =
and
+ * Pavel Pisa.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a=
 copy
+ * of this software and associated documentation files (the "Software"), t=
o deal
+ * in the Software without restriction, including without limitation the r=
ights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or se=
ll
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included=
 in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS=
 OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT=
HER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING=
 FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS =
IN
+ * THE SOFTWARE.
+ */
+
+#ifndef XLNX_ZYNQMP_CAN_H
+#define XLNX_ZYNQMP_CAN_H
+
+#include "hw/register.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+#include "qemu/fifo32.h"
+#include "hw/ptimer.h"
+#include "hw/qdev-clock.h"
+
+#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
+
+#define XLNX_ZYNQMP_CAN(obj) \
+     OBJECT_CHECK(XlnxZynqMPCANState, (obj), TYPE_XLNX_ZYNQMP_CAN)
+
+#define MAX_CAN_CTRLS      2
+#define XLNX_ZYNQMP_CAN_R_MAX     (0x84 / 4)
+#define MAILBOX_CAPACITY   64
+#define CAN_TIMER_MAX  0XFFFFUL
+#define CAN_DEFAULT_CLOCK (24 * 1000 * 1000)
+
+/* Each CAN_FRAME will have 4 * 32bit size. */
+#define CAN_FRAME_SIZE     4
+#define RXFIFO_SIZE        (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
+
+typedef struct XlnxZynqMPCANState {
+    SysBusDevice        parent_obj;
+    MemoryRegion        iomem;
+
+    qemu_irq            irq;
+
+    CanBusClientState   bus_client;
+    CanBusState         *canbus;
+
+    struct {
+        uint32_t        ext_clk_freq;
+    } cfg;
+
+    RegisterInfo        reg_info[XLNX_ZYNQMP_CAN_R_MAX];
+    uint32_t            regs[XLNX_ZYNQMP_CAN_R_MAX];
+
+    Fifo32              rx_fifo;
+    Fifo32              tx_fifo;
+    Fifo32              txhpb_fifo;
+
+    ptimer_state        *can_timer;
+} XlnxZynqMPCANState;
+
+#endif
diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c
new file mode 100644
index 00000000000..affa21a5ed3
--- /dev/null
+++ b/hw/net/can/xlnx-zynqmp-can.c
@@ -0,0 +1,1161 @@
+/*
+ * QEMU model of the Xilinx ZynqMP CAN controller.
+ * This implementation is based on the following datasheet:
+ * https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ul=
trascale-trm.pdf
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren =
and
+ * Pavel Pisa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a=
 copy
+ * of this software and associated documentation files (the "Software"), t=
o deal
+ * in the Software without restriction, including without limitation the r=
ights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or se=
ll
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included=
 in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS=
 OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT=
HER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING=
 FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS =
IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "hw/irq.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/cutils.h"
+#include "sysemu/sysemu.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+#include "qemu/event_notifier.h"
+#include "qom/object_interfaces.h"
+#include "hw/net/xlnx-zynqmp-can.h"
+#include "trace.h"
+
+#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
+#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
+#endif
+
+#define MAX_DLC            8
+#undef ERROR
+
+REG32(SOFTWARE_RESET_REGISTER, 0x0)
+    FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1)
+    FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1)
+REG32(MODE_SELECT_REGISTER, 0x4)
+    FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1)
+    FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1)
+    FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1)
+REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8)
+    FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8)
+REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc)
+    FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 7, 2)
+    FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 4, 3)
+    FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 4)
+REG32(ERROR_COUNTER_REGISTER, 0x10)
+    FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8)
+    FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8)
+REG32(ERROR_STATUS_REGISTER, 0x14)
+    FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1)
+    FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1)
+    FIELD(ERROR_STATUS_REGISTER, STER, 2, 1)
+    FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1)
+    FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1)
+REG32(STATUS_REGISTER, 0x18)
+    FIELD(STATUS_REGISTER, SNOOP, 12, 1)
+    FIELD(STATUS_REGISTER, ACFBSY, 11, 1)
+    FIELD(STATUS_REGISTER, TXFLL, 10, 1)
+    FIELD(STATUS_REGISTER, TXBFLL, 9, 1)
+    FIELD(STATUS_REGISTER, ESTAT, 7, 2)
+    FIELD(STATUS_REGISTER, ERRWRN, 6, 1)
+    FIELD(STATUS_REGISTER, BBSY, 5, 1)
+    FIELD(STATUS_REGISTER, BIDLE, 4, 1)
+    FIELD(STATUS_REGISTER, NORMAL, 3, 1)
+    FIELD(STATUS_REGISTER, SLEEP, 2, 1)
+    FIELD(STATUS_REGISTER, LBACK, 1, 1)
+    FIELD(STATUS_REGISTER, CONFIG, 0, 1)
+REG32(INTERRUPT_STATUS_REGISTER, 0x1c)
+    FIELD(INTERRUPT_STATUS_REGISTER, TXFEMP, 14, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, TXFWMEMP, 13, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, ERROR, 8, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, RXNEMP, 7, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, RXOFLW, 6, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, RXUFLW, 5, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, TXBFLL, 3, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, TXFLL, 2, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1)
+    FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1)
+REG32(INTERRUPT_ENABLE_REGISTER, 0x20)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ETXFEMP, 14, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ETXFWMEMP, 13, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ERXNEMP, 7, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ERXOFLW, 6, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ERXUFLW, 5, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ETXBFLL, 3, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ETXFLL, 2, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1)
+    FIELD(INTERRUPT_ENABLE_REGISTER, EARBLST, 0, 1)
+REG32(INTERRUPT_CLEAR_REGISTER, 0x24)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CTXFEMP, 14, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CTXFWMEMP, 13, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CRXNEMP, 7, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CRXOFLW, 6, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CRXUFLW, 5, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CTXBFLL, 3, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CTXFLL, 2, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1)
+    FIELD(INTERRUPT_CLEAR_REGISTER, CARBLST, 0, 1)
+REG32(TIMESTAMP_REGISTER, 0x28)
+    FIELD(TIMESTAMP_REGISTER, CTS, 0, 1)
+REG32(WIR, 0x2c)
+    FIELD(WIR, EW, 8, 8)
+    FIELD(WIR, FW, 0, 8)
+REG32(TXFIFO_ID, 0x30)
+    FIELD(TXFIFO_ID, IDH, 21, 11)
+    FIELD(TXFIFO_ID, SRRRTR, 20, 1)
+    FIELD(TXFIFO_ID, IDE, 19, 1)
+    FIELD(TXFIFO_ID, IDL, 1, 18)
+    FIELD(TXFIFO_ID, RTR, 0, 1)
+REG32(TXFIFO_DLC, 0x34)
+    FIELD(TXFIFO_DLC, DLC, 28, 4)
+REG32(TXFIFO_DATA1, 0x38)
+    FIELD(TXFIFO_DATA1, DB0, 24, 8)
+    FIELD(TXFIFO_DATA1, DB1, 16, 8)
+    FIELD(TXFIFO_DATA1, DB2, 8, 8)
+    FIELD(TXFIFO_DATA1, DB3, 0, 8)
+REG32(TXFIFO_DATA2, 0x3c)
+    FIELD(TXFIFO_DATA2, DB4, 24, 8)
+    FIELD(TXFIFO_DATA2, DB5, 16, 8)
+    FIELD(TXFIFO_DATA2, DB6, 8, 8)
+    FIELD(TXFIFO_DATA2, DB7, 0, 8)
+REG32(TXHPB_ID, 0x40)
+    FIELD(TXHPB_ID, IDH, 21, 11)
+    FIELD(TXHPB_ID, SRRRTR, 20, 1)
+    FIELD(TXHPB_ID, IDE, 19, 1)
+    FIELD(TXHPB_ID, IDL, 1, 18)
+    FIELD(TXHPB_ID, RTR, 0, 1)
+REG32(TXHPB_DLC, 0x44)
+    FIELD(TXHPB_DLC, DLC, 28, 4)
+REG32(TXHPB_DATA1, 0x48)
+    FIELD(TXHPB_DATA1, DB0, 24, 8)
+    FIELD(TXHPB_DATA1, DB1, 16, 8)
+    FIELD(TXHPB_DATA1, DB2, 8, 8)
+    FIELD(TXHPB_DATA1, DB3, 0, 8)
+REG32(TXHPB_DATA2, 0x4c)
+    FIELD(TXHPB_DATA2, DB4, 24, 8)
+    FIELD(TXHPB_DATA2, DB5, 16, 8)
+    FIELD(TXHPB_DATA2, DB6, 8, 8)
+    FIELD(TXHPB_DATA2, DB7, 0, 8)
+REG32(RXFIFO_ID, 0x50)
+    FIELD(RXFIFO_ID, IDH, 21, 11)
+    FIELD(RXFIFO_ID, SRRRTR, 20, 1)
+    FIELD(RXFIFO_ID, IDE, 19, 1)
+    FIELD(RXFIFO_ID, IDL, 1, 18)
+    FIELD(RXFIFO_ID, RTR, 0, 1)
+REG32(RXFIFO_DLC, 0x54)
+    FIELD(RXFIFO_DLC, DLC, 28, 4)
+    FIELD(RXFIFO_DLC, RXT, 0, 16)
+REG32(RXFIFO_DATA1, 0x58)
+    FIELD(RXFIFO_DATA1, DB0, 24, 8)
+    FIELD(RXFIFO_DATA1, DB1, 16, 8)
+    FIELD(RXFIFO_DATA1, DB2, 8, 8)
+    FIELD(RXFIFO_DATA1, DB3, 0, 8)
+REG32(RXFIFO_DATA2, 0x5c)
+    FIELD(RXFIFO_DATA2, DB4, 24, 8)
+    FIELD(RXFIFO_DATA2, DB5, 16, 8)
+    FIELD(RXFIFO_DATA2, DB6, 8, 8)
+    FIELD(RXFIFO_DATA2, DB7, 0, 8)
+REG32(AFR, 0x60)
+    FIELD(AFR, UAF4, 3, 1)
+    FIELD(AFR, UAF3, 2, 1)
+    FIELD(AFR, UAF2, 1, 1)
+    FIELD(AFR, UAF1, 0, 1)
+REG32(AFMR1, 0x64)
+    FIELD(AFMR1, AMIDH, 21, 11)
+    FIELD(AFMR1, AMSRR, 20, 1)
+    FIELD(AFMR1, AMIDE, 19, 1)
+    FIELD(AFMR1, AMIDL, 1, 18)
+    FIELD(AFMR1, AMRTR, 0, 1)
+REG32(AFIR1, 0x68)
+    FIELD(AFIR1, AIIDH, 21, 11)
+    FIELD(AFIR1, AISRR, 20, 1)
+    FIELD(AFIR1, AIIDE, 19, 1)
+    FIELD(AFIR1, AIIDL, 1, 18)
+    FIELD(AFIR1, AIRTR, 0, 1)
+REG32(AFMR2, 0x6c)
+    FIELD(AFMR2, AMIDH, 21, 11)
+    FIELD(AFMR2, AMSRR, 20, 1)
+    FIELD(AFMR2, AMIDE, 19, 1)
+    FIELD(AFMR2, AMIDL, 1, 18)
+    FIELD(AFMR2, AMRTR, 0, 1)
+REG32(AFIR2, 0x70)
+    FIELD(AFIR2, AIIDH, 21, 11)
+    FIELD(AFIR2, AISRR, 20, 1)
+    FIELD(AFIR2, AIIDE, 19, 1)
+    FIELD(AFIR2, AIIDL, 1, 18)
+    FIELD(AFIR2, AIRTR, 0, 1)
+REG32(AFMR3, 0x74)
+    FIELD(AFMR3, AMIDH, 21, 11)
+    FIELD(AFMR3, AMSRR, 20, 1)
+    FIELD(AFMR3, AMIDE, 19, 1)
+    FIELD(AFMR3, AMIDL, 1, 18)
+    FIELD(AFMR3, AMRTR, 0, 1)
+REG32(AFIR3, 0x78)
+    FIELD(AFIR3, AIIDH, 21, 11)
+    FIELD(AFIR3, AISRR, 20, 1)
+    FIELD(AFIR3, AIIDE, 19, 1)
+    FIELD(AFIR3, AIIDL, 1, 18)
+    FIELD(AFIR3, AIRTR, 0, 1)
+REG32(AFMR4, 0x7c)
+    FIELD(AFMR4, AMIDH, 21, 11)
+    FIELD(AFMR4, AMSRR, 20, 1)
+    FIELD(AFMR4, AMIDE, 19, 1)
+    FIELD(AFMR4, AMIDL, 1, 18)
+    FIELD(AFMR4, AMRTR, 0, 1)
+REG32(AFIR4, 0x80)
+    FIELD(AFIR4, AIIDH, 21, 11)
+    FIELD(AFIR4, AISRR, 20, 1)
+    FIELD(AFIR4, AIIDE, 19, 1)
+    FIELD(AFIR4, AIIDL, 1, 18)
+    FIELD(AFIR4, AIRTR, 0, 1)
+
+static void can_update_irq(XlnxZynqMPCANState *s)
+{
+    uint32_t irq;
+
+    /* Watermark register interrupts. */
+    if ((fifo32_num_free(&s->tx_fifo) / CAN_FRAME_SIZE) >
+            ARRAY_FIELD_EX32(s->regs, WIR, EW)) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFWMEMP, 1);
+    }
+
+    if ((fifo32_num_used(&s->rx_fifo) / CAN_FRAME_SIZE) >
+            ARRAY_FIELD_EX32(s->regs, WIR, FW)) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1);
+    }
+
+    /* RX Interrupts. */
+    if (fifo32_num_used(&s->rx_fifo) >=3D CAN_FRAME_SIZE) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXNEMP, 1);
+    }
+
+    /* TX interrupts. */
+    if (fifo32_is_empty(&s->tx_fifo)) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFEMP, 1);
+    }
+
+    if (fifo32_is_full(&s->tx_fifo)) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFLL, 1);
+    }
+
+    if (fifo32_is_full(&s->txhpb_fifo)) {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXBFLL, 1);
+    }
+
+    irq =3D s->regs[R_INTERRUPT_STATUS_REGISTER];
+    irq &=3D s->regs[R_INTERRUPT_ENABLE_REGISTER];
+
+    trace_xlnx_can_update_irq(s->regs[R_INTERRUPT_STATUS_REGISTER],
+                              s->regs[R_INTERRUPT_ENABLE_REGISTER], irq);
+    qemu_set_irq(s->irq, irq);
+}
+
+static void can_ier_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    can_update_irq(s);
+}
+
+static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    s->regs[R_INTERRUPT_STATUS_REGISTER] &=3D ~val;
+    can_update_irq(s);
+
+    return 0;
+}
+
+static void can_config_reset(XlnxZynqMPCANState *s)
+{
+    /* Reset all the configuration registers. */
+    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
+    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
+    register_reset(
+              &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTE=
R]);
+    register_reset(&s->reg_info[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER]);
+    register_reset(&s->reg_info[R_STATUS_REGISTER]);
+    register_reset(&s->reg_info[R_INTERRUPT_STATUS_REGISTER]);
+    register_reset(&s->reg_info[R_INTERRUPT_ENABLE_REGISTER]);
+    register_reset(&s->reg_info[R_INTERRUPT_CLEAR_REGISTER]);
+    register_reset(&s->reg_info[R_WIR]);
+}
+
+static void can_config_mode(XlnxZynqMPCANState *s)
+{
+    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
+    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
+
+    /* Put XlnxZynqMPCAN in configuration mode. */
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
+
+    can_update_irq(s);
+}
+
+static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
+{
+    bool sleep_status =3D ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP=
);
+    bool sleep_mode =3D ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SL=
EEP);
+    /* Wake up interrupt bit. */
+    bool wakeup_irq_val =3D sleep_status && (sleep_mode =3D=3D 0);
+    /* Sleep interrupt bit. */
+    bool sleep_irq_val =3D sleep_mode && (sleep_status =3D=3D 0);
+
+    /* Clear previous core mode status bits. */
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0);
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0);
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0);
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0);
+
+    /* set current mode bit and generate irqs accordingly. */
+    if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) {
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1);
+    } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) {
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1);
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP,
+                         sleep_irq_val);
+    } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) {
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1);
+    } else {
+        /*
+         * If all bits are zero then XlnxZynqMPCAN is set in normal mode.
+         */
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
+        /* Set wakeup interrupt bit. */
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
+                         wakeup_irq_val);
+    }
+
+    can_update_irq(s);
+}
+
+static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
+{
+    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
+    update_status_register_mode_bits(s);
+}
+
+static void generate_frame(qemu_can_frame *frame, uint32_t *data)
+{
+    frame->can_id =3D data[0];
+    frame->can_dlc =3D FIELD_EX32(data[1], TXFIFO_DLC, DLC);
+
+    frame->data[0] =3D FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
+    frame->data[1] =3D FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
+    frame->data[2] =3D FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
+    frame->data[3] =3D FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
+
+    frame->data[4] =3D FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
+    frame->data[5] =3D FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
+    frame->data[6] =3D FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
+    frame->data[7] =3D FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
+}
+
+static bool tx_ready_check(XlnxZynqMPCANState *s)
+{
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data wh=
ile"
+                      " data while controller is in reset mode.\n",
+                      path);
+        return false;
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) =3D=3D 0) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
+                      " data while controller is in configuration mode. Re=
set"
+                      " the core so operations can start fresh.\n",
+                      path);
+        return false;
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
+                      " data while controller is in SNOOP MODE.\n",
+                      path);
+        return false;
+    }
+
+    return true;
+}
+
+static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
+{
+    qemu_can_frame frame;
+    uint32_t data[CAN_FRAME_SIZE];
+    int i;
+    bool can_tx =3D tx_ready_check(s);
+
+    if (!can_tx) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is not enabled for =
data"
+                      " transfer.\n", path);
+        can_update_irq(s);
+        return;
+    }
+
+    while (!fifo32_is_empty(fifo)) {
+        for (i =3D 0; i < CAN_FRAME_SIZE; i++) {
+            data[i] =3D fifo32_pop(fifo);
+        }
+
+        if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
+            /*
+             * Controller is in loopback. In Loopback mode, the CAN core
+             * transmits a recessive bitstream on to the XlnxZynqMPCAN Bus.
+             * Any message transmitted is looped back to the RX line and
+             * acknowledged. The XlnxZynqMPCAN core receives any message
+             * that it transmits.
+             */
+            if (fifo32_is_full(&s->rx_fifo)) {
+                ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFL=
W, 1);
+            } else {
+                for (i =3D 0; i < CAN_FRAME_SIZE; i++) {
+                    fifo32_push(&s->rx_fifo, data[i]);
+                }
+
+                ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK,=
 1);
+            }
+        } else {
+            /* Normal mode Tx. */
+            generate_frame(&frame, data);
+
+            trace_xlnx_can_tx_data(frame.can_id, frame.can_dlc,
+                                   frame.data[0], frame.data[1],
+                                   frame.data[2], frame.data[3],
+                                   frame.data[4], frame.data[5],
+                                   frame.data[6], frame.data[7]);
+            can_bus_client_send(&s->bus_client, &frame, 1);
+        }
+    }
+
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1);
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, TXBFLL, 0);
+
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) {
+        can_exit_sleep_mode(s);
+    }
+
+    can_update_irq(s);
+}
+
+static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
+                     FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
+
+    if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
+        trace_xlnx_can_reset(val);
+
+        /* First, core will do software reset then will enter in config mo=
de. */
+        can_config_reset(s);
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) =3D=3D 0) {
+        can_config_mode(s);
+    } else {
+        /*
+         * Leave config mode. Now XlnxZynqMPCAN core will enter normal,
+         * sleep, snoop or loopback mode depending upon LBACK, SLEEP, SNOOP
+         * register states.
+         */
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
+
+        ptimer_transaction_begin(s->can_timer);
+        ptimer_set_count(s->can_timer, 0);
+        ptimer_transaction_commit(s->can_timer);
+
+        /* XlnxZynqMPCAN is out of config mode. It will send pending data.=
 */
+        transfer_fifo(s, &s->txhpb_fifo);
+        transfer_fifo(s, &s->tx_fifo);
+    }
+
+    update_status_register_mode_bits(s);
+
+    return s->regs[R_SOFTWARE_RESET_REGISTER];
+}
+
+static uint64_t can_msr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+    uint8_t multi_mode;
+
+    /*
+     * Multiple mode set check. This is done to make sure user doesn't set
+     * multiple modes.
+     */
+    multi_mode =3D FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) +
+                 FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) +
+                 FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP);
+
+    if (multi_mode > 1) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to config"
+                      " several modes simultaneously. One mode will be sel=
ected"
+                      " according to their priority: LBACK > SLEEP > SNOOP=
.\n",
+                      path);
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) =3D=3D 0) {
+        /* We are in configuration mode, any mode can be selected. */
+        s->regs[R_MODE_SELECT_REGISTER] =3D val;
+    } else {
+        bool sleep_mode_bit =3D FIELD_EX32(val, MODE_SELECT_REGISTER, SLEE=
P);
+
+        ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_=
bit);
+
+        if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
+            g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
+                          " LBACK mode without setting CEN bit as 0.\n",
+                          path);
+        } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
+            g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
+                          " SNOOP mode without setting CEN bit as 0.\n",
+                          path);
+        }
+
+        update_status_register_mode_bits(s);
+    }
+
+    return s->regs[R_MODE_SELECT_REGISTER];
+}
+
+static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    /* Only allow writes when in config mode. */
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+        return s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
+    }
+
+    return val;
+}
+
+static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    /* Only allow writes when in config mode. */
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+        return s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
+    }
+
+    return val;
+}
+
+static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
+        ptimer_transaction_begin(s->can_timer);
+        ptimer_set_count(s->can_timer, 0);
+        ptimer_transaction_commit(s->can_timer);
+    }
+
+    return 0;
+}
+
+static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *fr=
ame)
+{
+    bool filter_pass =3D false;
+    uint16_t timestamp =3D 0;
+
+    /* If no filter is enabled. Message will be stored in FIFO. */
+    if (!((ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) |
+       (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) |
+       (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) |
+       (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)))) {
+        filter_pass =3D true;
+    }
+
+    /*
+     * Messages that pass any of the acceptance filters will be stored in
+     * the RX FIFO.
+     */
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) {
+        uint32_t id_masked =3D s->regs[R_AFMR1] & frame->can_id;
+        uint32_t filter_id_masked =3D s->regs[R_AFMR1] & s->regs[R_AFIR1];
+
+        if (filter_id_masked =3D=3D id_masked) {
+            filter_pass =3D true;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
+        uint32_t id_masked =3D s->regs[R_AFMR2] & frame->can_id;
+        uint32_t filter_id_masked =3D s->regs[R_AFMR2] & s->regs[R_AFIR2];
+
+        if (filter_id_masked =3D=3D id_masked) {
+            filter_pass =3D true;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
+        uint32_t id_masked =3D s->regs[R_AFMR3] & frame->can_id;
+        uint32_t filter_id_masked =3D s->regs[R_AFMR3] & s->regs[R_AFIR3];
+
+        if (filter_id_masked =3D=3D id_masked) {
+            filter_pass =3D true;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
+        uint32_t id_masked =3D s->regs[R_AFMR4] & frame->can_id;
+        uint32_t filter_id_masked =3D s->regs[R_AFMR4] & s->regs[R_AFIR4];
+
+        if (filter_id_masked =3D=3D id_masked) {
+            filter_pass =3D true;
+        }
+    }
+
+    if (!filter_pass) {
+        trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc=
);
+        return;
+    }
+
+    /* Store the message in fifo if it passed through any of the filters. =
*/
+    if (filter_pass && frame->can_dlc <=3D MAX_DLC) {
+
+        if (fifo32_is_full(&s->rx_fifo)) {
+            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1=
);
+        } else {
+            timestamp =3D CAN_TIMER_MAX - ptimer_get_count(s->can_timer);
+
+            fifo32_push(&s->rx_fifo, frame->can_id);
+
+            fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT,
+                                               R_RXFIFO_DLC_DLC_LENGTH,
+                                               frame->can_dlc) |
+                                     deposit32(0, R_RXFIFO_DLC_RXT_SHIFT,
+                                               R_RXFIFO_DLC_RXT_LENGTH,
+                                               timestamp));
+
+            /* First 32 bit of the data. */
+            fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA1_DB3_SHIFT,
+                                               R_TXFIFO_DATA1_DB3_LENGTH,
+                                               frame->data[0]) |
+                                     deposit32(0, R_TXFIFO_DATA1_DB2_SHIFT,
+                                               R_TXFIFO_DATA1_DB2_LENGTH,
+                                               frame->data[1]) |
+                                     deposit32(0, R_TXFIFO_DATA1_DB1_SHIFT,
+                                               R_TXFIFO_DATA1_DB1_LENGTH,
+                                               frame->data[2]) |
+                                     deposit32(0, R_TXFIFO_DATA1_DB0_SHIFT,
+                                               R_TXFIFO_DATA1_DB0_LENGTH,
+                                               frame->data[3]));
+            /* Last 32 bit of the data. */
+            fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA2_DB7_SHIFT,
+                                               R_TXFIFO_DATA2_DB7_LENGTH,
+                                               frame->data[4]) |
+                                     deposit32(0, R_TXFIFO_DATA2_DB6_SHIFT,
+                                               R_TXFIFO_DATA2_DB6_LENGTH,
+                                               frame->data[5]) |
+                                     deposit32(0, R_TXFIFO_DATA2_DB5_SHIFT,
+                                               R_TXFIFO_DATA2_DB5_LENGTH,
+                                               frame->data[6]) |
+                                     deposit32(0, R_TXFIFO_DATA2_DB4_SHIFT,
+                                               R_TXFIFO_DATA2_DB4_LENGTH,
+                                               frame->data[7]));
+
+            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1);
+            trace_xlnx_can_rx_data(frame->can_id, frame->can_dlc,
+                                   frame->data[0], frame->data[1],
+                                   frame->data[2], frame->data[3],
+                                   frame->data[4], frame->data[5],
+                                   frame->data[6], frame->data[7]);
+        }
+
+        can_update_irq(s);
+    }
+}
+
+static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    if (!fifo32_is_empty(&s->rx_fifo)) {
+        val =3D fifo32_pop(&s->rx_fifo);
+    } else {
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1);
+    }
+
+    can_update_irq(s);
+    return val;
+}
+
+static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1) &&
+        ARRAY_FIELD_EX32(s->regs, AFR, UAF2) &&
+        ARRAY_FIELD_EX32(s->regs, AFR, UAF3) &&
+        ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 1);
+    } else {
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
+    }
+}
+
+static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t reg_idx =3D (reg->access->addr) / 4;
+    uint32_t filter_number =3D (reg_idx - R_AFMR1) / 2;
+
+    /* modify an acceptance filter, the corresponding UAF bit should be '0=
'. */
+    if (!(s->regs[R_AFR] & (1 << filter_number))) {
+        s->regs[reg_idx] =3D val;
+
+        trace_xlnx_can_filter_mask_pre_write(filter_number, s->regs[reg_id=
x]);
+    } else {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
+                      " mask is not set as corresponding UAF bit is not 0.=
\n",
+                      path, filter_number + 1);
+    }
+
+    return s->regs[reg_idx];
+}
+
+static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t reg_idx =3D (reg->access->addr) / 4;
+    uint32_t filter_number =3D (reg_idx - R_AFIR1) / 2;
+
+    if (!(s->regs[R_AFR] & (1 << filter_number))) {
+        s->regs[reg_idx] =3D val;
+
+        trace_xlnx_can_filter_id_pre_write(filter_number, s->regs[reg_idx]=
);
+    } else {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
+                      " id is not set as corresponding UAF bit is not 0.\n=
",
+                      path, filter_number + 1);
+    }
+
+    return s->regs[reg_idx];
+}
+
+static void can_tx_post_write(RegisterInfo *reg, uint64_t val)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(reg->opaque);
+
+    bool is_txhpb =3D reg->access->addr > A_TXFIFO_DATA2;
+
+    bool initiate_transfer =3D (reg->access->addr =3D=3D A_TXFIFO_DATA2) ||
+                             (reg->access->addr =3D=3D A_TXHPB_DATA2);
+
+    Fifo32 *f =3D is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
+
+    if (!fifo32_is_full(f)) {
+        fifo32_push(f, val);
+    } else {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: TX FIFO is full.\n", path);
+    }
+
+    /* Initiate the message send if TX register is written. */
+    if (initiate_transfer &&
+        ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+        transfer_fifo(s, f);
+    }
+
+    can_update_irq(s);
+}
+
+static const RegisterAccessInfo can_regs_info[] =3D {
+    {   .name =3D "SOFTWARE_RESET_REGISTER",
+        .addr =3D A_SOFTWARE_RESET_REGISTER,
+        .rsvd =3D 0xfffffffc,
+        .pre_write =3D can_srr_pre_write,
+    },{ .name =3D "MODE_SELECT_REGISTER",
+        .addr =3D A_MODE_SELECT_REGISTER,
+        .rsvd =3D 0xfffffff8,
+        .pre_write =3D can_msr_pre_write,
+    },{ .name =3D "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
+        .addr =3D A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
+        .rsvd =3D 0xffffff00,
+        .pre_write =3D can_brpr_pre_write,
+    },{ .name =3D "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
+        .addr =3D A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
+        .rsvd =3D 0xfffffe00,
+        .pre_write =3D can_btr_pre_write,
+    },{ .name =3D "ERROR_COUNTER_REGISTER",
+        .addr =3D A_ERROR_COUNTER_REGISTER,
+        .rsvd =3D 0xffff0000,
+        .ro =3D 0xffffffff,
+    },{ .name =3D "ERROR_STATUS_REGISTER",
+        .addr =3D A_ERROR_STATUS_REGISTER,
+        .rsvd =3D 0xffffffe0,
+        .w1c =3D 0x1f,
+    },{ .name =3D "STATUS_REGISTER",  .addr =3D A_STATUS_REGISTER,
+        .reset =3D 0x1,
+        .rsvd =3D 0xffffe000,
+        .ro =3D 0x1fff,
+    },{ .name =3D "INTERRUPT_STATUS_REGISTER",
+        .addr =3D A_INTERRUPT_STATUS_REGISTER,
+        .reset =3D 0x6000,
+        .rsvd =3D 0xffff8000,
+        .ro =3D 0x7fff,
+    },{ .name =3D "INTERRUPT_ENABLE_REGISTER",
+        .addr =3D A_INTERRUPT_ENABLE_REGISTER,
+        .rsvd =3D 0xffff8000,
+        .post_write =3D can_ier_post_write,
+    },{ .name =3D "INTERRUPT_CLEAR_REGISTER",
+        .addr =3D A_INTERRUPT_CLEAR_REGISTER,
+        .rsvd =3D 0xffff8000,
+        .pre_write =3D can_icr_pre_write,
+    },{ .name =3D "TIMESTAMP_REGISTER",
+        .addr =3D A_TIMESTAMP_REGISTER,
+        .rsvd =3D 0xfffffffe,
+        .pre_write =3D can_tcr_pre_write,
+    },{ .name =3D "WIR",  .addr =3D A_WIR,
+        .reset =3D 0x3f3f,
+        .rsvd =3D 0xffff0000,
+    },{ .name =3D "TXFIFO_ID",  .addr =3D A_TXFIFO_ID,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXFIFO_DLC",  .addr =3D A_TXFIFO_DLC,
+        .rsvd =3D 0xfffffff,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXFIFO_DATA1",  .addr =3D A_TXFIFO_DATA1,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXFIFO_DATA2",  .addr =3D A_TXFIFO_DATA2,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXHPB_ID",  .addr =3D A_TXHPB_ID,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXHPB_DLC",  .addr =3D A_TXHPB_DLC,
+        .rsvd =3D 0xfffffff,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXHPB_DATA1",  .addr =3D A_TXHPB_DATA1,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "TXHPB_DATA2",  .addr =3D A_TXHPB_DATA2,
+        .post_write =3D can_tx_post_write,
+    },{ .name =3D "RXFIFO_ID",  .addr =3D A_RXFIFO_ID,
+        .ro =3D 0xffffffff,
+        .post_read =3D can_rxfifo_pre_read,
+    },{ .name =3D "RXFIFO_DLC",  .addr =3D A_RXFIFO_DLC,
+        .rsvd =3D 0xfff0000,
+        .post_read =3D can_rxfifo_pre_read,
+    },{ .name =3D "RXFIFO_DATA1",  .addr =3D A_RXFIFO_DATA1,
+        .post_read =3D can_rxfifo_pre_read,
+    },{ .name =3D "RXFIFO_DATA2",  .addr =3D A_RXFIFO_DATA2,
+        .post_read =3D can_rxfifo_pre_read,
+    },{ .name =3D "AFR",  .addr =3D A_AFR,
+        .rsvd =3D 0xfffffff0,
+        .post_write =3D can_filter_enable_post_write,
+    },{ .name =3D "AFMR1",  .addr =3D A_AFMR1,
+        .pre_write =3D can_filter_mask_pre_write,
+    },{ .name =3D "AFIR1",  .addr =3D A_AFIR1,
+        .pre_write =3D can_filter_id_pre_write,
+    },{ .name =3D "AFMR2",  .addr =3D A_AFMR2,
+        .pre_write =3D can_filter_mask_pre_write,
+    },{ .name =3D "AFIR2",  .addr =3D A_AFIR2,
+        .pre_write =3D can_filter_id_pre_write,
+    },{ .name =3D "AFMR3",  .addr =3D A_AFMR3,
+        .pre_write =3D can_filter_mask_pre_write,
+    },{ .name =3D "AFIR3",  .addr =3D A_AFIR3,
+        .pre_write =3D can_filter_id_pre_write,
+    },{ .name =3D "AFMR4",  .addr =3D A_AFMR4,
+        .pre_write =3D can_filter_mask_pre_write,
+    },{ .name =3D "AFIR4",  .addr =3D A_AFIR4,
+        .pre_write =3D can_filter_id_pre_write,
+    }
+};
+
+static void xlnx_zynqmp_can_ptimer_cb(void *opaque)
+{
+    /* No action required on the timer rollover. */
+}
+
+static const MemoryRegionOps can_ops =3D {
+    .read =3D register_read_memory,
+    .write =3D register_write_memory,
+    .endianness =3D DEVICE_LITTLE_ENDIAN,
+    .valid =3D {
+        .min_access_size =3D 4,
+        .max_access_size =3D 4,
+    },
+};
+
+static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(obj);
+    unsigned int i;
+
+    for (i =3D R_RXFIFO_ID; i < ARRAY_SIZE(s->reg_info); ++i) {
+        register_reset(&s->reg_info[i]);
+    }
+
+    ptimer_transaction_begin(s->can_timer);
+    ptimer_set_count(s->can_timer, 0);
+    ptimer_transaction_commit(s->can_timer);
+}
+
+static void xlnx_zynqmp_can_reset_hold(Object *obj)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(obj);
+    unsigned int i;
+
+    for (i =3D 0; i < R_RXFIFO_ID; ++i) {
+        register_reset(&s->reg_info[i]);
+    }
+
+    /*
+     * Reset FIFOs when CAN model is reset. This will clear the fifo writes
+     * done by post_write which gets called from register_reset function,
+     * post_write handle will not be able to trigger tx because CAN will be
+     * disabled when software_reset_register is cleared first.
+     */
+    fifo32_reset(&s->rx_fifo);
+    fifo32_reset(&s->tx_fifo);
+    fifo32_reset(&s->txhpb_fifo);
+}
+
+static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
+{
+    XlnxZynqMPCANState *s =3D container_of(client, XlnxZynqMPCANState,
+                                         bus_client);
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in reset state.\=
n",
+                      path);
+        return false;
+    }
+
+    if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) =3D=3D 0=
) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is disabled. Incomi=
ng"
+                      " messages will be discarded.\n", path);
+        return false;
+    }
+
+    return true;
+}
+
+static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
+                               const qemu_can_frame *buf, size_t buf_size)=
 {
+    XlnxZynqMPCANState *s =3D container_of(client, XlnxZynqMPCANState,
+                                         bus_client);
+    const qemu_can_frame *frame =3D buf;
+
+    if (buf_size <=3D 0) {
+        g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n",
+                      path);
+        return 0;
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
+        /* Snoop Mode: Just keep the data. no response back. */
+        update_rx_fifo(s, frame);
+    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) {
+        /*
+         * XlnxZynqMPCAN is in sleep mode. Any data on bus will bring it t=
o wake
+         * up state.
+         */
+        can_exit_sleep_mode(s);
+        update_rx_fifo(s, frame);
+    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) =3D=3D =
0) {
+        update_rx_fifo(s, frame);
+    } else {
+        /*
+         * XlnxZynqMPCAN will not participate in normal bus communication
+         * and will not receive any messages transmitted by other CAN node=
s.
+         */
+        trace_xlnx_can_rx_discard(s->regs[R_STATUS_REGISTER]);
+    }
+
+    return 1;
+}
+
+static CanBusClientInfo can_xilinx_bus_client_info =3D {
+    .can_receive =3D xlnx_zynqmp_can_can_receive,
+    .receive =3D xlnx_zynqmp_can_receive,
+};
+
+static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCANState *s,
+                                          CanBusState *bus)
+{
+    s->bus_client.info =3D &can_xilinx_bus_client_info;
+
+    if (can_bus_insert_client(bus, &s->bus_client) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void xlnx_zynqmp_can_realize(DeviceState *dev, Error **errp)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(dev);
+
+    if (s->canbus) {
+        if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus) < 0) {
+            g_autofree char *path =3D object_get_canonical_path(OBJECT(s));
+
+            error_setg(errp, "%s: xlnx_zynqmp_can_connect_to_bus"
+                       " failed.", path);
+            return;
+        }
+    }
+
+    /* Create RX FIFO, TXFIFO, TXHPB storage. */
+    fifo32_create(&s->rx_fifo, RXFIFO_SIZE);
+    fifo32_create(&s->tx_fifo, RXFIFO_SIZE);
+    fifo32_create(&s->txhpb_fifo, CAN_FRAME_SIZE);
+
+    /* Allocate a new timer. */
+    s->can_timer =3D ptimer_init(xlnx_zynqmp_can_ptimer_cb, s,
+                               PTIMER_POLICY_DEFAULT);
+
+    ptimer_transaction_begin(s->can_timer);
+
+    ptimer_set_freq(s->can_timer, s->cfg.ext_clk_freq);
+    ptimer_set_limit(s->can_timer, CAN_TIMER_MAX, 1);
+    ptimer_run(s->can_timer, 0);
+    ptimer_transaction_commit(s->can_timer);
+}
+
+static void xlnx_zynqmp_can_init(Object *obj)
+{
+    XlnxZynqMPCANState *s =3D XLNX_ZYNQMP_CAN(obj);
+    SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj);
+
+    RegisterInfoArray *reg_array;
+
+    memory_region_init(&s->iomem, obj, TYPE_XLNX_ZYNQMP_CAN,
+                        XLNX_ZYNQMP_CAN_R_MAX * 4);
+    reg_array =3D register_init_block32(DEVICE(obj), can_regs_info,
+                               ARRAY_SIZE(can_regs_info),
+                               s->reg_info, s->regs,
+                               &can_ops,
+                               XLNX_ZYNQMP_CAN_ERR_DEBUG,
+                               XLNX_ZYNQMP_CAN_R_MAX * 4);
+
+    memory_region_add_subregion(&s->iomem, 0x00, &reg_array->mem);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+}
+
+static const VMStateDescription vmstate_can =3D {
+    .name =3D TYPE_XLNX_ZYNQMP_CAN,
+    .version_id =3D 1,
+    .minimum_version_id =3D 1,
+    .fields =3D (VMStateField[]) {
+        VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCANState),
+        VMSTATE_FIFO32(tx_fifo, XlnxZynqMPCANState),
+        VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState),
+        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_M=
AX),
+        VMSTATE_PTIMER(can_timer, XlnxZynqMPCANState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property xlnx_zynqmp_can_properties[] =3D {
+    DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_fre=
q,
+                       CAN_DEFAULT_CLOCK),
+    DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS,
+                     CanBusState *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc =3D DEVICE_CLASS(klass);
+    ResettableClass *rc =3D RESETTABLE_CLASS(klass);
+
+    rc->phases.enter =3D xlnx_zynqmp_can_reset_init;
+    rc->phases.hold =3D xlnx_zynqmp_can_reset_hold;
+    dc->realize =3D xlnx_zynqmp_can_realize;
+    device_class_set_props(dc, xlnx_zynqmp_can_properties);
+    dc->vmsd =3D &vmstate_can;
+}
+
+static const TypeInfo can_info =3D {
+    .name          =3D TYPE_XLNX_ZYNQMP_CAN,
+    .parent        =3D TYPE_SYS_BUS_DEVICE,
+    .instance_size =3D sizeof(XlnxZynqMPCANState),
+    .class_init    =3D xlnx_zynqmp_can_class_init,
+    .instance_init =3D xlnx_zynqmp_can_init,
+};
+
+static void can_register_types(void)
+{
+    type_register_static(&can_info);
+}
+
+type_init(can_register_types)
diff --git a/hw/Kconfig b/hw/Kconfig
index 4de1797ffda..5ad3c6b5a4b 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -80,3 +80,4 @@ config XILINX_AXI
 config XLNX_ZYNQMP
     bool
     select REGISTER
+    select CAN_BUS
diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build
index 714951f375a..8fabbd9ee68 100644
--- a/hw/net/can/meson.build
+++ b/hw/net/can/meson.build
@@ -4,3 +4,4 @@ softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_=
pcm3680_pci.c'))
 softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c'=
))
 softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'=
))
 softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci=
.c'))
+softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can=
.c'))
diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events
new file mode 100644
index 00000000000..8346a98ab5d
--- /dev/null
+++ b/hw/net/can/trace-events
@@ -0,0 +1,9 @@
+# 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 =3D 0x%08x"
+xlnx_can_rx_fifo_filter_reject(uint32_t id, uint8_t dlc) "Frame: ID: 0x%08=
x DLC: 0x%02x"
+xlnx_can_filter_id_pre_write(uint8_t filter_num, uint32_t value) "Filter%d=
 ID: 0x%08x"
+xlnx_can_filter_mask_pre_write(uint8_t filter_num, uint32_t value) "Filter=
%d MASK: 0x%08x"
+xlnx_can_tx_data(uint32_t id, uint8_t dlc, uint8_t db0, uint8_t db1, uint8=
_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "F=
rame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02=
x 0x%02x 0x%02x"
+xlnx_can_rx_data(uint32_t id, uint32_t dlc, uint8_t db0, uint8_t db1, uint=
8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "=
Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%0=
2x 0x%02x 0x%02x"
+xlnx_can_rx_discard(uint32_t status) "Controller is not enabled for bus co=
mmunication. Status Register: 0x%08x"
--=20
2.20.1