All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
@ 2020-02-03 21:06 Vikram Garhwal
  2020-02-03 21:06 ` [PATCH 1/1] hw/net/can: " Vikram Garhwal
  2020-02-05  3:17 ` [PATCH 0/1] " Jason Wang
  0 siblings, 2 replies; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-03 21:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: jasowang

Example for single CAN:
    -object can-bus,id=canbus0 \
    -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
    -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0

Example for connecting both CAN:
    -object can-bus,id=canbus0 -object can-bus,id=canbus1 \
    -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
    -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus1 \
    -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0 \
    -object can-host-socketcan,id=socketcan1,if=vcan0,canbus=canbus1

Vikram Garhwal (1):
  hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU

 hw/net/can/Makefile.objs         |    1 +
 hw/net/can/xlnx-zynqmp-can.c     | 1106 ++++++++++++++++++++++++++++++++++++++
 include/hw/net/xlnx-zynqmp-can.h |   77 +++
 3 files changed, 1184 insertions(+)
 create mode 100644 hw/net/can/xlnx-zynqmp-can.c
 create mode 100644 include/hw/net/xlnx-zynqmp-can.h

-- 
2.7.4



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-03 21:06 [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU Vikram Garhwal
@ 2020-02-03 21:06 ` Vikram Garhwal
  2020-02-05  5:46   ` Jason Wang
  2020-02-05  3:17 ` [PATCH 0/1] " Jason Wang
  1 sibling, 1 reply; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-03 21:06 UTC (permalink / raw)
  To: qemu-devel; +Cc: jasowang

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

Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
---
 hw/net/can/Makefile.objs         |    1 +
 hw/net/can/xlnx-zynqmp-can.c     | 1106 ++++++++++++++++++++++++++++++++++++++
 include/hw/net/xlnx-zynqmp-can.h |   77 +++
 3 files changed, 1184 insertions(+)
 create mode 100644 hw/net/can/xlnx-zynqmp-can.c
 create mode 100644 include/hw/net/xlnx-zynqmp-can.h

diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs
index 9f0c4ee..0fe87dd 100644
--- a/hw/net/can/Makefile.objs
+++ b/hw/net/can/Makefile.objs
@@ -2,3 +2,4 @@ common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
 common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
 common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
 common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
+common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-can.o
diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c
new file mode 100644
index 0000000..e14013c
--- /dev/null
+++ b/hw/net/can/xlnx-zynqmp-can.c
@@ -0,0 +1,1106 @@
+/*
+ * QEMU model of the Xilinx CAN device.
+ *
+ * Copyright (c) 2019 Xilinx Inc.
+ * Partially autogenerated by xregqemu.py 2019-06-20.
+ *
+ * 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"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * 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 OTHER
+ * 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"
+
+#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
+#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT(...) do { \
+    if (XLNX_ZYNQMP_CAN_ERR_DEBUG) { \
+        qemu_log(__VA_ARGS__); \
+    } \
+} while (0)
+
+#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(XlnxZynqMPCAN *s)
+{
+    unsigned int 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) >= 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 = s->regs[R_INTERRUPT_STATUS_REGISTER];
+    irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER];
+
+    qemu_set_irq(s->irq, irq);
+}
+
+static void can_ier_post_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+    can_update_irq(s);
+}
+
+static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val;
+    can_update_irq(s);
+
+    return 0;
+}
+
+static void can_config_reset(XlnxZynqMPCAN *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_REGISTER]);
+    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(XlnxZynqMPCAN *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(XlnxZynqMPCAN *s)
+{
+    /* Wake up interrupt bit. */
+    bool wakeup_irq_val = (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER,
+                            SLEEP) == 0) && ARRAY_FIELD_EX32(s->regs,
+                            STATUS_REGISTER, SLEEP);
+
+    /* Sleep interrupt bit. */
+    bool sleep_irq_val = (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER,
+                            SLEEP) && (ARRAY_FIELD_EX32(s->regs,
+                            STATUS_REGISTER, SLEEP) == 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(XlnxZynqMPCAN *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 = data[0];
+    frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC);
+
+    frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
+    frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
+    frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
+    frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
+
+    frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
+    frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
+    frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
+    frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
+}
+
+static bool tx_ready_check(XlnxZynqMPCAN *s)
+{
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
+                      " XlnxZynqMPCAN%d is in reset mode\n", s->cfg.ctrl_idx);
+        return false;
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
+                      " XlnxZynqMPCAN%d is in configuration mode.Reset the"
+                      " core so operations can start fresh\n",
+                      s->cfg.ctrl_idx);
+        return false;
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
+                        " XlnxZynqMPCAN%d is in SNOOP MODE\n",
+                         s->cfg.ctrl_idx);
+        return false;
+    }
+
+    return true;
+}
+
+static void transfer_fifo(XlnxZynqMPCAN *s, Fifo32 *fifo)
+{
+    qemu_can_frame frame;
+    uint32_t data[CAN_FRAME_SIZE];
+    int i;
+    bool can_tx = tx_ready_check(s);
+
+    if (can_tx) {
+        while (!fifo32_is_empty(fifo)) {
+            for (i = 0; i < CAN_FRAME_SIZE; i++) {
+                data[i] = fifo32_pop(fifo);
+            }
+
+            if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
+                /*
+                 * Controller in loopback. In Loopback mode, the XlnxZynqMPCAN
+                 * 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)) {
+                    DB_PRINT("Loopback: RX FIFO is full."
+                             "TX FIFO will be flushed.\n");
+
+                    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
+                                      RXOFLW, 1);
+                } else {
+                    for (i = 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);
+
+                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);
+        }
+    } else {
+        DB_PRINT("CAN is not enabled for data transfer.\n");
+    }
+
+    can_update_irq(s);
+}
+
+static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
+                        FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
+
+    if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
+        DB_PRINT("Resetting XlnxZynqMPCAN%d\n", s->cfg.ctrl_idx);
+
+        /* First, core will do software reset then will enter in config mode. */
+        can_config_reset(s);
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 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 regsiter
+         * states.
+         */
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
+
+        /* XlnxZynqMP CAN 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 val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+    uint8_t multi_mode = 0;
+
+    /*
+     * Multiple mode set check. This is done to make sure user doesn't set
+     * multiple modes.
+     */
+    multi_mode = 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) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure several modes "
+                     "simultaneously. One mode will be selected according to "
+                     "their priority: LBACK > SLEEP > SNOOP.\n ");
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
+        /* We are in configuration mode, any mode can be selected. */
+        s->regs[R_MODE_SELECT_REGISTER] = val;
+    } else {
+        bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP);
+
+        ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit);
+
+        if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK mode "
+                          "without setting CEN bit as 0\n");
+        } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP mode "
+                              "without setting CEN bit as 0\n");
+        }
+        update_status_register_mode_bits(s);
+    }
+    return s->regs[R_MODE_SELECT_REGISTER];
+}
+
+static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    /* Only allow writes when in config mode. */
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+        val = s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
+    }
+
+    return val;
+}
+
+static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    /* Only allow writes when in config mode. */
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+        val = s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
+    }
+
+    return val;
+}
+
+static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
+        s->rx_time_stamp = 0;
+    }
+
+    return 0;
+}
+
+static void update_rx_fifo(XlnxZynqMPCAN *s, const qemu_can_frame *frame)
+{
+    uint32_t filter_pass = 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 = 1;
+    }
+
+    /*
+     * 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 = s->regs[R_AFMR1] & frame->can_id;
+        uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];
+
+        if (filter_id_masked == id_masked) {
+            filter_pass = 1;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
+        uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
+        uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];
+
+        if (filter_id_masked == id_masked) {
+            filter_pass = 1;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
+        uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
+        uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];
+
+        if (filter_id_masked == id_masked) {
+            filter_pass = 1;
+        }
+    }
+
+    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
+        uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
+        uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];
+
+        if (filter_id_masked == id_masked) {
+            filter_pass = 1;
+        }
+    }
+
+    /* Store the message in fifo if it passed through any of the filters. */
+    if (filter_pass && frame->can_dlc <= MAX_DLC) {
+
+        if (fifo32_is_full(&s->rx_fifo)) {
+            DB_PRINT("RX FIFO is full.\n");
+
+            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
+        } else {
+            s->rx_time_stamp += 1;
+
+            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,
+                                                s->rx_time_stamp)));
+
+            /* 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);
+        }
+
+        can_update_irq(s);
+
+    } else {
+        DB_PRINT("Message didn't pass through any filter"
+                  "or dlc is not in range\n");
+    }
+}
+
+static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t r = 0;
+
+    if (!fifo32_is_empty(&s->rx_fifo)) {
+        r = fifo32_pop(&s->rx_fifo);
+    } else {
+        DB_PRINT("No message in RXFIFO\n");
+
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1);
+    }
+
+    can_update_irq(s);
+    return r;
+}
+
+static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = 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 val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t reg_idx = (reg->access->addr) / 4;
+    uint32_t val = val64;
+    uint32_t filter_number = (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] = val;
+    } else {
+        DB_PRINT("Acceptance filter %d mask is not set as it's corresponding "
+                 "UAF bit is not set to 0\n", filter_number + 1);
+    }
+
+    return s->regs[reg_idx];
+}
+
+static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t reg_idx = (reg->access->addr) / 4;
+    uint32_t val = val64;
+    uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
+
+    if (!(s->regs[R_AFR] & (1 << filter_number))) {
+        s->regs[reg_idx] = val;
+    } else {
+        DB_PRINT("Acceptance filter %d id is not set as it's corresponding "
+                 "UAF bit is not set to 0\n", filter_number + 1);
+    }
+
+    return s->regs[reg_idx];
+}
+
+static void can_tx_post_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
+    uint32_t val = val64;
+
+    bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2;
+
+    bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) ||
+                             (reg->access->addr == A_TXHPB_DATA2);
+
+    Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
+
+    DB_PRINT("TX FIFO write for CAN%d\n", s->cfg.ctrl_idx);
+
+    if (!fifo32_is_full(f)) {
+        fifo32_push(f, val);
+    } else {
+        DB_PRINT("TX FIFO is full.\n");
+    }
+
+    /* 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[] = {
+    {   .name = "SOFTWARE_RESET_REGISTER",
+        .addr = A_SOFTWARE_RESET_REGISTER,
+        .rsvd = 0xfffffffc,
+        .pre_write = can_srr_pre_write,
+    },{ .name = "MODE_SELECT_REGISTER",
+        .addr = A_MODE_SELECT_REGISTER,
+        .rsvd = 0xfffffff8,
+        .pre_write = can_msr_pre_write,
+    },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
+        .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
+        .rsvd = 0xffffff00,
+        .pre_write = can_brpr_pre_write,
+    },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
+        .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
+        .rsvd = 0xfffffe00,
+        .pre_write = can_btr_pre_write,
+    },{ .name = "ERROR_COUNTER_REGISTER",
+        .addr = A_ERROR_COUNTER_REGISTER,
+        .rsvd = 0xffff0000,
+        .ro = 0xffffffff,
+    },{ .name = "ERROR_STATUS_REGISTER",
+        .addr = A_ERROR_STATUS_REGISTER,
+        .rsvd = 0xffffffe0,
+        .w1c = 0x1f,
+    },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER,
+        .reset = 0x1,
+        .rsvd = 0xffffe000,
+        .ro = 0x1fff,
+    },{ .name = "INTERRUPT_STATUS_REGISTER",
+        .addr = A_INTERRUPT_STATUS_REGISTER,
+        .reset = 0x6000,
+        .rsvd = 0xffff8000,
+        .ro = 0x7fff,
+    },{ .name = "INTERRUPT_ENABLE_REGISTER",
+        .addr = A_INTERRUPT_ENABLE_REGISTER,
+        .rsvd = 0xffff8000,
+        .post_write = can_ier_post_write,
+    },{ .name = "INTERRUPT_CLEAR_REGISTER",
+        .addr = A_INTERRUPT_CLEAR_REGISTER,
+        .rsvd = 0xffff8000,
+        .pre_write = can_icr_pre_write,
+    },{ .name = "TIMESTAMP_REGISTER",
+        .addr = A_TIMESTAMP_REGISTER,
+        .rsvd = 0xfffffffe,
+        .pre_write = can_tcr_pre_write,
+    },{ .name = "WIR",  .addr = A_WIR,
+        .reset = 0x3f3f,
+        .rsvd = 0xffff0000,
+    },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC,
+        .rsvd = 0xfffffff,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC,
+        .rsvd = 0xfffffff,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1,
+        .post_write = can_tx_post_write,
+    },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2,
+        .post_write = can_tx_post_write,
+    },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID,
+        .ro = 0xffffffff,
+        .post_read = can_rxfifo_pre_read,
+    },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC,
+        .rsvd = 0xfff0000,
+        .post_read = can_rxfifo_pre_read,
+    },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1,
+        .post_read = can_rxfifo_pre_read,
+    },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2,
+        .post_read = can_rxfifo_pre_read,
+    },{ .name = "AFR",  .addr = A_AFR,
+        .rsvd = 0xfffffff0,
+        .post_write = can_filter_enable_post_write,
+    },{ .name = "AFMR1",  .addr = A_AFMR1,
+        .pre_write = can_filter_mask_pre_write,
+    },{ .name = "AFIR1",  .addr = A_AFIR1,
+        .pre_write = can_filter_id_pre_write,
+    },{ .name = "AFMR2",  .addr = A_AFMR2,
+        .pre_write = can_filter_mask_pre_write,
+    },{ .name = "AFIR2",  .addr = A_AFIR2,
+        .pre_write = can_filter_id_pre_write,
+    },{ .name = "AFMR3",  .addr = A_AFMR3,
+        .pre_write = can_filter_mask_pre_write,
+    },{ .name = "AFIR3",  .addr = A_AFIR3,
+        .pre_write = can_filter_id_pre_write,
+    },{ .name = "AFMR4",  .addr = A_AFMR4,
+        .pre_write = can_filter_mask_pre_write,
+    },{ .name = "AFIR4",  .addr = A_AFIR4,
+        .pre_write = can_filter_id_pre_write,
+    }
+};
+
+static const MemoryRegionOps can_ops = {
+    .read = register_read_memory,
+    .write = register_write_memory,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void xlnx_zynqmp_can_reset(DeviceState *dev)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++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 int xlnx_zynqmp_can_can_receive(CanBusClientState *client)
+{
+    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        DB_PRINT("XlnxZynqMPCAN%d Controller is reset\n", s->cfg.ctrl_idx);
+        return 0;
+    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
+        DB_PRINT("XlnxZynqMPCAN%d is disabled. Messages will be discarded\n",
+                s->cfg.ctrl_idx);
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
+                               const qemu_can_frame *buf, size_t buf_size) {
+    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
+    const qemu_can_frame *frame = buf;
+
+    DB_PRINT("Incoming data for CAN%d\n", s->cfg.ctrl_idx);
+
+    if (buf_size <= 0) {
+        DB_PRINT("junk data received on XlnxZynqMPCAN bus\n");
+        return 0;
+    }
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
+        /*
+         * XlnxZynqMPCAN will not participate in normal bus communication and
+         * does not receive any messages transmitted by other CAN nodes.
+         */
+        DB_PRINT("XlnxZynqMPCAN is in loopback mode."
+                " It will not receive data.\n");
+
+    } else 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 to wake
+         * up state.
+         */
+        can_exit_sleep_mode(s);
+        update_rx_fifo(s, frame);
+    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
+        update_rx_fifo(s, frame);
+    } else {
+        DB_PRINT("Can't receive data as XlnxZynqMPCAN is not set correctly.\n");
+    }
+
+    return 1;
+}
+
+static CanBusClientInfo can_xilinx_bus_client_info = {
+    .can_receive = xlnx_zynqmp_can_can_receive,
+    .receive = xlnx_zynqmp_can_receive,
+};
+
+static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCAN *s, CanBusState *bus)
+{
+    s->bus_client.info = &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)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
+
+    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
+        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCAN controller "
+                    "index", s->cfg.ctrl_idx);
+        return;
+    }
+
+    if (s->canbus[s->cfg.ctrl_idx]) {
+        if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus[s->cfg.ctrl_idx]) < 0) {
+            error_setg(errp, "xlnx_zynqmp_can_connect_to_bus failed");
+        }
+
+    } else {
+        /* If no bus is set. */
+        DB_PRINT("Canbus%d property is not set for xlnxCAN%d\n",
+                    s->cfg.ctrl_idx, s->cfg.ctrl_idx);
+    }
+
+    /* 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);
+}
+
+static void xlnx_zynqmp_can_init(Object *obj)
+{
+    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(obj);
+    SysBusDevice *sbd = 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 = 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);
+
+    object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+                             (Object **)&s->canbus[0],
+                             qdev_prop_allow_set_link_before_realize,
+                             0, &error_abort);
+
+    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+                             (Object **)&s->canbus[1],
+                             qdev_prop_allow_set_link_before_realize,
+                             0, &error_abort);
+}
+
+static const VMStateDescription vmstate_can = {
+    .name = TYPE_XLNX_ZYNQMP_CAN,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCAN),
+        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCAN, XLNX_ZYNQMP_CAN_R_MAX),
+        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCAN),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property xlnx_zynqmp_can_properties[] = {
+    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCAN, cfg.ctrl_idx, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->reset = xlnx_zynqmp_can_reset;
+    dc->realize = xlnx_zynqmp_can_realize;
+    device_class_set_props(dc, xlnx_zynqmp_can_properties);
+    dc->vmsd = &vmstate_can;
+}
+
+static const TypeInfo can_info = {
+    .name          = TYPE_XLNX_ZYNQMP_CAN,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XlnxZynqMPCAN),
+    .class_init    = xlnx_zynqmp_can_class_init,
+    .instance_init = xlnx_zynqmp_can_init,
+};
+
+static void can_register_types(void)
+{
+    type_register_static(&can_info);
+}
+
+type_init(can_register_types)
diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-zynqmp-can.h
new file mode 100644
index 0000000..537dbc2
--- /dev/null
+++ b/include/hw/net/xlnx-zynqmp-can.h
@@ -0,0 +1,77 @@
+/*
+ * QEMU model of the Xilinx CAN device.
+ *
+ * Copyright (c) 2019 Xilinx Inc.
+ * Partially autogenerated by xregqemu.py 2019-06-20.
+ *
+ * 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"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * 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 OTHER
+ * 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 HW_CAN_XILINX_H
+#define HW_CAN_XILINX_H
+
+#include "hw/register.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+#include "qemu/fifo32.h"
+
+#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
+
+#define XLNX_ZYNQMP_CAN(obj) \
+     OBJECT_CHECK(XlnxZynqMPCAN, (obj), TYPE_XLNX_ZYNQMP_CAN)
+
+#define MAX_CAN_CTRLS      2
+#define XLNX_ZYNQMP_CAN_R_MAX     (0x84 / 4)
+#define MAILBOX_CAPACITY   64
+
+/* Each CAN_FRAME will have 4 * 32bit size. */
+#define CAN_FRAME_SIZE     4
+#define RXFIFO_SIZE        (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
+
+
+typedef struct XlnxZynqMPCAN {
+    SysBusDevice        parent_obj;
+    MemoryRegion        iomem;
+
+    qemu_irq            irq;
+
+    CanBusClientState   bus_client;
+    CanBusState         *canbus[MAX_CAN_CTRLS];
+
+    struct {
+        uint8_t         ctrl_idx;
+   } cfg;
+
+    RegisterInfo        reg_info[XLNX_ZYNQMP_CAN_R_MAX];
+    uint32_t            regs[XLNX_ZYNQMP_CAN_R_MAX];
+
+    uint16_t            rx_time_stamp;
+
+    Fifo32                rx_fifo;
+    Fifo32                tx_fifo;
+    Fifo32                txhpb_fifo;
+
+} XlnxZynqMPCAN;
+
+#endif
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-03 21:06 [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU Vikram Garhwal
  2020-02-03 21:06 ` [PATCH 1/1] hw/net/can: " Vikram Garhwal
@ 2020-02-05  3:17 ` Jason Wang
  2020-02-05  3:23   ` Vikram Garhwal
  1 sibling, 1 reply; 12+ messages in thread
From: Jason Wang @ 2020-02-05  3:17 UTC (permalink / raw)
  To: Vikram Garhwal, qemu-devel


On 2020/2/4 上午5:06, Vikram Garhwal wrote:
> Example for single CAN:
>      -object can-bus,id=canbus0 \
>      -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
>      -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0
>
> Example for connecting both CAN:
>      -object can-bus,id=canbus0 -object can-bus,id=canbus1 \
>      -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
>      -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus1 \
>      -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0 \
>      -object can-host-socketcan,id=socketcan1,if=vcan0,canbus=canbus1
>
> Vikram Garhwal (1):
>    hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
>
>   hw/net/can/Makefile.objs         |    1 +
>   hw/net/can/xlnx-zynqmp-can.c     | 1106 ++++++++++++++++++++++++++++++++++++++
>   include/hw/net/xlnx-zynqmp-can.h |   77 +++
>   3 files changed, 1184 insertions(+)
>   create mode 100644 hw/net/can/xlnx-zynqmp-can.c
>   create mode 100644 include/hw/net/xlnx-zynqmp-can.h
>

Hi Vikram:

I would like to confirm that whether or not you would maintain this NIC. 
(I think the answer is yes). I tend not to merge a NIC model without a 
maintainer.

Thanks



^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-05  3:17 ` [PATCH 0/1] " Jason Wang
@ 2020-02-05  3:23   ` Vikram Garhwal
  2020-02-05  5:48     ` Jason Wang
  0 siblings, 1 reply; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-05  3:23 UTC (permalink / raw)
  To: Jason Wang, qemu-devel

Hi Jason,
Yes, I will maintain this NIC. Thanks for the help.

Regards,
Vikram

> -----Original Message-----
> From: Jason Wang <jasowang@redhat.com>
> Sent: Tuesday, February 4, 2020 7:17 PM
> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> Subject: Re: [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
> 
> 
> On 2020/2/4 上午5:06, Vikram Garhwal wrote:
> > Example for single CAN:
> >      -object can-bus,id=canbus0 \
> >      -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
> >      -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0
> >
> > Example for connecting both CAN:
> >      -object can-bus,id=canbus0 -object can-bus,id=canbus1 \
> >      -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
> >      -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus1 \
> >      -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0 \
> >      -object can-host-socketcan,id=socketcan1,if=vcan0,canbus=canbus1
> >
> > Vikram Garhwal (1):
> >    hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
> >
> >   hw/net/can/Makefile.objs         |    1 +
> >   hw/net/can/xlnx-zynqmp-can.c     | 1106
> ++++++++++++++++++++++++++++++++++++++
> >   include/hw/net/xlnx-zynqmp-can.h |   77 +++
> >   3 files changed, 1184 insertions(+)
> >   create mode 100644 hw/net/can/xlnx-zynqmp-can.c
> >   create mode 100644 include/hw/net/xlnx-zynqmp-can.h
> >
> 
> Hi Vikram:
> 
> I would like to confirm that whether or not you would maintain this NIC.
> (I think the answer is yes). I tend not to merge a NIC model without a
> maintainer.
> 
> Thanks


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-03 21:06 ` [PATCH 1/1] hw/net/can: " Vikram Garhwal
@ 2020-02-05  5:46   ` Jason Wang
  2020-02-06 18:39     ` Vikram Garhwal
  2020-02-10 21:45     ` Vikram Garhwal
  0 siblings, 2 replies; 12+ messages in thread
From: Jason Wang @ 2020-02-05  5:46 UTC (permalink / raw)
  To: Vikram Garhwal, qemu-devel


On 2020/2/4 上午5:06, Vikram Garhwal wrote:
> XlnxCAN is developed based on SocketCAN, QEMU CAN bus implementation.
> Bus connection and socketCAN connection for each CAN module can be set
> through command lines.
>
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
>   hw/net/can/Makefile.objs         |    1 +
>   hw/net/can/xlnx-zynqmp-can.c     | 1106 ++++++++++++++++++++++++++++++++++++++
>   include/hw/net/xlnx-zynqmp-can.h |   77 +++
>   3 files changed, 1184 insertions(+)
>   create mode 100644 hw/net/can/xlnx-zynqmp-can.c
>   create mode 100644 include/hw/net/xlnx-zynqmp-can.h
>
> diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs
> index 9f0c4ee..0fe87dd 100644
> --- a/hw/net/can/Makefile.objs
> +++ b/hw/net/can/Makefile.objs
> @@ -2,3 +2,4 @@ common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
>   common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
>   common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
>   common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
> +common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-can.o
> diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c
> new file mode 100644
> index 0000000..e14013c
> --- /dev/null
> +++ b/hw/net/can/xlnx-zynqmp-can.c
> @@ -0,0 +1,1106 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2019 Xilinx Inc.


2020?


> + * Partially autogenerated by xregqemu.py


What did xregqemu.py do? Is it possible to integrate it into qemu source 
then we can review high level language other than c?


> 2019-06-20.
> + *
> + * 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"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * 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 OTHER
> + * 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"
> +
> +#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
> +#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
> +#endif
> +
> +#define DB_PRINT(...) do { \
> +    if (XLNX_ZYNQMP_CAN_ERR_DEBUG) { \
> +        qemu_log(__VA_ARGS__); \
> +    } \
> +} while (0)
> +
> +#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(XlnxZynqMPCAN *s)
> +{
> +    unsigned int 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) >= 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 = s->regs[R_INTERRUPT_STATUS_REGISTER];
> +    irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER];
> +
> +    qemu_set_irq(s->irq, irq);
> +}
> +
> +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +
> +    can_update_irq(s);
> +}
> +
> +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val;
> +    can_update_irq(s);
> +
> +    return 0;
> +}
> +
> +static void can_config_reset(XlnxZynqMPCAN *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_REGISTER]);
> +    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(XlnxZynqMPCAN *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(XlnxZynqMPCAN *s)
> +{
> +    /* Wake up interrupt bit. */
> +    bool wakeup_irq_val = (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER,
> +                            SLEEP) == 0) && ARRAY_FIELD_EX32(s->regs,
> +                            STATUS_REGISTER, SLEEP);
> +
> +    /* Sleep interrupt bit. */
> +    bool sleep_irq_val = (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER,
> +                            SLEEP) && (ARRAY_FIELD_EX32(s->regs,
> +                            STATUS_REGISTER, SLEEP) == 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(XlnxZynqMPCAN *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 = data[0];
> +    frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC);
> +
> +    frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
> +    frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
> +    frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
> +    frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
> +
> +    frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
> +    frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
> +    frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
> +    frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
> +}
> +
> +static bool tx_ready_check(XlnxZynqMPCAN *s)
> +{
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
> +                      " XlnxZynqMPCAN%d is in reset mode\n", s->cfg.ctrl_idx);
> +        return false;
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
> +                      " XlnxZynqMPCAN%d is in configuration mode.Reset the"
> +                      " core so operations can start fresh\n",
> +                      s->cfg.ctrl_idx);
> +        return false;
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
> +                        " XlnxZynqMPCAN%d is in SNOOP MODE\n",
> +                         s->cfg.ctrl_idx);
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +static void transfer_fifo(XlnxZynqMPCAN *s, Fifo32 *fifo)
> +{
> +    qemu_can_frame frame;
> +    uint32_t data[CAN_FRAME_SIZE];
> +    int i;
> +    bool can_tx = tx_ready_check(s);
> +
> +    if (can_tx) {
> +        while (!fifo32_is_empty(fifo)) {
> +            for (i = 0; i < CAN_FRAME_SIZE; i++) {
> +                data[i] = fifo32_pop(fifo);
> +            }
> +
> +            if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> +                /*
> +                 * Controller in loopback. In Loopback mode, the XlnxZynqMPCAN
> +                 * 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)) {
> +                    DB_PRINT("Loopback: RX FIFO is full."
> +                             "TX FIFO will be flushed.\n");
> +
> +                    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
> +                                      RXOFLW, 1);
> +                } else {
> +                    for (i = 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);
> +
> +                can_bus_client_send(&s->bus_client, &frame, 1);


I had a quick glance at can_bus_client_send():

It did:

     QTAILQ_FOREACH(peer, &bus->clients, next) {
         if (peer->info->can_receive(peer)) {
             if (peer == client) {
                 /* No loopback support for now */
                 continue;
             }
             if (peer->info->receive(peer, frames, frames_cnt) > 0) {
                 ret = 1;
             }
         }
     }

which looks not correct. We need to use qemu_send_packet() instead of 
calling peer->info->receive() directly which bypasses filters completely.


> +            }
> +        }
> +
> +        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);
> +        }
> +    } else {
> +        DB_PRINT("CAN is not enabled for data transfer.\n");
> +    }
> +
> +    can_update_irq(s);
> +}
> +
> +static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
> +                        FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
> +
> +    if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
> +        DB_PRINT("Resetting XlnxZynqMPCAN%d\n", s->cfg.ctrl_idx);
> +
> +        /* First, core will do software reset then will enter in config mode. */
> +        can_config_reset(s);
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 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 regsiter
> +         * states.
> +         */
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> +
> +        /* XlnxZynqMP CAN 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 val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +    uint8_t multi_mode = 0;
> +
> +    /*
> +     * Multiple mode set check. This is done to make sure user doesn't set
> +     * multiple modes.
> +     */
> +    multi_mode = 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) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure several modes "
> +                     "simultaneously. One mode will be selected according to "
> +                     "their priority: LBACK > SLEEP > SNOOP.\n ");
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> +        /* We are in configuration mode, any mode can be selected. */
> +        s->regs[R_MODE_SELECT_REGISTER] = val;
> +    } else {
> +        bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP);
> +
> +        ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, sleep_mode_bit);
> +
> +        if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK mode "
> +                          "without setting CEN bit as 0\n");
> +        } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP mode "
> +                              "without setting CEN bit as 0\n");
> +        }
> +        update_status_register_mode_bits(s);
> +    }
> +    return s->regs[R_MODE_SELECT_REGISTER];
> +}
> +
> +static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    /* Only allow writes when in config mode. */
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> +        val = s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
> +    }
> +
> +    return val;
> +}
> +
> +static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    /* Only allow writes when in config mode. */
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> +        val = s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
> +    }
> +
> +    return val;
> +}
> +
> +static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
> +        s->rx_time_stamp = 0;
> +    }
> +
> +    return 0;
> +}
> +
> +static void update_rx_fifo(XlnxZynqMPCAN *s, const qemu_can_frame *frame)
> +{
> +    uint32_t filter_pass = 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 = 1;


Any reason for not doing such filtering in xlnx_zynqmp_can_can_receive()?


> +    }
> +
> +    /*
> +     * 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 = s->regs[R_AFMR1] & frame->can_id;
> +        uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];
> +
> +        if (filter_id_masked == id_masked) {
> +            filter_pass = 1;
> +        }
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
> +        uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
> +        uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];
> +
> +        if (filter_id_masked == id_masked) {
> +            filter_pass = 1;
> +        }
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
> +        uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
> +        uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];
> +
> +        if (filter_id_masked == id_masked) {
> +            filter_pass = 1;
> +        }
> +    }
> +
> +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
> +        uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
> +        uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];
> +
> +        if (filter_id_masked == id_masked) {
> +            filter_pass = 1;
> +        }
> +    }
> +
> +    /* Store the message in fifo if it passed through any of the filters. */
> +    if (filter_pass && frame->can_dlc <= MAX_DLC) {
> +
> +        if (fifo32_is_full(&s->rx_fifo)) {
> +            DB_PRINT("RX FIFO is full.\n");
> +
> +            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
> +        } else {
> +            s->rx_time_stamp += 1;
> +
> +            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,
> +                                                s->rx_time_stamp)));
> +
> +            /* 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);
> +        }
> +
> +        can_update_irq(s);
> +
> +    } else {
> +        DB_PRINT("Message didn't pass through any filter"
> +                  "or dlc is not in range\n");
> +    }
> +}
> +
> +static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t r = 0;
> +
> +    if (!fifo32_is_empty(&s->rx_fifo)) {
> +        r = fifo32_pop(&s->rx_fifo);
> +    } else {
> +        DB_PRINT("No message in RXFIFO\n");
> +
> +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1);
> +    }
> +
> +    can_update_irq(s);
> +    return r;
> +}
> +
> +static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = 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 val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t reg_idx = (reg->access->addr) / 4;
> +    uint32_t val = val64;
> +    uint32_t filter_number = (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] = val;
> +    } else {
> +        DB_PRINT("Acceptance filter %d mask is not set as it's corresponding "
> +                 "UAF bit is not set to 0\n", filter_number + 1);
> +    }
> +
> +    return s->regs[reg_idx];
> +}
> +
> +static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t reg_idx = (reg->access->addr) / 4;
> +    uint32_t val = val64;
> +    uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
> +
> +    if (!(s->regs[R_AFR] & (1 << filter_number))) {
> +        s->regs[reg_idx] = val;
> +    } else {
> +        DB_PRINT("Acceptance filter %d id is not set as it's corresponding "
> +                 "UAF bit is not set to 0\n", filter_number + 1);
> +    }
> +
> +    return s->regs[reg_idx];
> +}
> +
> +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +
> +    bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2;
> +
> +    bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) ||
> +                             (reg->access->addr == A_TXHPB_DATA2);
> +
> +    Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
> +
> +    DB_PRINT("TX FIFO write for CAN%d\n", s->cfg.ctrl_idx);
> +
> +    if (!fifo32_is_full(f)) {
> +        fifo32_push(f, val);
> +    } else {
> +        DB_PRINT("TX FIFO is full.\n");
> +    }
> +
> +    /* 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[] = {
> +    {   .name = "SOFTWARE_RESET_REGISTER",
> +        .addr = A_SOFTWARE_RESET_REGISTER,
> +        .rsvd = 0xfffffffc,
> +        .pre_write = can_srr_pre_write,
> +    },{ .name = "MODE_SELECT_REGISTER",
> +        .addr = A_MODE_SELECT_REGISTER,
> +        .rsvd = 0xfffffff8,
> +        .pre_write = can_msr_pre_write,
> +    },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
> +        .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
> +        .rsvd = 0xffffff00,
> +        .pre_write = can_brpr_pre_write,
> +    },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
> +        .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
> +        .rsvd = 0xfffffe00,
> +        .pre_write = can_btr_pre_write,
> +    },{ .name = "ERROR_COUNTER_REGISTER",
> +        .addr = A_ERROR_COUNTER_REGISTER,
> +        .rsvd = 0xffff0000,
> +        .ro = 0xffffffff,
> +    },{ .name = "ERROR_STATUS_REGISTER",
> +        .addr = A_ERROR_STATUS_REGISTER,
> +        .rsvd = 0xffffffe0,
> +        .w1c = 0x1f,
> +    },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER,
> +        .reset = 0x1,
> +        .rsvd = 0xffffe000,
> +        .ro = 0x1fff,
> +    },{ .name = "INTERRUPT_STATUS_REGISTER",
> +        .addr = A_INTERRUPT_STATUS_REGISTER,
> +        .reset = 0x6000,
> +        .rsvd = 0xffff8000,
> +        .ro = 0x7fff,
> +    },{ .name = "INTERRUPT_ENABLE_REGISTER",
> +        .addr = A_INTERRUPT_ENABLE_REGISTER,
> +        .rsvd = 0xffff8000,
> +        .post_write = can_ier_post_write,
> +    },{ .name = "INTERRUPT_CLEAR_REGISTER",
> +        .addr = A_INTERRUPT_CLEAR_REGISTER,
> +        .rsvd = 0xffff8000,
> +        .pre_write = can_icr_pre_write,
> +    },{ .name = "TIMESTAMP_REGISTER",
> +        .addr = A_TIMESTAMP_REGISTER,
> +        .rsvd = 0xfffffffe,
> +        .pre_write = can_tcr_pre_write,
> +    },{ .name = "WIR",  .addr = A_WIR,
> +        .reset = 0x3f3f,
> +        .rsvd = 0xffff0000,
> +    },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC,
> +        .rsvd = 0xfffffff,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC,
> +        .rsvd = 0xfffffff,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2,
> +        .post_write = can_tx_post_write,
> +    },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID,
> +        .ro = 0xffffffff,
> +        .post_read = can_rxfifo_pre_read,
> +    },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC,
> +        .rsvd = 0xfff0000,
> +        .post_read = can_rxfifo_pre_read,
> +    },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1,
> +        .post_read = can_rxfifo_pre_read,
> +    },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2,
> +        .post_read = can_rxfifo_pre_read,
> +    },{ .name = "AFR",  .addr = A_AFR,
> +        .rsvd = 0xfffffff0,
> +        .post_write = can_filter_enable_post_write,
> +    },{ .name = "AFMR1",  .addr = A_AFMR1,
> +        .pre_write = can_filter_mask_pre_write,
> +    },{ .name = "AFIR1",  .addr = A_AFIR1,
> +        .pre_write = can_filter_id_pre_write,
> +    },{ .name = "AFMR2",  .addr = A_AFMR2,
> +        .pre_write = can_filter_mask_pre_write,
> +    },{ .name = "AFIR2",  .addr = A_AFIR2,
> +        .pre_write = can_filter_id_pre_write,
> +    },{ .name = "AFMR3",  .addr = A_AFMR3,
> +        .pre_write = can_filter_mask_pre_write,
> +    },{ .name = "AFIR3",  .addr = A_AFIR3,
> +        .pre_write = can_filter_id_pre_write,
> +    },{ .name = "AFMR4",  .addr = A_AFMR4,
> +        .pre_write = can_filter_mask_pre_write,
> +    },{ .name = "AFIR4",  .addr = A_AFIR4,
> +        .pre_write = can_filter_id_pre_write,
> +    }
> +};
> +
> +static const MemoryRegionOps can_ops = {
> +    .read = register_read_memory,
> +    .write = register_write_memory,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void xlnx_zynqmp_can_reset(DeviceState *dev)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++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 int xlnx_zynqmp_can_can_receive(CanBusClientState *client)
> +{
> +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        DB_PRINT("XlnxZynqMPCAN%d Controller is reset\n", s->cfg.ctrl_idx);
> +        return 0;
> +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
> +        DB_PRINT("XlnxZynqMPCAN%d is disabled. Messages will be discarded\n",
> +                s->cfg.ctrl_idx);
> +        return 0;
> +    } else {
> +        return 1;
> +    }
> +}
> +
> +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> +                               const qemu_can_frame *buf, size_t buf_size) {
> +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> +    const qemu_can_frame *frame = buf;
> +
> +    DB_PRINT("Incoming data for CAN%d\n", s->cfg.ctrl_idx);
> +
> +    if (buf_size <= 0) {
> +        DB_PRINT("junk data received on XlnxZynqMPCAN bus\n");
> +        return 0;
> +    }
> +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> +        /*
> +         * XlnxZynqMPCAN will not participate in normal bus communication and
> +         * does not receive any messages transmitted by other CAN nodes.
> +         */
> +        DB_PRINT("XlnxZynqMPCAN is in loopback mode."
> +                " It will not receive data.\n");
> +
> +    } else 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 to wake
> +         * up state.
> +         */
> +        can_exit_sleep_mode(s);
> +        update_rx_fifo(s, frame);
> +    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
> +        update_rx_fifo(s, frame);
> +    } else {
> +        DB_PRINT("Can't receive data as XlnxZynqMPCAN is not set correctly.\n");
> +    }
> +
> +    return 1;
> +}
> +
> +static CanBusClientInfo can_xilinx_bus_client_info = {
> +    .can_receive = xlnx_zynqmp_can_can_receive,
> +    .receive = xlnx_zynqmp_can_receive,
> +};
> +
> +static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCAN *s, CanBusState *bus)
> +{
> +    s->bus_client.info = &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)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> +
> +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCAN controller "
> +                    "index", s->cfg.ctrl_idx);
> +        return;
> +    }
> +
> +    if (s->canbus[s->cfg.ctrl_idx]) {
> +        if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus[s->cfg.ctrl_idx]) < 0) {
> +            error_setg(errp, "xlnx_zynqmp_can_connect_to_bus failed");
> +        }
> +
> +    } else {
> +        /* If no bus is set. */
> +        DB_PRINT("Canbus%d property is not set for xlnxCAN%d\n",
> +                    s->cfg.ctrl_idx, s->cfg.ctrl_idx);
> +    }
> +
> +    /* 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);
> +}
> +
> +static void xlnx_zynqmp_can_init(Object *obj)
> +{
> +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(obj);
> +    SysBusDevice *sbd = 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 = 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);
> +
> +    object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
> +                             (Object **)&s->canbus[0],
> +                             qdev_prop_allow_set_link_before_realize,
> +                             0, &error_abort);
> +
> +    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
> +                             (Object **)&s->canbus[1],
> +                             qdev_prop_allow_set_link_before_realize,
> +                             0, &error_abort);
> +}
> +
> +static const VMStateDescription vmstate_can = {
> +    .name = TYPE_XLNX_ZYNQMP_CAN,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCAN),
> +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCAN, XLNX_ZYNQMP_CAN_R_MAX),
> +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCAN),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static Property xlnx_zynqmp_can_properties[] = {
> +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCAN, cfg.ctrl_idx, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    dc->reset = xlnx_zynqmp_can_reset;
> +    dc->realize = xlnx_zynqmp_can_realize;
> +    device_class_set_props(dc, xlnx_zynqmp_can_properties);
> +    dc->vmsd = &vmstate_can;
> +}
> +
> +static const TypeInfo can_info = {
> +    .name          = TYPE_XLNX_ZYNQMP_CAN,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(XlnxZynqMPCAN),
> +    .class_init    = xlnx_zynqmp_can_class_init,
> +    .instance_init = xlnx_zynqmp_can_init,
> +};
> +
> +static void can_register_types(void)
> +{
> +    type_register_static(&can_info);
> +}
> +
> +type_init(can_register_types)
> diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-zynqmp-can.h
> new file mode 100644
> index 0000000..537dbc2
> --- /dev/null
> +++ b/include/hw/net/xlnx-zynqmp-can.h
> @@ -0,0 +1,77 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2019 Xilinx Inc.
> + * Partially autogenerated by xregqemu.py 2019-06-20.
> + *
> + * 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"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * 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 OTHER
> + * 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 HW_CAN_XILINX_H
> +#define HW_CAN_XILINX_H
> +
> +#include "hw/register.h"
> +#include "net/can_emu.h"
> +#include "net/can_host.h"
> +#include "qemu/fifo32.h"
> +
> +#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
> +
> +#define XLNX_ZYNQMP_CAN(obj) \
> +     OBJECT_CHECK(XlnxZynqMPCAN, (obj), TYPE_XLNX_ZYNQMP_CAN)
> +
> +#define MAX_CAN_CTRLS      2
> +#define XLNX_ZYNQMP_CAN_R_MAX     (0x84 / 4)
> +#define MAILBOX_CAPACITY   64
> +
> +/* Each CAN_FRAME will have 4 * 32bit size. */
> +#define CAN_FRAME_SIZE     4
> +#define RXFIFO_SIZE        (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
> +
> +
> +typedef struct XlnxZynqMPCAN {


For device state we usually have a "State" suffix. So it's better use 
"XlnxZynqMPCANState".

Thanks


> +    SysBusDevice        parent_obj;
> +    MemoryRegion        iomem;
> +
> +    qemu_irq            irq;
> +
> +    CanBusClientState   bus_client;
> +    CanBusState         *canbus[MAX_CAN_CTRLS];
> +
> +    struct {
> +        uint8_t         ctrl_idx;
> +   } cfg;
> +
> +    RegisterInfo        reg_info[XLNX_ZYNQMP_CAN_R_MAX];
> +    uint32_t            regs[XLNX_ZYNQMP_CAN_R_MAX];
> +
> +    uint16_t            rx_time_stamp;
> +
> +    Fifo32                rx_fifo;
> +    Fifo32                tx_fifo;
> +    Fifo32                txhpb_fifo;
> +
> +} XlnxZynqMPCAN;
> +
> +#endif



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-05  3:23   ` Vikram Garhwal
@ 2020-02-05  5:48     ` Jason Wang
  0 siblings, 0 replies; 12+ messages in thread
From: Jason Wang @ 2020-02-05  5:48 UTC (permalink / raw)
  To: Vikram Garhwal, qemu-devel


On 2020/2/5 上午11:23, Vikram Garhwal wrote:
> Hi Jason,
> Yes, I will maintain this NIC. Thanks for the help.
>
> Regards,
> Vikram


Good to know that.

Please update MAINTINERS and it would be better if you can draft a qtest 
for this NIC.

I leave some quick comments for the patch and I hope the patch could be 
reviewed by others as well.

Thanks


>
>> -----Original Message-----
>> From: Jason Wang <jasowang@redhat.com>
>> Sent: Tuesday, February 4, 2020 7:17 PM
>> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
>> Subject: Re: [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU
>>
>>
>> On 2020/2/4 上午5:06, Vikram Garhwal wrote:
>>> Example for single CAN:
>>>       -object can-bus,id=canbus0 \
>>>       -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
>>>       -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0
>>>
>>> Example for connecting both CAN:
>>>       -object can-bus,id=canbus0 -object can-bus,id=canbus1 \
>>>       -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0 \
>>>       -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus1 \
>>>       -object can-host-socketcan,id=socketcan0,if=vcan0,canbus=canbus0 \
>>>       -object can-host-socketcan,id=socketcan1,if=vcan0,canbus=canbus1
>>>
>>> Vikram Garhwal (1):
>>>     hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
>>>
>>>    hw/net/can/Makefile.objs         |    1 +
>>>    hw/net/can/xlnx-zynqmp-can.c     | 1106
>> ++++++++++++++++++++++++++++++++++++++
>>>    include/hw/net/xlnx-zynqmp-can.h |   77 +++
>>>    3 files changed, 1184 insertions(+)
>>>    create mode 100644 hw/net/can/xlnx-zynqmp-can.c
>>>    create mode 100644 include/hw/net/xlnx-zynqmp-can.h
>>>
>> Hi Vikram:
>>
>> I would like to confirm that whether or not you would maintain this NIC.
>> (I think the answer is yes). I tend not to merge a NIC model without a
>> maintainer.
>>
>> Thanks



^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-05  5:46   ` Jason Wang
@ 2020-02-06 18:39     ` Vikram Garhwal
  2020-02-10 21:45     ` Vikram Garhwal
  1 sibling, 0 replies; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-06 18:39 UTC (permalink / raw)
  To: Jason Wang, qemu-devel

Hi Jason,
Thanks for reviewing it. Few comments inline. 
Also, I will add a patch for Maintainer updates when I send v2.

Regards,
Vikram
> -----Original Message-----
> From: Jason Wang <jasowang@redhat.com>
> Sent: Tuesday, February 4, 2020 9:47 PM
> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller
> for QEMU
> 
> 
> On 2020/2/4 上午5:06, Vikram Garhwal wrote:
> > XlnxCAN is developed based on SocketCAN, QEMU CAN bus implementation.
> > Bus connection and socketCAN connection for each CAN module can be set
> > through command lines.
> >
> > Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> > ---
> >   hw/net/can/Makefile.objs         |    1 +
> >   hw/net/can/xlnx-zynqmp-can.c     | 1106
> ++++++++++++++++++++++++++++++++++++++
> >   include/hw/net/xlnx-zynqmp-can.h |   77 +++
> >   3 files changed, 1184 insertions(+)
> >   create mode 100644 hw/net/can/xlnx-zynqmp-can.c
> >   create mode 100644 include/hw/net/xlnx-zynqmp-can.h
> >
> > diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs index
> > 9f0c4ee..0fe87dd 100644
> > --- a/hw/net/can/Makefile.objs
> > +++ b/hw/net/can/Makefile.objs
> > @@ -2,3 +2,4 @@ common-obj-$(CONFIG_CAN_SJA1000) +=
> can_sja1000.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
> > +common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-can.o
> > diff --git a/hw/net/can/xlnx-zynqmp-can.c
> > b/hw/net/can/xlnx-zynqmp-can.c new file mode 100644 index
> > 0000000..e14013c
> > --- /dev/null
> > +++ b/hw/net/can/xlnx-zynqmp-can.c
> > @@ -0,0 +1,1106 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2019 Xilinx Inc.
> 
> 
> 2020?
[Vikram Garhwal] Yeah. I will change it 2020 in V2 patch.
> 
> 
> > + * Partially autogenerated by xregqemu.py
> 
> 
> What did xregqemu.py do? Is it possible to integrate it into qemu source then
> we can review high level language other than c?
[Vikram Garhwal] I guess it's better to remove it as it is creating confusion. 
It was basically a tool(Internal use) which generate register defs.
> 
> 
> > 2019-06-20.
> > + *
> > + * 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"), to
> deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * 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 OTHER
> > + * 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"
> > +
> > +#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
> > +#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
> > +#endif
> > +
> > +#define DB_PRINT(...) do { \
> > +    if (XLNX_ZYNQMP_CAN_ERR_DEBUG) { \
> > +        qemu_log(__VA_ARGS__); \
> > +    } \
> > +} while (0)
> > +
> > +#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(XlnxZynqMPCAN *s)
> > +{
> > +    unsigned int 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) >= 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 = s->regs[R_INTERRUPT_STATUS_REGISTER];
> > +    irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER];
> > +
> > +    qemu_set_irq(s->irq, irq);
> > +}
> > +
> > +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val;
> > +    can_update_irq(s);
> > +
> > +    return 0;
> > +}
> > +
> > +static void can_config_reset(XlnxZynqMPCAN *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_REGISTER]);
> > +    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(XlnxZynqMPCAN *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(XlnxZynqMPCAN *s)
> > +{
> > +    /* Wake up interrupt bit. */
> > +    bool wakeup_irq_val = (ARRAY_FIELD_EX32(s->regs,
> MODE_SELECT_REGISTER,
> > +                            SLEEP) == 0) && ARRAY_FIELD_EX32(s->regs,
> > +                            STATUS_REGISTER, SLEEP);
> > +
> > +    /* Sleep interrupt bit. */
> > +    bool sleep_irq_val = (ARRAY_FIELD_EX32(s->regs,
> MODE_SELECT_REGISTER,
> > +                            SLEEP) && (ARRAY_FIELD_EX32(s->regs,
> > +                            STATUS_REGISTER, SLEEP) == 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(XlnxZynqMPCAN *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 = data[0];
> > +    frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC);
> > +
> > +    frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
> > +    frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
> > +    frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
> > +    frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
> > +
> > +    frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
> > +    frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
> > +    frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
> > +    frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
> > +}
> > +
> > +static bool tx_ready_check(XlnxZynqMPCAN *s)
> > +{
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCAN%d is in reset mode\n", s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCAN%d is in configuration mode.Reset the"
> > +                      " core so operations can start fresh\n",
> > +                      s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                        " XlnxZynqMPCAN%d is in SNOOP MODE\n",
> > +                         s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +static void transfer_fifo(XlnxZynqMPCAN *s, Fifo32 *fifo)
> > +{
> > +    qemu_can_frame frame;
> > +    uint32_t data[CAN_FRAME_SIZE];
> > +    int i;
> > +    bool can_tx = tx_ready_check(s);
> > +
> > +    if (can_tx) {
> > +        while (!fifo32_is_empty(fifo)) {
> > +            for (i = 0; i < CAN_FRAME_SIZE; i++) {
> > +                data[i] = fifo32_pop(fifo);
> > +            }
> > +
> > +            if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +                /*
> > +                 * Controller in loopback. In Loopback mode, the XlnxZynqMPCAN
> > +                 * 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)) {
> > +                    DB_PRINT("Loopback: RX FIFO is full."
> > +                             "TX FIFO will be flushed.\n");
> > +
> > +                    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
> > +                                      RXOFLW, 1);
> > +                } else {
> > +                    for (i = 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);
> > +
> > +                can_bus_client_send(&s->bus_client, &frame, 1);
> 
> 
> I had a quick glance at can_bus_client_send():
> 
> It did:
> 
>      QTAILQ_FOREACH(peer, &bus->clients, next) {
>          if (peer->info->can_receive(peer)) {
>              if (peer == client) {
>                  /* No loopback support for now */
>                  continue;
>              }
>              if (peer->info->receive(peer, frames, frames_cnt) > 0) {
>                  ret = 1;
>              }
>          }
>      }
> 
> which looks not correct. We need to use qemu_send_packet() instead of
> calling peer->info->receive() directly which bypasses filters completely.
[Vikram Garhwal] I am looking into this. Will address it in v2.
> 
> 
> > +            }
> > +        }
> > +
> > +        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);
> > +        }
> > +    } else {
> > +        DB_PRINT("CAN is not enabled for data transfer.\n");
> > +    }
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
> > +                        FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
> > +
> > +    if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("Resetting XlnxZynqMPCAN%d\n", s->cfg.ctrl_idx);
> > +
> > +        /* First, core will do software reset then will enter in config mode. */
> > +        can_config_reset(s);
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 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
> regsiter
> > +         * states.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> > +
> > +        /* XlnxZynqMP CAN 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 val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +    uint8_t multi_mode = 0;
> > +
> > +    /*
> > +     * Multiple mode set check. This is done to make sure user doesn't set
> > +     * multiple modes.
> > +     */
> > +    multi_mode = 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) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure
> several modes "
> > +                     "simultaneously. One mode will be selected according to "
> > +                     "their priority: LBACK > SLEEP > SNOOP.\n ");
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> > +        /* We are in configuration mode, any mode can be selected. */
> > +        s->regs[R_MODE_SELECT_REGISTER] = val;
> > +    } else {
> > +        bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER,
> SLEEP);
> > +
> > +        ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP,
> sleep_mode_bit);
> > +
> > +        if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
> > +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK
> mode "
> > +                          "without setting CEN bit as 0\n");
> > +        } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
> > +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP
> mode "
> > +                              "without setting CEN bit as 0\n");
> > +        }
> > +        update_status_register_mode_bits(s);
> > +    }
> > +    return s->regs[R_MODE_SELECT_REGISTER];
> > +}
> > +
> > +static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    /* Only allow writes when in config mode. */
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> > +        val = s-
> >regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    /* Only allow writes when in config mode. */
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> > +        val = s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
> > +        s->rx_time_stamp = 0;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static void update_rx_fifo(XlnxZynqMPCAN *s, const qemu_can_frame
> *frame)
> > +{
> > +    uint32_t filter_pass = 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 = 1;
> 
> 
> Any reason for not doing such filtering in xlnx_zynqmp_can_can_receive()?
[Vikram Garhwal] Intention of xlnx_zynqmp_can_can_receive() is to see if CAN is enabled to receive the data or not. 
		Reason I put is here because here we basically filter and store all received message here only. 
> 
> 
> > +    }
> > +
> > +    /*
> > +     * 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 = s->regs[R_AFMR1] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
> > +        uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
> > +        uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
> > +        uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    /* Store the message in fifo if it passed through any of the filters. */
> > +    if (filter_pass && frame->can_dlc <= MAX_DLC) {
> > +
> > +        if (fifo32_is_full(&s->rx_fifo)) {
> > +            DB_PRINT("RX FIFO is full.\n");
> > +
> > +            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
> RXOFLW, 1);
> > +        } else {
> > +            s->rx_time_stamp += 1;
> > +
> > +            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,
> > +                                                s->rx_time_stamp)));
> > +
> > +            /* 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);
> > +        }
> > +
> > +        can_update_irq(s);
> > +
> > +    } else {
> > +        DB_PRINT("Message didn't pass through any filter"
> > +                  "or dlc is not in range\n");
> > +    }
> > +}
> > +
> > +static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t r = 0;
> > +
> > +    if (!fifo32_is_empty(&s->rx_fifo)) {
> > +        r = fifo32_pop(&s->rx_fifo);
> > +    } else {
> > +        DB_PRINT("No message in RXFIFO\n");
> > +
> > +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW,
> 1);
> > +    }
> > +
> > +    can_update_irq(s);
> > +    return r;
> > +}
> > +
> > +static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = 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
> val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t reg_idx = (reg->access->addr) / 4;
> > +    uint32_t val = val64;
> > +    uint32_t filter_number = (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] = val;
> > +    } else {
> > +        DB_PRINT("Acceptance filter %d mask is not set as it's corresponding "
> > +                 "UAF bit is not set to 0\n", filter_number + 1);
> > +    }
> > +
> > +    return s->regs[reg_idx];
> > +}
> > +
> > +static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t reg_idx = (reg->access->addr) / 4;
> > +    uint32_t val = val64;
> > +    uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
> > +
> > +    if (!(s->regs[R_AFR] & (1 << filter_number))) {
> > +        s->regs[reg_idx] = val;
> > +    } else {
> > +        DB_PRINT("Acceptance filter %d id is not set as it's corresponding "
> > +                 "UAF bit is not set to 0\n", filter_number + 1);
> > +    }
> > +
> > +    return s->regs[reg_idx];
> > +}
> > +
> > +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2;
> > +
> > +    bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) ||
> > +                             (reg->access->addr == A_TXHPB_DATA2);
> > +
> > +    Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
> > +
> > +    DB_PRINT("TX FIFO write for CAN%d\n", s->cfg.ctrl_idx);
> > +
> > +    if (!fifo32_is_full(f)) {
> > +        fifo32_push(f, val);
> > +    } else {
> > +        DB_PRINT("TX FIFO is full.\n");
> > +    }
> > +
> > +    /* 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[] = {
> > +    {   .name = "SOFTWARE_RESET_REGISTER",
> > +        .addr = A_SOFTWARE_RESET_REGISTER,
> > +        .rsvd = 0xfffffffc,
> > +        .pre_write = can_srr_pre_write,
> > +    },{ .name = "MODE_SELECT_REGISTER",
> > +        .addr = A_MODE_SELECT_REGISTER,
> > +        .rsvd = 0xfffffff8,
> > +        .pre_write = can_msr_pre_write,
> > +    },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
> > +        .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
> > +        .rsvd = 0xffffff00,
> > +        .pre_write = can_brpr_pre_write,
> > +    },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
> > +        .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
> > +        .rsvd = 0xfffffe00,
> > +        .pre_write = can_btr_pre_write,
> > +    },{ .name = "ERROR_COUNTER_REGISTER",
> > +        .addr = A_ERROR_COUNTER_REGISTER,
> > +        .rsvd = 0xffff0000,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ERROR_STATUS_REGISTER",
> > +        .addr = A_ERROR_STATUS_REGISTER,
> > +        .rsvd = 0xffffffe0,
> > +        .w1c = 0x1f,
> > +    },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER,
> > +        .reset = 0x1,
> > +        .rsvd = 0xffffe000,
> > +        .ro = 0x1fff,
> > +    },{ .name = "INTERRUPT_STATUS_REGISTER",
> > +        .addr = A_INTERRUPT_STATUS_REGISTER,
> > +        .reset = 0x6000,
> > +        .rsvd = 0xffff8000,
> > +        .ro = 0x7fff,
> > +    },{ .name = "INTERRUPT_ENABLE_REGISTER",
> > +        .addr = A_INTERRUPT_ENABLE_REGISTER,
> > +        .rsvd = 0xffff8000,
> > +        .post_write = can_ier_post_write,
> > +    },{ .name = "INTERRUPT_CLEAR_REGISTER",
> > +        .addr = A_INTERRUPT_CLEAR_REGISTER,
> > +        .rsvd = 0xffff8000,
> > +        .pre_write = can_icr_pre_write,
> > +    },{ .name = "TIMESTAMP_REGISTER",
> > +        .addr = A_TIMESTAMP_REGISTER,
> > +        .rsvd = 0xfffffffe,
> > +        .pre_write = can_tcr_pre_write,
> > +    },{ .name = "WIR",  .addr = A_WIR,
> > +        .reset = 0x3f3f,
> > +        .rsvd = 0xffff0000,
> > +    },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC,
> > +        .rsvd = 0xfffffff,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC,
> > +        .rsvd = 0xfffffff,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID,
> > +        .ro = 0xffffffff,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC,
> > +        .rsvd = 0xfff0000,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "AFR",  .addr = A_AFR,
> > +        .rsvd = 0xfffffff0,
> > +        .post_write = can_filter_enable_post_write,
> > +    },{ .name = "AFMR1",  .addr = A_AFMR1,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR1",  .addr = A_AFIR1,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR2",  .addr = A_AFMR2,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR2",  .addr = A_AFIR2,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR3",  .addr = A_AFMR3,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR3",  .addr = A_AFIR3,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR4",  .addr = A_AFMR4,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR4",  .addr = A_AFIR4,
> > +        .pre_write = can_filter_id_pre_write,
> > +    }
> > +};
> > +
> > +static const MemoryRegionOps can_ops = {
> > +    .read = register_read_memory,
> > +    .write = register_write_memory,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void xlnx_zynqmp_can_reset(DeviceState *dev)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++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 int xlnx_zynqmp_can_can_receive(CanBusClientState *client)
> > +{
> > +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("XlnxZynqMPCAN%d Controller is reset\n", s->cfg.ctrl_idx);
> > +        return 0;
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER,
> CEN)) == 0) {
> > +        DB_PRINT("XlnxZynqMPCAN%d is disabled. Messages will be
> discarded\n",
> > +                s->cfg.ctrl_idx);
> > +        return 0;
> > +    } else {
> > +        return 1;
> > +    }
> > +}
> > +
> > +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> > +                               const qemu_can_frame *buf, size_t buf_size) {
> > +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> > +    const qemu_can_frame *frame = buf;
> > +
> > +    DB_PRINT("Incoming data for CAN%d\n", s->cfg.ctrl_idx);
> > +
> > +    if (buf_size <= 0) {
> > +        DB_PRINT("junk data received on XlnxZynqMPCAN bus\n");
> > +        return 0;
> > +    }
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +        /*
> > +         * XlnxZynqMPCAN will not participate in normal bus communication
> and
> > +         * does not receive any messages transmitted by other CAN nodes.
> > +         */
> > +        DB_PRINT("XlnxZynqMPCAN is in loopback mode."
> > +                " It will not receive data.\n");
> > +
> > +    } else 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 to
> wake
> > +         * up state.
> > +         */
> > +        can_exit_sleep_mode(s);
> > +        update_rx_fifo(s, frame);
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
> > +        update_rx_fifo(s, frame);
> > +    } else {
> > +        DB_PRINT("Can't receive data as XlnxZynqMPCAN is not set
> correctly.\n");
> > +    }
> > +
> > +    return 1;
> > +}
> > +
> > +static CanBusClientInfo can_xilinx_bus_client_info = {
> > +    .can_receive = xlnx_zynqmp_can_can_receive,
> > +    .receive = xlnx_zynqmp_can_receive,
> > +};
> > +
> > +static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCAN *s,
> CanBusState *bus)
> > +{
> > +    s->bus_client.info = &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)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> > +
> > +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> > +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCAN
> controller "
> > +                    "index", s->cfg.ctrl_idx);
> > +        return;
> > +    }
> > +
> > +    if (s->canbus[s->cfg.ctrl_idx]) {
> > +        if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus[s->cfg.ctrl_idx]) <
> 0) {
> > +            error_setg(errp, "xlnx_zynqmp_can_connect_to_bus failed");
> > +        }
> > +
> > +    } else {
> > +        /* If no bus is set. */
> > +        DB_PRINT("Canbus%d property is not set for xlnxCAN%d\n",
> > +                    s->cfg.ctrl_idx, s->cfg.ctrl_idx);
> > +    }
> > +
> > +    /* 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);
> > +}
> > +
> > +static void xlnx_zynqmp_can_init(Object *obj)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(obj);
> > +    SysBusDevice *sbd = 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 = 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);
> > +
> > +    object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
> > +                             (Object **)&s->canbus[0],
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             0, &error_abort);
> > +
> > +    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
> > +                             (Object **)&s->canbus[1],
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             0, &error_abort);
> > +}
> > +
> > +static const VMStateDescription vmstate_can = {
> > +    .name = TYPE_XLNX_ZYNQMP_CAN,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCAN),
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCAN,
> XLNX_ZYNQMP_CAN_R_MAX),
> > +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCAN),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property xlnx_zynqmp_can_properties[] = {
> > +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCAN, cfg.ctrl_idx, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +    dc->reset = xlnx_zynqmp_can_reset;
> > +    dc->realize = xlnx_zynqmp_can_realize;
> > +    device_class_set_props(dc, xlnx_zynqmp_can_properties);
> > +    dc->vmsd = &vmstate_can;
> > +}
> > +
> > +static const TypeInfo can_info = {
> > +    .name          = TYPE_XLNX_ZYNQMP_CAN,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(XlnxZynqMPCAN),
> > +    .class_init    = xlnx_zynqmp_can_class_init,
> > +    .instance_init = xlnx_zynqmp_can_init,
> > +};
> > +
> > +static void can_register_types(void)
> > +{
> > +    type_register_static(&can_info);
> > +}
> > +
> > +type_init(can_register_types)
> > diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-
> zynqmp-can.h
> > new file mode 100644
> > index 0000000..537dbc2
> > --- /dev/null
> > +++ b/include/hw/net/xlnx-zynqmp-can.h
> > @@ -0,0 +1,77 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2019 Xilinx Inc.
> > + * Partially autogenerated by xregqemu.py 2019-06-20.
> > + *
> > + * 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"), to
> deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * 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 OTHER
> > + * 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 HW_CAN_XILINX_H
> > +#define HW_CAN_XILINX_H
> > +
> > +#include "hw/register.h"
> > +#include "net/can_emu.h"
> > +#include "net/can_host.h"
> > +#include "qemu/fifo32.h"
> > +
> > +#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
> > +
> > +#define XLNX_ZYNQMP_CAN(obj) \
> > +     OBJECT_CHECK(XlnxZynqMPCAN, (obj), TYPE_XLNX_ZYNQMP_CAN)
> > +
> > +#define MAX_CAN_CTRLS      2
> > +#define XLNX_ZYNQMP_CAN_R_MAX     (0x84 / 4)
> > +#define MAILBOX_CAPACITY   64
> > +
> > +/* Each CAN_FRAME will have 4 * 32bit size. */
> > +#define CAN_FRAME_SIZE     4
> > +#define RXFIFO_SIZE        (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
> > +
> > +
> > +typedef struct XlnxZynqMPCAN {
> 
> 
> For device state we usually have a "State" suffix. So it's better use
> "XlnxZynqMPCANState".
[Vikram Garhwal] Thanks for catching this. I will change it in v2.
> 
> Thanks
> 
> 
> > +    SysBusDevice        parent_obj;
> > +    MemoryRegion        iomem;
> > +
> > +    qemu_irq            irq;
> > +
> > +    CanBusClientState   bus_client;
> > +    CanBusState         *canbus[MAX_CAN_CTRLS];
> > +
> > +    struct {
> > +        uint8_t         ctrl_idx;
> > +   } cfg;
> > +
> > +    RegisterInfo        reg_info[XLNX_ZYNQMP_CAN_R_MAX];
> > +    uint32_t            regs[XLNX_ZYNQMP_CAN_R_MAX];
> > +
> > +    uint16_t            rx_time_stamp;
> > +
> > +    Fifo32                rx_fifo;
> > +    Fifo32                tx_fifo;
> > +    Fifo32                txhpb_fifo;
> > +
> > +} XlnxZynqMPCAN;
> > +
> > +#endif


^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-05  5:46   ` Jason Wang
  2020-02-06 18:39     ` Vikram Garhwal
@ 2020-02-10 21:45     ` Vikram Garhwal
  2020-02-11  3:09       ` Jason Wang
  1 sibling, 1 reply; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-10 21:45 UTC (permalink / raw)
  To: Jason Wang, qemu-devel

Hi Jason,
Thanks again for your comments, much appreciated. I looked into using qemu_send_packet() instead of can_bus_client_send(). And have a few comments inline.

-Vikram

> -----Original Message-----
> From: Jason Wang <jasowang@redhat.com>
> Sent: Tuesday, February 4, 2020 9:47 PM
> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller
> for QEMU
> 
> 
> On 2020/2/4 上午5:06, Vikram Garhwal wrote:
> > XlnxCAN is developed based on SocketCAN, QEMU CAN bus implementation.
> > Bus connection and socketCAN connection for each CAN module can be set
> > through command lines.
> >
> > Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> > ---
> >   hw/net/can/Makefile.objs         |    1 +
> >   hw/net/can/xlnx-zynqmp-can.c     | 1106
> ++++++++++++++++++++++++++++++++++++++
> >   include/hw/net/xlnx-zynqmp-can.h |   77 +++
> >   3 files changed, 1184 insertions(+)
> >   create mode 100644 hw/net/can/xlnx-zynqmp-can.c
> >   create mode 100644 include/hw/net/xlnx-zynqmp-can.h
> >
> > diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs index
> > 9f0c4ee..0fe87dd 100644
> > --- a/hw/net/can/Makefile.objs
> > +++ b/hw/net/can/Makefile.objs
> > @@ -2,3 +2,4 @@ common-obj-$(CONFIG_CAN_SJA1000) +=
> can_sja1000.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
> >   common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
> > +common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-can.o
> > diff --git a/hw/net/can/xlnx-zynqmp-can.c
> > b/hw/net/can/xlnx-zynqmp-can.c new file mode 100644 index
> > 0000000..e14013c
> > --- /dev/null
> > +++ b/hw/net/can/xlnx-zynqmp-can.c
> > @@ -0,0 +1,1106 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2019 Xilinx Inc.
> 
> 
> 2020?
> 
> 
> > + * Partially autogenerated by xregqemu.py
> 
> 
> What did xregqemu.py do? Is it possible to integrate it into qemu source then
> we can review high level language other than c?
> 
> 
> > 2019-06-20.
> > + *
> > + * 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"), to
> deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * 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 OTHER
> > + * 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"
> > +
> > +#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
> > +#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
> > +#endif
> > +
> > +#define DB_PRINT(...) do { \
> > +    if (XLNX_ZYNQMP_CAN_ERR_DEBUG) { \
> > +        qemu_log(__VA_ARGS__); \
> > +    } \
> > +} while (0)
> > +
> > +#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(XlnxZynqMPCAN *s)
> > +{
> > +    unsigned int 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) >= 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 = s->regs[R_INTERRUPT_STATUS_REGISTER];
> > +    irq &= s->regs[R_INTERRUPT_ENABLE_REGISTER];
> > +
> > +    qemu_set_irq(s->irq, irq);
> > +}
> > +
> > +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_INTERRUPT_STATUS_REGISTER] &= ~val;
> > +    can_update_irq(s);
> > +
> > +    return 0;
> > +}
> > +
> > +static void can_config_reset(XlnxZynqMPCAN *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_REGISTER]);
> > +    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(XlnxZynqMPCAN *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(XlnxZynqMPCAN *s)
> > +{
> > +    /* Wake up interrupt bit. */
> > +    bool wakeup_irq_val = (ARRAY_FIELD_EX32(s->regs,
> MODE_SELECT_REGISTER,
> > +                            SLEEP) == 0) && ARRAY_FIELD_EX32(s->regs,
> > +                            STATUS_REGISTER, SLEEP);
> > +
> > +    /* Sleep interrupt bit. */
> > +    bool sleep_irq_val = (ARRAY_FIELD_EX32(s->regs,
> MODE_SELECT_REGISTER,
> > +                            SLEEP) && (ARRAY_FIELD_EX32(s->regs,
> > +                            STATUS_REGISTER, SLEEP) == 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(XlnxZynqMPCAN *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 = data[0];
> > +    frame->can_dlc = FIELD_EX32(data[1], TXFIFO_DLC, DLC);
> > +
> > +    frame->data[0] = FIELD_EX32(data[2], TXFIFO_DATA1, DB3);
> > +    frame->data[1] = FIELD_EX32(data[2], TXFIFO_DATA1, DB2);
> > +    frame->data[2] = FIELD_EX32(data[2], TXFIFO_DATA1, DB1);
> > +    frame->data[3] = FIELD_EX32(data[2], TXFIFO_DATA1, DB0);
> > +
> > +    frame->data[4] = FIELD_EX32(data[3], TXFIFO_DATA2, DB7);
> > +    frame->data[5] = FIELD_EX32(data[3], TXFIFO_DATA2, DB6);
> > +    frame->data[6] = FIELD_EX32(data[3], TXFIFO_DATA2, DB5);
> > +    frame->data[7] = FIELD_EX32(data[3], TXFIFO_DATA2, DB4);
> > +}
> > +
> > +static bool tx_ready_check(XlnxZynqMPCAN *s)
> > +{
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCAN%d is in reset mode\n", s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCAN%d is in configuration mode.Reset the"
> > +                      " core so operations can start fresh\n",
> > +                      s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                        " XlnxZynqMPCAN%d is in SNOOP MODE\n",
> > +                         s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +static void transfer_fifo(XlnxZynqMPCAN *s, Fifo32 *fifo)
> > +{
> > +    qemu_can_frame frame;
> > +    uint32_t data[CAN_FRAME_SIZE];
> > +    int i;
> > +    bool can_tx = tx_ready_check(s);
> > +
> > +    if (can_tx) {
> > +        while (!fifo32_is_empty(fifo)) {
> > +            for (i = 0; i < CAN_FRAME_SIZE; i++) {
> > +                data[i] = fifo32_pop(fifo);
> > +            }
> > +
> > +            if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +                /*
> > +                 * Controller in loopback. In Loopback mode, the XlnxZynqMPCAN
> > +                 * 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)) {
> > +                    DB_PRINT("Loopback: RX FIFO is full."
> > +                             "TX FIFO will be flushed.\n");
> > +
> > +                    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
> > +                                      RXOFLW, 1);
> > +                } else {
> > +                    for (i = 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);
> > +
> > +                can_bus_client_send(&s->bus_client, &frame, 1);
> 
> 
> I had a quick glance at can_bus_client_send():
> 
> It did:
> 
>      QTAILQ_FOREACH(peer, &bus->clients, next) {
>          if (peer->info->can_receive(peer)) {
>              if (peer == client) {
>                  /* No loopback support for now */
>                  continue;
>              }
>              if (peer->info->receive(peer, frames, frames_cnt) > 0) {
>                  ret = 1;
>              }
>          }
>      }
> 
> which looks not correct. We need to use qemu_send_packet() instead of
> calling peer->info->receive() directly which bypasses filters completely.
[Vikram Garhwal] Can you please elaborate it bit more on why do we need to filter outgoing message? So, I can either add a filter before sending the packets. I am unable to understand the use case for it. For any message which is incoming, we are filtering it for sure before storing in update_rx_fifo(). 

Also, I can see existing CAN models like CAN sja1000 and CAN Kavser are using it same can_bus_client_send() function. However, this doesn't mean that it is the correct way to send & receive packets.
> 
> 
> > +            }
> > +        }
> > +
> > +        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);
> > +        }
> > +    } else {
> > +        DB_PRINT("CAN is not enabled for data transfer.\n");
> > +    }
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
> > +                        FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
> > +
> > +    if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("Resetting XlnxZynqMPCAN%d\n", s->cfg.ctrl_idx);
> > +
> > +        /* First, core will do software reset then will enter in config mode. */
> > +        can_config_reset(s);
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 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
> regsiter
> > +         * states.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> > +
> > +        /* XlnxZynqMP CAN 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 val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +    uint8_t multi_mode = 0;
> > +
> > +    /*
> > +     * Multiple mode set check. This is done to make sure user doesn't set
> > +     * multiple modes.
> > +     */
> > +    multi_mode = 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) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to configure
> several modes "
> > +                     "simultaneously. One mode will be selected according to "
> > +                     "their priority: LBACK > SLEEP > SNOOP.\n ");
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
> > +        /* We are in configuration mode, any mode can be selected. */
> > +        s->regs[R_MODE_SELECT_REGISTER] = val;
> > +    } else {
> > +        bool sleep_mode_bit = FIELD_EX32(val, MODE_SELECT_REGISTER,
> SLEEP);
> > +
> > +        ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP,
> sleep_mode_bit);
> > +
> > +        if (FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK)) {
> > +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set LBACK
> mode "
> > +                          "without setting CEN bit as 0\n");
> > +        } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
> > +            qemu_log_mask(LOG_GUEST_ERROR, "Attempting to set SNOOP
> mode "
> > +                              "without setting CEN bit as 0\n");
> > +        }
> > +        update_status_register_mode_bits(s);
> > +    }
> > +    return s->regs[R_MODE_SELECT_REGISTER];
> > +}
> > +
> > +static uint64_t can_brpr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    /* Only allow writes when in config mode. */
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> > +        val = s-
> >regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +static uint64_t can_btr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    /* Only allow writes when in config mode. */
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
> > +        val = s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +static uint64_t can_tcr_pre_write(RegisterInfo  *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
> > +        s->rx_time_stamp = 0;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static void update_rx_fifo(XlnxZynqMPCAN *s, const qemu_can_frame
> *frame)
> > +{
> > +    uint32_t filter_pass = 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 = 1;
> 
> 
> Any reason for not doing such filtering in xlnx_zynqmp_can_can_receive()?
> 
> 
> > +    }
> > +
> > +    /*
> > +     * 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 = s->regs[R_AFMR1] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR1] & s->regs[R_AFIR1];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF2)) {
> > +        uint32_t id_masked = s->regs[R_AFMR2] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR2] & s->regs[R_AFIR2];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF3)) {
> > +        uint32_t id_masked = s->regs[R_AFMR3] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR3] & s->regs[R_AFIR3];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
> > +        uint32_t id_masked = s->regs[R_AFMR4] & frame->can_id;
> > +        uint32_t filter_id_masked = s->regs[R_AFMR4] & s->regs[R_AFIR4];
> > +
> > +        if (filter_id_masked == id_masked) {
> > +            filter_pass = 1;
> > +        }
> > +    }
> > +
> > +    /* Store the message in fifo if it passed through any of the filters. */
> > +    if (filter_pass && frame->can_dlc <= MAX_DLC) {
> > +
> > +        if (fifo32_is_full(&s->rx_fifo)) {
> > +            DB_PRINT("RX FIFO is full.\n");
> > +
> > +            ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER,
> RXOFLW, 1);
> > +        } else {
> > +            s->rx_time_stamp += 1;
> > +
> > +            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,
> > +                                                s->rx_time_stamp)));
> > +
> > +            /* 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);
> > +        }
> > +
> > +        can_update_irq(s);
> > +
> > +    } else {
> > +        DB_PRINT("Message didn't pass through any filter"
> > +                  "or dlc is not in range\n");
> > +    }
> > +}
> > +
> > +static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t r = 0;
> > +
> > +    if (!fifo32_is_empty(&s->rx_fifo)) {
> > +        r = fifo32_pop(&s->rx_fifo);
> > +    } else {
> > +        DB_PRINT("No message in RXFIFO\n");
> > +
> > +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW,
> 1);
> > +    }
> > +
> > +    can_update_irq(s);
> > +    return r;
> > +}
> > +
> > +static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = 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
> val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t reg_idx = (reg->access->addr) / 4;
> > +    uint32_t val = val64;
> > +    uint32_t filter_number = (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] = val;
> > +    } else {
> > +        DB_PRINT("Acceptance filter %d mask is not set as it's corresponding "
> > +                 "UAF bit is not set to 0\n", filter_number + 1);
> > +    }
> > +
> > +    return s->regs[reg_idx];
> > +}
> > +
> > +static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t reg_idx = (reg->access->addr) / 4;
> > +    uint32_t val = val64;
> > +    uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
> > +
> > +    if (!(s->regs[R_AFR] & (1 << filter_number))) {
> > +        s->regs[reg_idx] = val;
> > +    } else {
> > +        DB_PRINT("Acceptance filter %d id is not set as it's corresponding "
> > +                 "UAF bit is not set to 0\n", filter_number + 1);
> > +    }
> > +
> > +    return s->regs[reg_idx];
> > +}
> > +
> > +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    bool is_txhpb = reg->access->addr > A_TXFIFO_DATA2;
> > +
> > +    bool initiate_transfer = (reg->access->addr == A_TXFIFO_DATA2) ||
> > +                             (reg->access->addr == A_TXHPB_DATA2);
> > +
> > +    Fifo32 *f = is_txhpb ? &s->txhpb_fifo : &s->tx_fifo;
> > +
> > +    DB_PRINT("TX FIFO write for CAN%d\n", s->cfg.ctrl_idx);
> > +
> > +    if (!fifo32_is_full(f)) {
> > +        fifo32_push(f, val);
> > +    } else {
> > +        DB_PRINT("TX FIFO is full.\n");
> > +    }
> > +
> > +    /* 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[] = {
> > +    {   .name = "SOFTWARE_RESET_REGISTER",
> > +        .addr = A_SOFTWARE_RESET_REGISTER,
> > +        .rsvd = 0xfffffffc,
> > +        .pre_write = can_srr_pre_write,
> > +    },{ .name = "MODE_SELECT_REGISTER",
> > +        .addr = A_MODE_SELECT_REGISTER,
> > +        .rsvd = 0xfffffff8,
> > +        .pre_write = can_msr_pre_write,
> > +    },{ .name = "ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER",
> > +        .addr = A_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER,
> > +        .rsvd = 0xffffff00,
> > +        .pre_write = can_brpr_pre_write,
> > +    },{ .name = "ARBITRATION_PHASE_BIT_TIMING_REGISTER",
> > +        .addr = A_ARBITRATION_PHASE_BIT_TIMING_REGISTER,
> > +        .rsvd = 0xfffffe00,
> > +        .pre_write = can_btr_pre_write,
> > +    },{ .name = "ERROR_COUNTER_REGISTER",
> > +        .addr = A_ERROR_COUNTER_REGISTER,
> > +        .rsvd = 0xffff0000,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ERROR_STATUS_REGISTER",
> > +        .addr = A_ERROR_STATUS_REGISTER,
> > +        .rsvd = 0xffffffe0,
> > +        .w1c = 0x1f,
> > +    },{ .name = "STATUS_REGISTER",  .addr = A_STATUS_REGISTER,
> > +        .reset = 0x1,
> > +        .rsvd = 0xffffe000,
> > +        .ro = 0x1fff,
> > +    },{ .name = "INTERRUPT_STATUS_REGISTER",
> > +        .addr = A_INTERRUPT_STATUS_REGISTER,
> > +        .reset = 0x6000,
> > +        .rsvd = 0xffff8000,
> > +        .ro = 0x7fff,
> > +    },{ .name = "INTERRUPT_ENABLE_REGISTER",
> > +        .addr = A_INTERRUPT_ENABLE_REGISTER,
> > +        .rsvd = 0xffff8000,
> > +        .post_write = can_ier_post_write,
> > +    },{ .name = "INTERRUPT_CLEAR_REGISTER",
> > +        .addr = A_INTERRUPT_CLEAR_REGISTER,
> > +        .rsvd = 0xffff8000,
> > +        .pre_write = can_icr_pre_write,
> > +    },{ .name = "TIMESTAMP_REGISTER",
> > +        .addr = A_TIMESTAMP_REGISTER,
> > +        .rsvd = 0xfffffffe,
> > +        .pre_write = can_tcr_pre_write,
> > +    },{ .name = "WIR",  .addr = A_WIR,
> > +        .reset = 0x3f3f,
> > +        .rsvd = 0xffff0000,
> > +    },{ .name = "TXFIFO_ID",  .addr = A_TXFIFO_ID,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DLC",  .addr = A_TXFIFO_DLC,
> > +        .rsvd = 0xfffffff,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DATA1",  .addr = A_TXFIFO_DATA1,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXFIFO_DATA2",  .addr = A_TXFIFO_DATA2,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_ID",  .addr = A_TXHPB_ID,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DLC",  .addr = A_TXHPB_DLC,
> > +        .rsvd = 0xfffffff,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DATA1",  .addr = A_TXHPB_DATA1,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "TXHPB_DATA2",  .addr = A_TXHPB_DATA2,
> > +        .post_write = can_tx_post_write,
> > +    },{ .name = "RXFIFO_ID",  .addr = A_RXFIFO_ID,
> > +        .ro = 0xffffffff,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DLC",  .addr = A_RXFIFO_DLC,
> > +        .rsvd = 0xfff0000,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DATA1",  .addr = A_RXFIFO_DATA1,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "RXFIFO_DATA2",  .addr = A_RXFIFO_DATA2,
> > +        .post_read = can_rxfifo_pre_read,
> > +    },{ .name = "AFR",  .addr = A_AFR,
> > +        .rsvd = 0xfffffff0,
> > +        .post_write = can_filter_enable_post_write,
> > +    },{ .name = "AFMR1",  .addr = A_AFMR1,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR1",  .addr = A_AFIR1,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR2",  .addr = A_AFMR2,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR2",  .addr = A_AFIR2,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR3",  .addr = A_AFMR3,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR3",  .addr = A_AFIR3,
> > +        .pre_write = can_filter_id_pre_write,
> > +    },{ .name = "AFMR4",  .addr = A_AFMR4,
> > +        .pre_write = can_filter_mask_pre_write,
> > +    },{ .name = "AFIR4",  .addr = A_AFIR4,
> > +        .pre_write = can_filter_id_pre_write,
> > +    }
> > +};
> > +
> > +static const MemoryRegionOps can_ops = {
> > +    .read = register_read_memory,
> > +    .write = register_write_memory,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void xlnx_zynqmp_can_reset(DeviceState *dev)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++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 int xlnx_zynqmp_can_can_receive(CanBusClientState *client)
> > +{
> > +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("XlnxZynqMPCAN%d Controller is reset\n", s->cfg.ctrl_idx);
> > +        return 0;
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER,
> CEN)) == 0) {
> > +        DB_PRINT("XlnxZynqMPCAN%d is disabled. Messages will be
> discarded\n",
> > +                s->cfg.ctrl_idx);
> > +        return 0;
> > +    } else {
> > +        return 1;
> > +    }
> > +}
> > +
> > +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> > +                               const qemu_can_frame *buf, size_t buf_size) {
> > +    XlnxZynqMPCAN *s = container_of(client, XlnxZynqMPCAN, bus_client);
> > +    const qemu_can_frame *frame = buf;
> > +
> > +    DB_PRINT("Incoming data for CAN%d\n", s->cfg.ctrl_idx);
> > +
> > +    if (buf_size <= 0) {
> > +        DB_PRINT("junk data received on XlnxZynqMPCAN bus\n");
> > +        return 0;
> > +    }
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +        /*
> > +         * XlnxZynqMPCAN will not participate in normal bus communication
> and
> > +         * does not receive any messages transmitted by other CAN nodes.
> > +         */
> > +        DB_PRINT("XlnxZynqMPCAN is in loopback mode."
> > +                " It will not receive data.\n");
> > +
> > +    } else 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 to
> wake
> > +         * up state.
> > +         */
> > +        can_exit_sleep_mode(s);
> > +        update_rx_fifo(s, frame);
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
> > +        update_rx_fifo(s, frame);
> > +    } else {
> > +        DB_PRINT("Can't receive data as XlnxZynqMPCAN is not set
> correctly.\n");
> > +    }
> > +
> > +    return 1;
> > +}
> > +
> > +static CanBusClientInfo can_xilinx_bus_client_info = {
> > +    .can_receive = xlnx_zynqmp_can_can_receive,
> > +    .receive = xlnx_zynqmp_can_receive,
> > +};
> > +
> > +static int xlnx_zynqmp_can_connect_to_bus(XlnxZynqMPCAN *s,
> CanBusState *bus)
> > +{
> > +    s->bus_client.info = &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)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(dev);
> > +
> > +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> > +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCAN
> controller "
> > +                    "index", s->cfg.ctrl_idx);
> > +        return;
> > +    }
> > +
> > +    if (s->canbus[s->cfg.ctrl_idx]) {
> > +        if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus[s->cfg.ctrl_idx]) <
> 0) {
> > +            error_setg(errp, "xlnx_zynqmp_can_connect_to_bus failed");
> > +        }
> > +
> > +    } else {
> > +        /* If no bus is set. */
> > +        DB_PRINT("Canbus%d property is not set for xlnxCAN%d\n",
> > +                    s->cfg.ctrl_idx, s->cfg.ctrl_idx);
> > +    }
> > +
> > +    /* 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);
> > +}
> > +
> > +static void xlnx_zynqmp_can_init(Object *obj)
> > +{
> > +    XlnxZynqMPCAN *s = XLNX_ZYNQMP_CAN(obj);
> > +    SysBusDevice *sbd = 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 = 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);
> > +
> > +    object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
> > +                             (Object **)&s->canbus[0],
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             0, &error_abort);
> > +
> > +    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
> > +                             (Object **)&s->canbus[1],
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             0, &error_abort);
> > +}
> > +
> > +static const VMStateDescription vmstate_can = {
> > +    .name = TYPE_XLNX_ZYNQMP_CAN,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_FIFO32(rx_fifo, XlnxZynqMPCAN),
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCAN,
> XLNX_ZYNQMP_CAN_R_MAX),
> > +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCAN),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property xlnx_zynqmp_can_properties[] = {
> > +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCAN, cfg.ctrl_idx, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +    dc->reset = xlnx_zynqmp_can_reset;
> > +    dc->realize = xlnx_zynqmp_can_realize;
> > +    device_class_set_props(dc, xlnx_zynqmp_can_properties);
> > +    dc->vmsd = &vmstate_can;
> > +}
> > +
> > +static const TypeInfo can_info = {
> > +    .name          = TYPE_XLNX_ZYNQMP_CAN,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(XlnxZynqMPCAN),
> > +    .class_init    = xlnx_zynqmp_can_class_init,
> > +    .instance_init = xlnx_zynqmp_can_init,
> > +};
> > +
> > +static void can_register_types(void)
> > +{
> > +    type_register_static(&can_info);
> > +}
> > +
> > +type_init(can_register_types)
> > diff --git a/include/hw/net/xlnx-zynqmp-can.h b/include/hw/net/xlnx-
> zynqmp-can.h
> > new file mode 100644
> > index 0000000..537dbc2
> > --- /dev/null
> > +++ b/include/hw/net/xlnx-zynqmp-can.h
> > @@ -0,0 +1,77 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2019 Xilinx Inc.
> > + * Partially autogenerated by xregqemu.py 2019-06-20.
> > + *
> > + * 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"), to
> deal
> > + * in the Software without restriction, including without limitation the rights
> > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> > + * 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 OTHER
> > + * 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 HW_CAN_XILINX_H
> > +#define HW_CAN_XILINX_H
> > +
> > +#include "hw/register.h"
> > +#include "net/can_emu.h"
> > +#include "net/can_host.h"
> > +#include "qemu/fifo32.h"
> > +
> > +#define TYPE_XLNX_ZYNQMP_CAN "xlnx.zynqmp-can"
> > +
> > +#define XLNX_ZYNQMP_CAN(obj) \
> > +     OBJECT_CHECK(XlnxZynqMPCAN, (obj), TYPE_XLNX_ZYNQMP_CAN)
> > +
> > +#define MAX_CAN_CTRLS      2
> > +#define XLNX_ZYNQMP_CAN_R_MAX     (0x84 / 4)
> > +#define MAILBOX_CAPACITY   64
> > +
> > +/* Each CAN_FRAME will have 4 * 32bit size. */
> > +#define CAN_FRAME_SIZE     4
> > +#define RXFIFO_SIZE        (MAILBOX_CAPACITY * CAN_FRAME_SIZE)
> > +
> > +
> > +typedef struct XlnxZynqMPCAN {
> 
> 
> For device state we usually have a "State" suffix. So it's better use
> "XlnxZynqMPCANState".
> 
> Thanks
> 
> 
> > +    SysBusDevice        parent_obj;
> > +    MemoryRegion        iomem;
> > +
> > +    qemu_irq            irq;
> > +
> > +    CanBusClientState   bus_client;
> > +    CanBusState         *canbus[MAX_CAN_CTRLS];
> > +
> > +    struct {
> > +        uint8_t         ctrl_idx;
> > +   } cfg;
> > +
> > +    RegisterInfo        reg_info[XLNX_ZYNQMP_CAN_R_MAX];
> > +    uint32_t            regs[XLNX_ZYNQMP_CAN_R_MAX];
> > +
> > +    uint16_t            rx_time_stamp;
> > +
> > +    Fifo32                rx_fifo;
> > +    Fifo32                tx_fifo;
> > +    Fifo32                txhpb_fifo;
> > +
> > +} XlnxZynqMPCAN;
> > +
> > +#endif


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-10 21:45     ` Vikram Garhwal
@ 2020-02-11  3:09       ` Jason Wang
  2020-02-25  6:22         ` Vikram Garhwal
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2020-02-11  3:09 UTC (permalink / raw)
  To: Vikram Garhwal, qemu-devel


On 2020/2/11 上午5:45, Vikram Garhwal wrote:
>>> +                }
>>> +            } else {
>>> +                /* Normal mode Tx. */
>>> +                generate_frame(&frame, data);
>>> +
>>> +                can_bus_client_send(&s->bus_client, &frame, 1);
>> I had a quick glance at can_bus_client_send():
>>
>> It did:
>>
>>       QTAILQ_FOREACH(peer, &bus->clients, next) {
>>           if (peer->info->can_receive(peer)) {
>>               if (peer == client) {
>>                   /* No loopback support for now */
>>                   continue;
>>               }
>>               if (peer->info->receive(peer, frames, frames_cnt) > 0) {
>>                   ret = 1;
>>               }
>>           }
>>       }
>>
>> which looks not correct. We need to use qemu_send_packet() instead of
>> calling peer->info->receive() directly which bypasses filters completely.
> [Vikram Garhwal] Can you please elaborate it bit more on why do we need to filter outgoing message? So, I can either add a filter before sending the packets. I am unable to understand the use case for it. For any message which is incoming, we are filtering it for sure before storing in update_rx_fifo().


I might be not clear, I meant the netfilters supported by qemu which 
allows you to attach a filter to a specific NetClientState, see 
qemu_send_packet_async_with_flags. It doesn't mean the filter 
implemented in your own NIC model.

Thanks


> Also, I can see existing CAN models like CAN sja1000 and CAN Kavser are using it same can_bus_client_send() function. However, this doesn't mean that it is the correct way to send & receive packets.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-11  3:09       ` Jason Wang
@ 2020-02-25  6:22         ` Vikram Garhwal
  2020-02-26  3:24           ` Jason Wang
  0 siblings, 1 reply; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-25  6:22 UTC (permalink / raw)
  To: Jason Wang, qemu-devel; +Cc: Francisco Eduardo Iglesias

Hi Jason,
Apologies for the delayed response. I tried plugging NetClientState in the CAN which is required if we use qemu_send_packet but this will change the underlying architecture of can-core, can-socketcan a lot. This means changes the way CAN bus is created/works and socket CAN works. CAN Socket(CAN Raw socket) is much different from Ethernet so plugging/using NetClient state is not working here.

I apologize for still being a little confused about the filters but when looking into the code, I can only find them being used with ethernet frames. Since no other can controller uses NetClientState it makes me wonder if this model perhaps was thought of being an ethernet NIC? Or has the code in net/can/ which I referenced been obsoleted?

Sharing this link for SocketCAN(in case you want to have a look): https://www.kernel.org/doc/Documentation/networking/can.txt. Section 4 talks on how CAN Socket is intended to work. Equivalent file is located as net/can-socketcan.c.
 
Regards,
Vikram

> -----Original Message-----
> From: Jason Wang <jasowang@redhat.com>
> Sent: Monday, February 10, 2020 7:09 PM
> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller
> for QEMU
> 
> 
> On 2020/2/11 上午5:45, Vikram Garhwal wrote:
> >>> +                }
> >>> +            } else {
> >>> +                /* Normal mode Tx. */
> >>> +                generate_frame(&frame, data);
> >>> +
> >>> +                can_bus_client_send(&s->bus_client, &frame, 1);
> >> I had a quick glance at can_bus_client_send():
> >>
> >> It did:
> >>
> >>       QTAILQ_FOREACH(peer, &bus->clients, next) {
> >>           if (peer->info->can_receive(peer)) {
> >>               if (peer == client) {
> >>                   /* No loopback support for now */
> >>                   continue;
> >>               }
> >>               if (peer->info->receive(peer, frames, frames_cnt) > 0) {
> >>                   ret = 1;
> >>               }
> >>           }
> >>       }
> >>
> >> which looks not correct. We need to use qemu_send_packet() instead of
> >> calling peer->info->receive() directly which bypasses filters completely.
> > [Vikram Garhwal] Can you please elaborate it bit more on why do we need
> to filter outgoing message? So, I can either add a filter before sending the
> packets. I am unable to understand the use case for it. For any message which
> is incoming, we are filtering it for sure before storing in update_rx_fifo().
> 
> 
> I might be not clear, I meant the netfilters supported by qemu which allows
> you to attach a filter to a specific NetClientState, see
> qemu_send_packet_async_with_flags. It doesn't mean the filter implemented
> in your own NIC model.
> 
> Thanks
> 
> 
> > Also, I can see existing CAN models like CAN sja1000 and CAN Kavser are
> using it same can_bus_client_send() function. However, this doesn't mean
> that it is the correct way to send & receive packets.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-25  6:22         ` Vikram Garhwal
@ 2020-02-26  3:24           ` Jason Wang
  2020-02-27 16:04             ` Vikram Garhwal
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Wang @ 2020-02-26  3:24 UTC (permalink / raw)
  To: Vikram Garhwal, qemu-devel; +Cc: Francisco Eduardo Iglesias


On 2020/2/25 下午2:22, Vikram Garhwal wrote:
> Hi Jason,
> Apologies for the delayed response. I tried plugging NetClientState in the CAN which is required if we use qemu_send_packet but this will change the underlying architecture of can-core, can-socketcan a lot. This means changes the way CAN bus is created/works and socket CAN works. CAN Socket(CAN Raw socket) is much different from Ethernet so plugging/using NetClient state is not working here.


I get you.


>
> I apologize for still being a little confused about the filters but when looking into the code, I can only find them being used with ethernet frames. Since no other can controller uses NetClientState it makes me wonder if this model perhaps was thought of being an ethernet NIC?


Nope NetclientState is not necessarily a NIC, it can be a peer of the 
NIC (e.g network backend like tap, hubport etc).


> Or has the code in net/can/ which I referenced been obsoleted?


No :)


>
> Sharing this link for SocketCAN(in case you want to have a look): https://www.kernel.org/doc/Documentation/networking/can.txt. Section 4 talks on how CAN Socket is intended to work. Equivalent file is located as net/can-socketcan.c.


Thanks for the pointer.

I agree that there's no need to change that part. But we may consider to 
unify the CanBusClientState and NetClientState in the future.


>   
> Regards,
> Vikram
>
>> -----Original Message-----
>> From: Jason Wang <jasowang@redhat.com>
>> Sent: Monday, February 10, 2020 7:09 PM
>> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
>> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller
>> for QEMU
>>
>>
>> On 2020/2/11 上午5:45, Vikram Garhwal wrote:
>>>>> +                }
>>>>> +            } else {
>>>>> +                /* Normal mode Tx. */
>>>>> +                generate_frame(&frame, data);
>>>>> +
>>>>> +                can_bus_client_send(&s->bus_client, &frame, 1);
>>>> I had a quick glance at can_bus_client_send():
>>>>
>>>> It did:
>>>>
>>>>        QTAILQ_FOREACH(peer, &bus->clients, next) {
>>>>            if (peer->info->can_receive(peer)) {
>>>>                if (peer == client) {
>>>>                    /* No loopback support for now */
>>>>                    continue;
>>>>                }
>>>>                if (peer->info->receive(peer, frames, frames_cnt) > 0) {
>>>>                    ret = 1;
>>>>                }
>>>>            }
>>>>        }
>>>>
>>>> which looks not correct. We need to use qemu_send_packet() instead of
>>>> calling peer->info->receive() directly which bypasses filters completely.
>>> [Vikram Garhwal] Can you please elaborate it bit more on why do we need
>> to filter outgoing message? So, I can either add a filter before sending the
>> packets. I am unable to understand the use case for it. For any message which
>> is incoming, we are filtering it for sure before storing in update_rx_fifo().
>>
>>
>> I might be not clear, I meant the netfilters supported by qemu which allows
>> you to attach a filter to a specific NetClientState, see
>> qemu_send_packet_async_with_flags. It doesn't mean the filter implemented
>> in your own NIC model.
>>
>> Thanks
>>
>>
>>> Also, I can see existing CAN models like CAN sja1000 and CAN Kavser are
>> using it same can_bus_client_send() function. However, this doesn't mean
>> that it is the correct way to send & receive packets.



^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN controller for QEMU
  2020-02-26  3:24           ` Jason Wang
@ 2020-02-27 16:04             ` Vikram Garhwal
  0 siblings, 0 replies; 12+ messages in thread
From: Vikram Garhwal @ 2020-02-27 16:04 UTC (permalink / raw)
  To: Jason Wang, qemu-devel; +Cc: Francisco Eduardo Iglesias

Hi Jason,
Thanks for replying. 
Yeah, we can start looking into combining NetClient and CanBusClient in near future.

Meanwhile sending a V2 today with incorporating your suggestion.. 
Regards,
Vikram

> -----Original Message-----
> From: Jason Wang <jasowang@redhat.com>
> Sent: Tuesday, February 25, 2020 7:25 PM
> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> Cc: Francisco Eduardo Iglesias <figlesia@xilinx.com>
> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN
> controller for QEMU
> 
> 
> On 2020/2/25 下午2:22, Vikram Garhwal wrote:
> > Hi Jason,
> > Apologies for the delayed response. I tried plugging NetClientState in the
> CAN which is required if we use qemu_send_packet but this will change the
> underlying architecture of can-core, can-socketcan a lot. This means
> changes the way CAN bus is created/works and socket CAN works. CAN
> Socket(CAN Raw socket) is much different from Ethernet so plugging/using
> NetClient state is not working here.
> 
> 
> I get you.
> 
> 
> >
> > I apologize for still being a little confused about the filters but when
> looking into the code, I can only find them being used with ethernet frames.
> Since no other can controller uses NetClientState it makes me wonder if this
> model perhaps was thought of being an ethernet NIC?
> 
> 
> Nope NetclientState is not necessarily a NIC, it can be a peer of the NIC (e.g
> network backend like tap, hubport etc).
> 
> 
> > Or has the code in net/can/ which I referenced been obsoleted?
> 
> 
> No :)
> 
> 
> >
> > Sharing this link for SocketCAN(in case you want to have a look):
> https://www.kernel.org/doc/Documentation/networking/can.txt. Section 4
> talks on how CAN Socket is intended to work. Equivalent file is located as
> net/can-socketcan.c.
> 
> 
> Thanks for the pointer.
> 
> I agree that there's no need to change that part. But we may consider to
> unify the CanBusClientState and NetClientState in the future.
> 
> 
> >
> > Regards,
> > Vikram
> >
> >> -----Original Message-----
> >> From: Jason Wang <jasowang@redhat.com>
> >> Sent: Monday, February 10, 2020 7:09 PM
> >> To: Vikram Garhwal <fnuv@xilinx.com>; qemu-devel@nongnu.org
> >> Subject: Re: [PATCH 1/1] hw/net/can: Introduce Xlnx ZynqMP CAN
> controller
> >> for QEMU
> >>
> >>
> >> On 2020/2/11 上午5:45, Vikram Garhwal wrote:
> >>>>> +                }
> >>>>> +            } else {
> >>>>> +                /* Normal mode Tx. */
> >>>>> +                generate_frame(&frame, data);
> >>>>> +
> >>>>> +                can_bus_client_send(&s->bus_client, &frame, 1);
> >>>> I had a quick glance at can_bus_client_send():
> >>>>
> >>>> It did:
> >>>>
> >>>>        QTAILQ_FOREACH(peer, &bus->clients, next) {
> >>>>            if (peer->info->can_receive(peer)) {
> >>>>                if (peer == client) {
> >>>>                    /* No loopback support for now */
> >>>>                    continue;
> >>>>                }
> >>>>                if (peer->info->receive(peer, frames, frames_cnt) > 0) {
> >>>>                    ret = 1;
> >>>>                }
> >>>>            }
> >>>>        }
> >>>>
> >>>> which looks not correct. We need to use qemu_send_packet() instead
> of
> >>>> calling peer->info->receive() directly which bypasses filters
> completely.
> >>> [Vikram Garhwal] Can you please elaborate it bit more on why do we
> need
> >> to filter outgoing message? So, I can either add a filter before sending
> the
> >> packets. I am unable to understand the use case for it. For any message
> which
> >> is incoming, we are filtering it for sure before storing in update_rx_fifo().
> >>
> >>
> >> I might be not clear, I meant the netfilters supported by qemu which
> allows
> >> you to attach a filter to a specific NetClientState, see
> >> qemu_send_packet_async_with_flags. It doesn't mean the filter
> implemented
> >> in your own NIC model.
> >>
> >> Thanks
> >>
> >>
> >>> Also, I can see existing CAN models like CAN sja1000 and CAN Kavser
> are
> >> using it same can_bus_client_send() function. However, this doesn't
> mean
> >> that it is the correct way to send & receive packets.


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-02-27 16:04 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-03 21:06 [PATCH 0/1] Introduce Xlnx ZynqMP CAN controller for QEMU Vikram Garhwal
2020-02-03 21:06 ` [PATCH 1/1] hw/net/can: " Vikram Garhwal
2020-02-05  5:46   ` Jason Wang
2020-02-06 18:39     ` Vikram Garhwal
2020-02-10 21:45     ` Vikram Garhwal
2020-02-11  3:09       ` Jason Wang
2020-02-25  6:22         ` Vikram Garhwal
2020-02-26  3:24           ` Jason Wang
2020-02-27 16:04             ` Vikram Garhwal
2020-02-05  3:17 ` [PATCH 0/1] " Jason Wang
2020-02-05  3:23   ` Vikram Garhwal
2020-02-05  5:48     ` Jason Wang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.