All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller
@ 2020-04-23  0:56 Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-23  0:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: Vikram Garhwal

 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-soc

Changelog:

v2 -> v3:
    Rectify the build issue.
    Rearrange the patch order.

v1 -> v2:
    Rename the CAN device state and address code style issues.
    Connect the CAN device to Xlnx-ZCU102 board.
    Add maintainer entry.
    Add QTEST for the CAN device.

Vikram Garhwal (4):
  hw/net/can: Introduce Xilinx ZynqMP CAN controller
  xlnx-zynqmp: Connect Xilinx ZynqMP CAN controller
  tests/qtest: Introduce tests for Xilinx ZyqnMP CAN controller
  MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN

 MAINTAINERS                      |    7 +
 hw/arm/xlnx-zynqmp.c             |   26 +
 hw/net/can/Makefile.objs         |    1 +
 hw/net/can/xlnx-zynqmp-can.c     | 1113 ++++++++++++++++++++++++++++++++++++++
 include/hw/arm/xlnx-zynqmp.h     |    3 +
 include/hw/net/xlnx-zynqmp-can.h |   76 +++
 tests/qtest/Makefile.include     |    2 +
 tests/qtest/xlnx-can-test.c      |  367 +++++++++++++
 8 files changed, 1595 insertions(+)
 create mode 100644 hw/net/can/xlnx-zynqmp-can.c
 create mode 100644 include/hw/net/xlnx-zynqmp-can.h
 create mode 100644 tests/qtest/xlnx-can-test.c

--
2.7.4



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

* [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-23  0:56 [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller Vikram Garhwal
@ 2020-04-23  0:56 ` Vikram Garhwal
  2020-04-28 16:28   ` Edgar E. Iglesias
  2020-04-28 19:20   ` Francisco Iglesias
  2020-04-23  0:56 ` [PATCH v3 2/4] xlnx-zynqmp: Connect " Vikram Garhwal
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-23  0:56 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Vikram Garhwal, Jason Wang, Alistair Francis,
	open list:Xilinx ZynqMP, Edgar E. Iglesias

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     | 1113 ++++++++++++++++++++++++++++++++++++++
 include/hw/net/xlnx-zynqmp-can.h |   76 +++
 3 files changed, 1190 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..31799c0
--- /dev/null
+++ b/hw/net/can/xlnx-zynqmp-can.c
@@ -0,0 +1,1113 @@
+/*
+ * QEMU model of the Xilinx CAN device.
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
+ * Pavel Pisa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), 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(XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+    can_update_irq(s);
+}
+
+static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s)
+{
+    /* Reset all the configuration registers. */
+    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
+    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
+    register_reset(
+              &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s)
+{
+    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
+    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
+
+    /* Put XlnxZynqMPCANState in configuration mode. */
+    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
+    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
+
+    can_update_irq(s);
+}
+
+static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
+{
+    /* 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 XlnxZynqMPCANState is set in normal mode.
+         */
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
+        /* Set wakeup interrupt bit. */
+        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
+                            wakeup_irq_val);
+    }
+
+    can_update_irq(s);
+}
+
+static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
+{
+    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
+    update_status_register_mode_bits(s);
+}
+
+static void generate_frame(qemu_can_frame *frame, uint32_t *data)
+{
+    frame->can_id = 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(XlnxZynqMPCANState *s)
+{
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
+                      " XlnxZynqMPCANState%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"
+                      " XlnxZynqMPCANState%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"
+                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
+                         s->cfg.ctrl_idx);
+        return false;
+    }
+
+    return true;
+}
+
+static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
+{
+    qemu_can_frame frame;
+    uint32_t data[CAN_FRAME_SIZE];
+    int i;
+    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
+                 * transmits a recessive bitstream on to the XlnxZynqMPCANState
+                 * Bus. Any message transmitted is looped back to the RX line
+                 * and acknowledged. The XlnxZynqMPCANState 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)
+{
+    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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 XlnxZynqMPCANState core will enter Normal,
+         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP, SNOOP
+         * register states.
+         */
+        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
+
+        /* 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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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(XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *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 bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
+{
+    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
+                                         bus_client);
+
+    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+        DB_PRINT("XlnxZynqMPCANState%d Controller is reset\n", s->cfg.ctrl_idx);
+        return false;
+    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
+        DB_PRINT("XlnxZynqMPCANState%d is disabled. Incoming messages will be"
+                 "discarded\n", s->cfg.ctrl_idx);
+        return false;
+    } else {
+        return true;
+    }
+}
+
+static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
+                               const qemu_can_frame *buf, size_t buf_size) {
+    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
+                                        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 XlnxZynqMPCANState bus\n");
+        return 0;
+    }
+    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
+        /*
+         * XlnxZynqMPCANState will not participate in normal bus communication
+         *  and does not receive any messages transmitted by other CAN nodes.
+         */
+        DB_PRINT("XlnxZynqMPCANState 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))) {
+        /*
+         * XlnxZynqMPCANState 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 XlnxZynqMPCANState is not configured"
+                  " 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(XlnxZynqMPCANState *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)
+{
+    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
+
+    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
+        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCANState"
+                   " 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)
+{
+    XlnxZynqMPCANState *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, XlnxZynqMPCANState),
+        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX),
+        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCANState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property xlnx_zynqmp_can_properties[] = {
+    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCANState, 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(XlnxZynqMPCANState),
+    .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..3038542
--- /dev/null
+++ b/include/hw/net/xlnx-zynqmp-can.h
@@ -0,0 +1,76 @@
+/*
+ * QEMU model of the Xilinx CAN device.
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
+ * Pavel Pisa.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), 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(XlnxZynqMPCANState, (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 XlnxZynqMPCANState {
+    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;
+
+} XlnxZynqMPCANState;
+
+#endif
-- 
2.7.4



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

* [PATCH v3 2/4] xlnx-zynqmp: Connect Xilinx ZynqMP CAN controller
  2020-04-23  0:56 [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
@ 2020-04-23  0:56 ` Vikram Garhwal
  2020-04-28 16:33   ` Edgar E. Iglesias
  2020-04-28 18:56   ` Francisco Iglesias
  2020-04-23  0:56 ` [PATCH v3 3/4] tests/qtest: Introduce tests for Xilinx ZyqnMP " Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN Vikram Garhwal
  3 siblings, 2 replies; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-23  0:56 UTC (permalink / raw)
  To: qemu-devel
  Cc: Edgar E. Iglesias, Vikram Garhwal, open list:Xilinx ZynqMP,
	Alistair Francis, Peter Maydell

Connect CAN0 and CAN1 to ZCU102 board.

Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
---
 hw/arm/xlnx-zynqmp.c         | 26 ++++++++++++++++++++++++++
 include/hw/arm/xlnx-zynqmp.h |  3 +++
 2 files changed, 29 insertions(+)

diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index b84d153..e5f0d9f 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -81,6 +81,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
     21, 22,
 };
 
+static const uint64_t can_addr[XLNX_ZYNQMP_NUM_CAN] = {
+    0xFF060000, 0xFF070000,
+};
+
+static const int can_intr[XLNX_ZYNQMP_NUM_CAN] = {
+    23, 24,
+};
+
 static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
     0xFF160000, 0xFF170000,
 };
@@ -254,6 +262,11 @@ static void xlnx_zynqmp_init(Object *obj)
                               TYPE_CADENCE_UART);
     }
 
+    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
+        sysbus_init_child_obj(obj, "can[*]", &s->can[i], sizeof(s->can[i]),
+                              TYPE_XLNX_ZYNQMP_CAN);
+    }
+
     sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata),
                           TYPE_SYSBUS_AHCI);
 
@@ -508,6 +521,19 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
                            gic_spi[uart_intr[i]]);
     }
 
+    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
+        object_property_set_int(OBJECT(&s->can[i]), i, "ctrl-idx",
+                                &error_abort);
+        object_property_set_bool(OBJECT(&s->can[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->can[i]), 0, can_addr[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->can[i]), 0,
+                           gic_spi[can_intr[i]]);
+    }
+
     object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports",
                             &error_abort);
     object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
index 53076fa..2be0ff9 100644
--- a/include/hw/arm/xlnx-zynqmp.h
+++ b/include/hw/arm/xlnx-zynqmp.h
@@ -22,6 +22,7 @@
 #include "hw/intc/arm_gic.h"
 #include "hw/net/cadence_gem.h"
 #include "hw/char/cadence_uart.h"
+#include "hw/net/xlnx-zynqmp-can.h"
 #include "hw/ide/ahci.h"
 #include "hw/sd/sdhci.h"
 #include "hw/ssi/xilinx_spips.h"
@@ -41,6 +42,7 @@
 #define XLNX_ZYNQMP_NUM_RPU_CPUS 2
 #define XLNX_ZYNQMP_NUM_GEMS 4
 #define XLNX_ZYNQMP_NUM_UARTS 2
+#define XLNX_ZYNQMP_NUM_CAN 2
 #define XLNX_ZYNQMP_NUM_SDHCI 2
 #define XLNX_ZYNQMP_NUM_SPIS 2
 #define XLNX_ZYNQMP_NUM_GDMA_CH 8
@@ -92,6 +94,7 @@ typedef struct XlnxZynqMPState {
 
     CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
     CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
+    XlnxZynqMPCANState    can[XLNX_ZYNQMP_NUM_CAN];
     SysbusAHCIState sata;
     SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
     XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
-- 
2.7.4



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

* [PATCH v3 3/4] tests/qtest: Introduce tests for Xilinx ZyqnMP CAN controller
  2020-04-23  0:56 [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 2/4] xlnx-zynqmp: Connect " Vikram Garhwal
@ 2020-04-23  0:56 ` Vikram Garhwal
  2020-04-23  0:56 ` [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN Vikram Garhwal
  3 siblings, 0 replies; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-23  0:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: Laurent Vivier, Paolo Bonzini, Thomas Huth, Vikram Garhwal

Qtest performs five tests on Xlnx-CAN controller:
	It checks communication between CAN0 and CAN1 via can-bus.
	Tests CAN in loopback, sleep and snoop mode.
	Tests CAN filtering for incoming messages.

Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
---
 tests/qtest/Makefile.include |   2 +
 tests/qtest/xlnx-can-test.c  | 367 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 369 insertions(+)
 create mode 100644 tests/qtest/xlnx-can-test.c

diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
index 9e5a51d..0996075 100644
--- a/tests/qtest/Makefile.include
+++ b/tests/qtest/Makefile.include
@@ -136,6 +136,7 @@ check-qtest-aarch64-$(CONFIG_TPM_TIS_SYSBUS) += tpm-tis-device-swtpm-test
 check-qtest-aarch64-y += numa-test
 check-qtest-aarch64-y += boot-serial-test
 check-qtest-aarch64-y += migration-test
+check-qtest-aarch64-y += xlnx-can-test
 
 # TODO: once aarch64 TCG is fixed on ARM 32 bit host, make test unconditional
 ifneq ($(ARCH),arm)
@@ -265,6 +266,7 @@ tests/qtest/bios-tables-test$(EXESUF): tests/qtest/bios-tables-test.o \
 	tests/qtest/boot-sector.o tests/qtest/acpi-utils.o $(libqos-obj-y)
 tests/qtest/pxe-test$(EXESUF): tests/qtest/pxe-test.o tests/qtest/boot-sector.o $(libqos-obj-y)
 tests/qtest/microbit-test$(EXESUF): tests/qtest/microbit-test.o
+tests/qtest/xlnx-can-test$(EXESUF): tests/qtest/xlnx-can-test.o
 tests/qtest/m25p80-test$(EXESUF): tests/qtest/m25p80-test.o
 tests/qtest/i440fx-test$(EXESUF): tests/qtest/i440fx-test.o $(libqos-pc-obj-y)
 tests/qtest/q35-test$(EXESUF): tests/qtest/q35-test.o $(libqos-pc-obj-y)
diff --git a/tests/qtest/xlnx-can-test.c b/tests/qtest/xlnx-can-test.c
new file mode 100644
index 0000000..c9e2efc
--- /dev/null
+++ b/tests/qtest/xlnx-can-test.c
@@ -0,0 +1,367 @@
+/*
+ *  Xilinx CAN qtest.
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * 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 "libqtest.h"
+
+/* Xlnx-CAN base address. */
+#define CAN0_BASE_ADDR          0xFF060000
+#define CAN1_BASE_ADDR          0xFF070000
+
+/* Register address in Xlnx-CAN. */
+#define R_SRR_OFFSET            0x00
+#define R_MSR_OFFSET            0x04
+#define R_SR_OFFSET             0x18
+#define R_ISR_OFFSET            0x1C
+#define R_ICR_OFFSET            0x24
+#define R_TXID_OFFSET           0x30
+#define R_TXDLC_OFFSET          0x34
+#define R_TXDATA1_OFFSET        0x38
+#define R_TXDATA2_OFFSET        0x3C
+#define R_RXID_OFFSET           0x50
+#define R_RXDLC_OFFSET          0x54
+#define R_RXDATA1_OFFSET        0x58
+#define R_RXDATA2_OFFSET        0x5C
+#define R_AFR                   0x60
+#define R_AFMR1                 0x64
+#define R_AFIR1                 0x68
+#define R_AFMR2                 0x6C
+#define R_AFIR2                 0x70
+#define R_AFMR3                 0x74
+#define R_AFIR3                 0x78
+#define R_AFMR4                 0x7C
+#define R_AFIR4                 0x80
+
+/* CAN modes. */
+#define CONFIG_MODE             0x00
+#define NORMAL_MODE             0x00
+#define LOOPBACK_MODE           0x02
+#define SNOOP_MODE              0x04
+#define SLEEP_MODE              0x01
+#define ENABLE_CAN              (1 << 1)
+#define STATUS_NORMAL_MODE      (1 << 3)
+#define STATUS_LOOPBACK_MODE    (1 << 1)
+#define STATUS_SNOOP_MODE       (1 << 12)
+#define STATUS_SLEEP_MODE       (1 << 2)
+#define ISR_TXOK                (1 << 1)
+#define ISR_RXOK                (1 << 4)
+
+static void match_rx_tx_data(uint32_t *buf_tx, uint32_t *buf_rx,
+                             uint8_t can_timestamp)
+{
+    uint16_t size = 0;
+    uint8_t len = 4;
+
+    while (size < len) {
+        if (R_RXID_OFFSET + 4 * size == R_RXDLC_OFFSET)  {
+            g_assert_cmpint(buf_rx[size], ==, buf_tx[size] + can_timestamp);
+        } else {
+            g_assert_cmpint(buf_rx[size], ==, buf_tx[size]);
+        }
+
+        size++;
+    }
+}
+
+static void read_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_rx)
+{
+    uint32_t int_status;
+
+    /* Read the interrupt on CAN rx. */
+    int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_RXOK;
+
+    g_assert_cmpint(int_status, ==, ISR_RXOK);
+
+    /* Read the RX register data for CAN. */
+    buf_rx[0] = qtest_readl(qts, can_base_addr + R_RXID_OFFSET);
+    buf_rx[1] = qtest_readl(qts, can_base_addr + R_RXDLC_OFFSET);
+    buf_rx[2] = qtest_readl(qts, can_base_addr + R_RXDATA1_OFFSET);
+    buf_rx[3] = qtest_readl(qts, can_base_addr + R_RXDATA2_OFFSET);
+
+    /* Clear the RX interrupt. */
+    qtest_writel(qts, CAN1_BASE_ADDR + R_ICR_OFFSET, ISR_RXOK);
+}
+
+static void send_data(QTestState *qts, uint64_t can_base_addr, uint32_t *buf_tx)
+{
+    uint32_t int_status;
+
+    /* Write the TX register data for CAN. */
+    qtest_writel(qts, can_base_addr + R_TXID_OFFSET, buf_tx[0]);
+    qtest_writel(qts, can_base_addr + R_TXDLC_OFFSET, buf_tx[1]);
+    qtest_writel(qts, can_base_addr + R_TXDATA1_OFFSET, buf_tx[2]);
+    qtest_writel(qts, can_base_addr + R_TXDATA2_OFFSET, buf_tx[3]);
+
+    /* Read the interrupt on CAN for tx. */
+    int_status = qtest_readl(qts, can_base_addr + R_ISR_OFFSET) & ISR_TXOK;
+
+    g_assert_cmpint(int_status, ==, ISR_TXOK);
+
+    /* Clear the interrupt for tx. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_ICR_OFFSET, ISR_TXOK);
+}
+
+/*
+ * This test will be transferring data from CAN0 and CAN1 through canbus. CAN0
+ * initiate the data transfer to can-bus, CAN1 receives the data. Test compares
+ * the data sent from CAN0 with received on CAN1.
+ */
+static void test_can_bus(void)
+{
+    uint32_t buf_tx[4] = {0xFF, 0x80000000, 0x12345678, 0x87654321};
+    uint32_t buf_rx[4] = {0x00, 0x00, 0x00, 0x00};
+    uint32_t status = 0;
+    uint8_t can_timestamp = 0;
+
+    QTestState *qts = qtest_init("-m 4G -machine xlnx-zcu102"
+                " -object can-bus,id=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus0"
+                );
+
+    /* Configure the CAN0 and CAN1. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+
+    /* Check here if CAN0 and CAN1 are in normal mode. */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    send_data(qts, CAN0_BASE_ADDR, buf_tx);
+
+    can_timestamp += 1;
+
+    read_data(qts, CAN1_BASE_ADDR, buf_rx);
+    match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
+
+    qtest_quit(qts);
+}
+
+/*
+ * This test is performing loopback mode on CAN0 and CAN1. Data sent from TX of
+ * each CAN0 and CAN1 are compared with RX register data for respective CAN.
+ */
+static void test_can_loopback(void)
+{
+    uint32_t buf_tx[4] = {0xFF, 0x80000000, 0x12345678, 0x87654321};
+    uint32_t buf_rx[4] = {0x00, 0x00, 0x00, 0x00};
+    uint32_t status = 0;
+
+    QTestState *qts = qtest_init("-machine xlnx-zcu102"
+                " -object can-bus,id=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus0"
+                );
+
+    /* Configure the CAN0 in loopback mode. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, LOOPBACK_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+
+    /* Check here if CAN0 is set in loopback mode. */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+
+    g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE);
+
+    send_data(qts, CAN0_BASE_ADDR, buf_tx);
+    read_data(qts, CAN0_BASE_ADDR, buf_rx);
+    match_rx_tx_data(buf_tx, buf_rx, 0);
+
+    /* Configure the CAN1 in loopback mode. */
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, LOOPBACK_MODE);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+
+    /* Check here if CAN1 is set in loopback mode. */
+    status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
+
+    g_assert_cmpint(status, ==, STATUS_LOOPBACK_MODE);
+
+    send_data(qts, CAN1_BASE_ADDR, buf_tx);
+    read_data(qts, CAN1_BASE_ADDR, buf_rx);
+    match_rx_tx_data(buf_tx, buf_rx, 0);
+
+    qtest_quit(qts);
+}
+
+/*
+ * Enable filters for CAN1. This will filter incoming messages with ID. In this
+ * test message will pass through filter 2.
+ */
+static void test_can_filter(void)
+{
+    uint32_t buf_tx[4] = {0x14, 0x80000000, 0x12345678, 0x87654321};
+    uint32_t buf_rx[4] = {0x00, 0x00, 0x00, 0x00};
+    uint32_t status = 0;
+    uint8_t can_timestamp = 0;
+
+    QTestState *qts = qtest_init("-m 4G -machine xlnx-zcu102"
+                " -object can-bus,id=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus0"
+                );
+
+    /* Configure the CAN0 and CAN1. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+
+    /* Check here if CAN0 and CAN1 are in normal mode. */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    /* Set filter for CAN1 for incoming messages. */
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFR, 0x0);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR1, 0xF7);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR1, 0x121F);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR2, 0x5431);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR2, 0x14);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR3, 0x1234);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR3, 0x5431);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFMR4, 0xFFF);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFIR4, 0x1234);
+
+    qtest_writel(qts, CAN1_BASE_ADDR + R_AFR, 0xF);
+
+    send_data(qts, CAN0_BASE_ADDR, buf_tx);
+
+    can_timestamp += 1;
+
+    read_data(qts, CAN1_BASE_ADDR, buf_rx);
+    match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
+
+    qtest_quit(qts);
+}
+
+/* Testing sleep mode on CAN0 while CAN1 is in normal mode. */
+static void test_can_sleepmode(void)
+{
+    uint32_t buf_tx[4] = {0x14, 0x80000000, 0x12345678, 0x87654321};
+    uint32_t buf_rx[4] = {0x00, 0x00, 0x00, 0x00};
+    uint32_t status = 0;
+    uint8_t can_timestamp = 0;
+
+    QTestState *qts = qtest_init("-m 4G -machine xlnx-zcu102"
+                " -object can-bus,id=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus0"
+                );
+
+    /* Configure the CAN0. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, SLEEP_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+
+    /* Check here if CAN0 is in SNOOP mode and CAN1 in normal mode. */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_SLEEP_MODE);
+
+    status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    send_data(qts, CAN1_BASE_ADDR, buf_tx);
+
+    /*
+     * Once CAN1 sends data on can-bus. CAN0 should exit sleep mode.
+     * Check the CAN0 status now. It should exit the sleep mode and receive the
+     * incoming data.
+     */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    can_timestamp += 1;
+
+    read_data(qts, CAN0_BASE_ADDR, buf_rx);
+
+    match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
+
+    qtest_quit(qts);
+}
+
+/* Testing Snoop mode on CAN0 while CAN1 is in normal mode. */
+static void test_can_snoopmode(void)
+{
+    uint32_t buf_tx[4] = {0x14, 0x80000000, 0x12345678, 0x87654321};
+    uint32_t buf_rx[4] = {0x00, 0x00, 0x00, 0x00};
+    uint32_t status = 0;
+    uint8_t can_timestamp = 0;
+
+    QTestState *qts = qtest_init("-m 4G -machine xlnx-zcu102"
+                " -object can-bus,id=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus0,value=canbus0"
+                " -global driver=xlnx.zynqmp-can,property=canbus1,value=canbus0"
+                );
+
+    /* Configure the CAN0. */
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, CONFIG_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_MSR_OFFSET, SNOOP_MODE);
+    qtest_writel(qts, CAN0_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+
+    qtest_writel(qts, CAN1_BASE_ADDR + R_SRR_OFFSET, ENABLE_CAN);
+    qtest_writel(qts, CAN1_BASE_ADDR + R_MSR_OFFSET, NORMAL_MODE);
+
+    /* Check here if CAN0 is in SNOOP mode and CAN1 in normal mode. */
+    status = qtest_readl(qts, CAN0_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_SNOOP_MODE);
+
+    status = qtest_readl(qts, CAN1_BASE_ADDR + R_SR_OFFSET);
+    g_assert_cmpint(status, ==, STATUS_NORMAL_MODE);
+
+    send_data(qts, CAN1_BASE_ADDR, buf_tx);
+
+    can_timestamp += 1;
+
+    read_data(qts, CAN0_BASE_ADDR, buf_rx);
+
+    match_rx_tx_data(buf_tx, buf_rx, can_timestamp);
+
+    qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/net/can/can_bus", test_can_bus);
+    qtest_add_func("/net/can/can_loopback", test_can_loopback);
+    qtest_add_func("/net/can/can_filter", test_can_filter);
+    qtest_add_func("/net/can/can_test_snoopmode", test_can_snoopmode);
+    qtest_add_func("/net/can/can_test_sleepmode", test_can_sleepmode);
+
+    return g_test_run();
+}
-- 
2.7.4



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

* [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN
  2020-04-23  0:56 [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller Vikram Garhwal
                   ` (2 preceding siblings ...)
  2020-04-23  0:56 ` [PATCH v3 3/4] tests/qtest: Introduce tests for Xilinx ZyqnMP " Vikram Garhwal
@ 2020-04-23  0:56 ` Vikram Garhwal
  2020-04-28 16:37   ` Edgar E. Iglesias
  3 siblings, 1 reply; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-23  0:56 UTC (permalink / raw)
  To: qemu-devel; +Cc: Vikram Garhwal

Add myself as Xilinx CAN maintainer.

Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8cbc1fa..6223573 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1433,6 +1433,13 @@ F: hw/net/opencores_eth.c
 
 Devices
 -------
+Xilinx CAN
+M: Vikram Garhwal <fnu.vikram@xilinx.com>
+S: Maintained
+F: hw/net/can/xlnx-*
+F: include/hw/net/xlnx-*
+F: tests/qtest/xlnx-can-test*
+
 EDU
 M: Jiri Slaby <jslaby@suse.cz>
 S: Maintained
-- 
2.7.4



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

* Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
@ 2020-04-28 16:28   ` Edgar E. Iglesias
  2020-04-28 17:35     ` Vikram Garhwal
  2020-04-28 19:20   ` Francisco Iglesias
  1 sibling, 1 reply; 13+ messages in thread
From: Edgar E. Iglesias @ 2020-04-28 16:28 UTC (permalink / raw)
  To: Vikram Garhwal
  Cc: damien.hedde, Peter Maydell, Jason Wang, Alistair Francis,
	qemu-devel, francisco.iglesias, open list:Xilinx ZynqMP

On Wed, Apr 22, 2020 at 05:56:06PM -0700, 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.

+ Damien (there's a reset question for you inline)
+ Francisco

Nice Vikram!

I've copied Francisco since he was quite invovled with this too.

Some comments inline.

Cheers,
Edgar

> 
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
>  hw/net/can/Makefile.objs         |    1 +
>  hw/net/can/xlnx-zynqmp-can.c     | 1113 ++++++++++++++++++++++++++++++++++++++
>  include/hw/net/xlnx-zynqmp-can.h |   76 +++
>  3 files changed, 1190 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..31799c0
> --- /dev/null
> +++ b/hw/net/can/xlnx-zynqmp-can.c
> @@ -0,0 +1,1113 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2020 Xilinx Inc.
> + *
> + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> + *
> + * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
> + * Pavel Pisa
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), 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(XlnxZynqMPCANState *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];

Since you're working with s->regs[], it would be cleaner to make
irq the same type as the regs (i.e uint32_t).

> +
> +    qemu_set_irq(s->irq, irq);
> +}
> +
> +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +
> +    can_update_irq(s);
> +}
> +
> +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s)
> +{
> +    /* Reset all the configuration registers. */
> +    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
> +    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
> +    register_reset(
> +              &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s)
> +{
> +    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
> +    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
> +
> +    /* Put XlnxZynqMPCANState in configuration mode. */
> +    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
> +
> +    can_update_irq(s);
> +}
> +
> +static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
> +{
> +    /* 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);

Looks like you have too many layers of parentheses here?

> +
> +    /* 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));

Here too.


> +
> +    /* 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 XlnxZynqMPCANState is set in normal mode.
> +         */
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
> +        /* Set wakeup interrupt bit. */
> +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> +                            wakeup_irq_val);
> +    }
> +
> +    can_update_irq(s);
> +}
> +
> +static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
> +{
> +    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
> +    update_status_register_mode_bits(s);
> +}
> +
> +static void generate_frame(qemu_can_frame *frame, uint32_t *data)
> +{
> +    frame->can_id = 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(XlnxZynqMPCANState *s)
> +{
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
> +                      " XlnxZynqMPCANState%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"
> +                      " XlnxZynqMPCANState%d is in configuration mode.Reset the"

Missing space after dot "mode.Reset".                                  ^^^

> +                      " 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"
> +                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
> +                         s->cfg.ctrl_idx);
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
> +{
> +    qemu_can_frame frame;
> +    uint32_t data[CAN_FRAME_SIZE];
> +    int i;
> +    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
> +                 * transmits a recessive bitstream on to the XlnxZynqMPCANState
> +                 * Bus. Any message transmitted is looped back to the RX line
> +                 * and acknowledged. The XlnxZynqMPCANState 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)
> +{
> +    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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);
> +

I would drop this blank line.


> +    } else {
> +        /*
> +         * Leave config mode. Now XlnxZynqMPCANState core will enter Normal,
> +         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP, SNOOP
> +         * register states.
> +         */
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> +
> +        /* 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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *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);
> +

I would drop this blank line...

> +    } 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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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);
> +

Another odd blank line.

> +    } else {
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
> +    }
> +}
> +
> +static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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);

Should this be a LOG_GUEST_ERROR?


> +    }
> +
> +    return s->regs[reg_idx];
> +}
> +
> +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCANState *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");

Doesn't this trigger any SW visible event like an interrupt, set some status bit?
We should probably LOG_GUEST_ERROR here too...

> +    }
> +
> +    /* 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)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++i) {
> +        register_reset(&s->reg_info[i]);

These register resets will end up triggering update_irq() calls but that
interrupt propagation may get lost because devices reset in random order
so the receiving device may get reset after this one. This is a problem
with many models I think but with Damiens new reset API we can trigger
update_irq() post-reset.

@Damien, are there any examples Vikram could look at to solve this?


> +    }
> +
> +    /*
> +     * Reset FIFOs when CAN model is reset. This will clear the fifo writes
> +     * done by post_write which gets called from register_reset function,
> +     * post_write handle will not be able to trigger tx because CAN will be
> +     * disabled when software_reset_register is cleared first.
> +     */
> +    fifo32_reset(&s->rx_fifo);
> +    fifo32_reset(&s->tx_fifo);
> +    fifo32_reset(&s->txhpb_fifo);
> +}
> +
> +static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
> +{
> +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> +                                         bus_client);
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        DB_PRINT("XlnxZynqMPCANState%d Controller is reset\n", s->cfg.ctrl_idx);
> +        return false;
> +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
> +        DB_PRINT("XlnxZynqMPCANState%d is disabled. Incoming messages will be"
> +                 "discarded\n", s->cfg.ctrl_idx);
> +        return false;
> +    } else {
> +        return true;
> +    }
> +}
> +
> +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> +                               const qemu_can_frame *buf, size_t buf_size) {
> +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> +                                        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 XlnxZynqMPCANState bus\n");
> +        return 0;
> +    }
> +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> +        /*
> +         * XlnxZynqMPCANState will not participate in normal bus communication
> +         *  and does not receive any messages transmitted by other CAN nodes.
> +         */
> +        DB_PRINT("XlnxZynqMPCANState 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))) {
> +        /*
> +         * XlnxZynqMPCANState 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 XlnxZynqMPCANState is not configured"
> +                  " 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(XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> +
> +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCANState"
> +                   " 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)
> +{
> +    XlnxZynqMPCANState *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, XlnxZynqMPCANState),
> +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX),
> +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCANState),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static Property xlnx_zynqmp_can_properties[] = {
> +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCANState, 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(XlnxZynqMPCANState),
> +    .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..3038542
> --- /dev/null
> +++ b/include/hw/net/xlnx-zynqmp-can.h
> @@ -0,0 +1,76 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2020 Xilinx Inc.
> + *
> + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> + *
> + * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
> + * Pavel Pisa.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), 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

We should probably name these guarding macros according to the filename.


> +
> +#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(XlnxZynqMPCANState, (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 XlnxZynqMPCANState {
> +    SysBusDevice        parent_obj;
> +    MemoryRegion        iomem;
> +
> +    qemu_irq            irq;
> +
> +    CanBusClientState   bus_client;
> +    CanBusState         *canbus[MAX_CAN_CTRLS];
> +
> +    struct {
> +        uint8_t         ctrl_idx;
> +   } cfg;

Indentation looks odd here.


> +
> +    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;

These ones look misaligned.

> +
> +} XlnxZynqMPCANState;
> +
> +#endif
> -- 
> 2.7.4
> 


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

* Re: [PATCH v3 2/4] xlnx-zynqmp: Connect Xilinx ZynqMP CAN controller
  2020-04-23  0:56 ` [PATCH v3 2/4] xlnx-zynqmp: Connect " Vikram Garhwal
@ 2020-04-28 16:33   ` Edgar E. Iglesias
  2020-04-28 18:56   ` Francisco Iglesias
  1 sibling, 0 replies; 13+ messages in thread
From: Edgar E. Iglesias @ 2020-04-28 16:33 UTC (permalink / raw)
  To: Vikram Garhwal
  Cc: Peter Maydell, Alistair Francis, qemu-devel, open list:Xilinx ZynqMP

On Wed, Apr 22, 2020 at 05:56:07PM -0700, Vikram Garhwal wrote:
> Connect CAN0 and CAN1 to ZCU102 board.

Comments inline.



> 
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
>  hw/arm/xlnx-zynqmp.c         | 26 ++++++++++++++++++++++++++
>  include/hw/arm/xlnx-zynqmp.h |  3 +++
>  2 files changed, 29 insertions(+)
> 
> diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
> index b84d153..e5f0d9f 100644
> --- a/hw/arm/xlnx-zynqmp.c
> +++ b/hw/arm/xlnx-zynqmp.c
> @@ -81,6 +81,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
>      21, 22,
>  };
>  
> +static const uint64_t can_addr[XLNX_ZYNQMP_NUM_CAN] = {
> +    0xFF060000, 0xFF070000,
> +};
> +
> +static const int can_intr[XLNX_ZYNQMP_NUM_CAN] = {
> +    23, 24,
> +};
> +
>  static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
>      0xFF160000, 0xFF170000,
>  };
> @@ -254,6 +262,11 @@ static void xlnx_zynqmp_init(Object *obj)
>                                TYPE_CADENCE_UART);
>      }
>  
> +    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
> +        sysbus_init_child_obj(obj, "can[*]", &s->can[i], sizeof(s->can[i]),
> +                              TYPE_XLNX_ZYNQMP_CAN);
> +    }
> +
>      sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata),
>                            TYPE_SYSBUS_AHCI);
>  
> @@ -508,6 +521,19 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
>                             gic_spi[uart_intr[i]]);
>      }
>  
> +    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
> +        object_property_set_int(OBJECT(&s->can[i]), i, "ctrl-idx",
> +                                &error_abort);
> +        object_property_set_bool(OBJECT(&s->can[i]), true, "realized", &err);
> +        if (err) {
> +            error_propagate(errp, err);
> +            return;
> +        }
> +        sysbus_mmio_map(SYS_BUS_DEVICE(&s->can[i]), 0, can_addr[i]);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->can[i]), 0,
> +                           gic_spi[can_intr[i]]);
> +    }
> +
>      object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports",
>                              &error_abort);
>      object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
> diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
> index 53076fa..2be0ff9 100644
> --- a/include/hw/arm/xlnx-zynqmp.h
> +++ b/include/hw/arm/xlnx-zynqmp.h
> @@ -22,6 +22,7 @@
>  #include "hw/intc/arm_gic.h"
>  #include "hw/net/cadence_gem.h"
>  #include "hw/char/cadence_uart.h"
> +#include "hw/net/xlnx-zynqmp-can.h"
>  #include "hw/ide/ahci.h"
>  #include "hw/sd/sdhci.h"
>  #include "hw/ssi/xilinx_spips.h"
> @@ -41,6 +42,7 @@
>  #define XLNX_ZYNQMP_NUM_RPU_CPUS 2
>  #define XLNX_ZYNQMP_NUM_GEMS 4
>  #define XLNX_ZYNQMP_NUM_UARTS 2
> +#define XLNX_ZYNQMP_NUM_CAN 2
>  #define XLNX_ZYNQMP_NUM_SDHCI 2
>  #define XLNX_ZYNQMP_NUM_SPIS 2
>  #define XLNX_ZYNQMP_NUM_GDMA_CH 8
> @@ -92,6 +94,7 @@ typedef struct XlnxZynqMPState {
>  
>      CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
>      CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
> +    XlnxZynqMPCANState    can[XLNX_ZYNQMP_NUM_CAN];

All those spaces between type and array look odd here.

Without those spaces:
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>




>      SysbusAHCIState sata;
>      SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
>      XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
> -- 
> 2.7.4
> 


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

* Re: [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN
  2020-04-23  0:56 ` [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN Vikram Garhwal
@ 2020-04-28 16:37   ` Edgar E. Iglesias
  0 siblings, 0 replies; 13+ messages in thread
From: Edgar E. Iglesias @ 2020-04-28 16:37 UTC (permalink / raw)
  To: Vikram Garhwal; +Cc: qemu-devel

On Wed, Apr 22, 2020 at 05:56:09PM -0700, Vikram Garhwal wrote:
> Add myself as Xilinx CAN maintainer.

Hi Vikram,

I think it would be good to add Francisco Iglesias <francisco.iglesias@xilinx.com>
as co-maintainer since he's been involved with this too.

Anyway:
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>



> 
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
>  MAINTAINERS | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8cbc1fa..6223573 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1433,6 +1433,13 @@ F: hw/net/opencores_eth.c
>  
>  Devices
>  -------
> +Xilinx CAN
> +M: Vikram Garhwal <fnu.vikram@xilinx.com>
> +S: Maintained
> +F: hw/net/can/xlnx-*
> +F: include/hw/net/xlnx-*
> +F: tests/qtest/xlnx-can-test*
> +
>  EDU
>  M: Jiri Slaby <jslaby@suse.cz>
>  S: Maintained
> -- 
> 2.7.4
> 
> 


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

* RE: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-28 16:28   ` Edgar E. Iglesias
@ 2020-04-28 17:35     ` Vikram Garhwal
  2020-04-30 13:16       ` Edgar E. Iglesias
  0 siblings, 1 reply; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-28 17:35 UTC (permalink / raw)
  To: Edgar E. Iglesias
  Cc: damien.hedde, Peter Maydell, Jason Wang, Alistair Francis,
	qemu-devel, open list:Xilinx ZynqMP, Francisco Eduardo Iglesias

Hi Edgar,
Thanks for review.

> -----Original Message-----
> From: Edgar E. Iglesias <edgar.iglesias@gmail.com>
> Sent: Tuesday, April 28, 2020 9:28 AM
> To: Vikram Garhwal <fnuv@xilinx.com>
> Cc: qemu-devel@nongnu.org; Jason Wang <jasowang@redhat.com>;
> Alistair Francis <alistair@alistair23.me>; Peter Maydell
> <peter.maydell@linaro.org>; open list:Xilinx ZynqMP <qemu-
> arm@nongnu.org>; damien.hedde@greensocs.com; Francisco Eduardo
> Iglesias <figlesia@xilinx.com>
> Subject: Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN
> controller
> 
> On Wed, Apr 22, 2020 at 05:56:06PM -0700, 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.
> 
> + Damien (there's a reset question for you inline) Francisco
> 
> Nice Vikram!
> 
> I've copied Francisco since he was quite invovled with this too.
> 
> Some comments inline.
> 
> Cheers,
> Edgar
> 
> >
> > Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> > ---
> >  hw/net/can/Makefile.objs         |    1 +
> >  hw/net/can/xlnx-zynqmp-can.c     | 1113
> ++++++++++++++++++++++++++++++++++++++
> >  include/hw/net/xlnx-zynqmp-can.h |   76 +++
> >  3 files changed, 1190 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..31799c0
> > --- /dev/null
> > +++ b/hw/net/can/xlnx-zynqmp-can.c
> > @@ -0,0 +1,1113 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2020 Xilinx Inc.
> > + *
> > + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> > + *
> > + * Based on QEMU CAN Device emulation implemented by Jin Yang,
> Deniz
> > +Eren and
> > + * Pavel Pisa
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), 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(XlnxZynqMPCANState *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];
> 
> Since you're working with s->regs[], it would be cleaner to make irq the
> same type as the regs (i.e uint32_t).
[Vikram Garhwal] I will change the irq data type.
> 
> > +
> > +    qemu_set_irq(s->irq, irq);
> > +}
> > +
> > +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64) {
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s) {
> > +    /* Reset all the configuration registers. */
> > +    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
> > +    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
> > +    register_reset(
> > +              &s-
> >reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s) {
> > +    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
> > +    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
> > +
> > +    /* Put XlnxZynqMPCANState in configuration mode. */
> > +    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST,
> 0);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
> {
> > +    /* 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);
> 
> Looks like you have too many layers of parentheses here?
> 
[Vikram Garhwal] I will make this look more cleaner.
> > +
> > +    /* 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));
> 
> Here too.
> 
> 
> > +
> > +    /* 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 XlnxZynqMPCANState is set in normal
> mode.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
> > +        /* Set wakeup interrupt bit. */
> > +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> > +                            wakeup_irq_val);
> > +    }
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static void can_exit_sleep_mode(XlnxZynqMPCANState *s) {
> > +    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
> > +    update_status_register_mode_bits(s);
> > +}
> > +
> > +static void generate_frame(qemu_can_frame *frame, uint32_t *data) {
> > +    frame->can_id = 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(XlnxZynqMPCANState *s) {
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCANState%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"
> > +                      " XlnxZynqMPCANState%d is in configuration mode.Reset
> the"
> 
> Missing space after dot "mode.Reset".                                  ^^^
[Vikram Garhwal] I will add the missing space in v4.
> 
> > +                      " 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"
> > +                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
> > +                         s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) {
> > +    qemu_can_frame frame;
> > +    uint32_t data[CAN_FRAME_SIZE];
> > +    int i;
> > +    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
> > +                 * transmits a recessive bitstream on to the
> XlnxZynqMPCANState
> > +                 * Bus. Any message transmitted is looped back to the RX line
> > +                 * and acknowledged. The XlnxZynqMPCANState 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)
> > +{
> > +    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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);
> > +
> 
> I would drop this blank line.
[Vikram Garhwal] I will remove it in next patch.
> 
> 
> > +    } else {
> > +        /*
> > +         * Leave config mode. Now XlnxZynqMPCANState core will enter
> Normal,
> > +         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP,
> SNOOP
> > +         * register states.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> > +
> > +        /* 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)
> > +{
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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)
> > +{
> > +    XlnxZynqMPCANState *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)
> > +{
> > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *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);
> > +
> 
> I would drop this blank line...
[Vikram Garhwal] Will remove this in v4.
> 
> > +    } 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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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);
> > +
> 
> Another odd blank line.
[Vikram Garhwal] Will remove this in v4.
> 
> > +    } else {
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
> > +    }
> > +}
> > +
> > +static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t
> > +val64) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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);
> 
> Should this be a LOG_GUEST_ERROR?
[Vikram Garhwal] Thanks for catching this. This one affects functionality of CAN so LOG_GUEST_ERROR will do better here.
> 
> 
> > +    }
> > +
> > +    return s->regs[reg_idx];
> > +}
> > +
> > +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64) {
> > +    XlnxZynqMPCANState *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");
> 
> Doesn't this trigger any SW visible event like an interrupt, set some status
> bit?
[Vikram Garhwal] Yeah, at the end of this function it calls can_update_irq this will set the interrupt. 
> We should probably LOG_GUEST_ERROR here too...
> 
> > +    }
> > +
> > +    /* 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) {
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->reg_info); ++i) {
> > +        register_reset(&s->reg_info[i]);
> 
> These register resets will end up triggering update_irq() calls but that
> interrupt propagation may get lost because devices reset in random order
> so the receiving device may get reset after this one. This is a problem with
> many models I think but with Damiens new reset API we can trigger
> update_irq() post-reset.
> 
> @Damien, are there any examples Vikram could look at to solve this?
> 
> 
> > +    }
> > +
> > +    /*
> > +     * Reset FIFOs when CAN model is reset. This will clear the fifo writes
> > +     * done by post_write which gets called from register_reset function,
> > +     * post_write handle will not be able to trigger tx because CAN will be
> > +     * disabled when software_reset_register is cleared first.
> > +     */
> > +    fifo32_reset(&s->rx_fifo);
> > +    fifo32_reset(&s->tx_fifo);
> > +    fifo32_reset(&s->txhpb_fifo);
> > +}
> > +
> > +static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client) {
> > +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> > +                                         bus_client);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("XlnxZynqMPCANState%d Controller is reset\n", s-
> >cfg.ctrl_idx);
> > +        return false;
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER,
> CEN)) == 0) {
> > +        DB_PRINT("XlnxZynqMPCANState%d is disabled. Incoming messages
> will be"
> > +                 "discarded\n", s->cfg.ctrl_idx);
> > +        return false;
> > +    } else {
> > +        return true;
> > +    }
> > +}
> > +
> > +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> > +                               const qemu_can_frame *buf, size_t buf_size) {
> > +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> > +                                        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 XlnxZynqMPCANState bus\n");
> > +        return 0;
> > +    }
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +        /*
> > +         * XlnxZynqMPCANState will not participate in normal bus
> communication
> > +         *  and does not receive any messages transmitted by other CAN
> nodes.
> > +         */
> > +        DB_PRINT("XlnxZynqMPCANState 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))) {
> > +        /*
> > +         * XlnxZynqMPCANState 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 XlnxZynqMPCANState is not
> configured"
> > +                  " 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(XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> > +
> > +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> > +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCANState"
> > +                   " 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) {
> > +    XlnxZynqMPCANState *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, XlnxZynqMPCANState),
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState,
> XLNX_ZYNQMP_CAN_R_MAX),
> > +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCANState),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property xlnx_zynqmp_can_properties[] = {
> > +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCANState, 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(XlnxZynqMPCANState),
> > +    .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..3038542
> > --- /dev/null
> > +++ b/include/hw/net/xlnx-zynqmp-can.h
> > @@ -0,0 +1,76 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2020 Xilinx Inc.
> > + *
> > + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> > + *
> > + * Based on QEMU CAN Device emulation implemented by Jin Yang,
> Deniz
> > +Eren and
> > + * Pavel Pisa.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), 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
> 
> We should probably name these guarding macros according to the
> filename.
[Vikram Garhwal] I will change this to XLNX_ZYNQMP_CAN_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(XlnxZynqMPCANState, (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 XlnxZynqMPCANState {
> > +    SysBusDevice        parent_obj;
> > +    MemoryRegion        iomem;
> > +
> > +    qemu_irq            irq;
> > +
> > +    CanBusClientState   bus_client;
> > +    CanBusState         *canbus[MAX_CAN_CTRLS];
> > +
> > +    struct {
> > +        uint8_t         ctrl_idx;
> > +   } cfg;
> 
> Indentation looks odd here.
[Vikram Garhwal] Yeah, it is not properly spaced.
> 
> 
> > +
> > +    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;
> 
> These ones look misaligned.
[Vikram Garhwal] I will correct this one too.
> 
> > +
> > +} XlnxZynqMPCANState;
> > +
> > +#endif
> > --
> > 2.7.4
> >


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

* Re: [PATCH v3 2/4] xlnx-zynqmp: Connect Xilinx ZynqMP CAN controller
  2020-04-23  0:56 ` [PATCH v3 2/4] xlnx-zynqmp: Connect " Vikram Garhwal
  2020-04-28 16:33   ` Edgar E. Iglesias
@ 2020-04-28 18:56   ` Francisco Iglesias
  1 sibling, 0 replies; 13+ messages in thread
From: Francisco Iglesias @ 2020-04-28 18:56 UTC (permalink / raw)
  To: Vikram Garhwal
  Cc: Edgar E. Iglesias, open list:Xilinx ZynqMP, qemu-devel,
	Alistair Francis, Peter Maydell

Hi Vikram,

A couple of more comments:

On the git summary:
s/controller/controllers/

On [2020 Apr 22] Wed 17:56:07, Vikram Garhwal wrote:
> Connect CAN0 and CAN1 to ZCU102 board.

Perhaps also:
s/to ZCU102 board/on the ZynqMP/

(even though zcu102 is the only board using it at the moment).

Best regards,
Francisco

> 
> Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> ---
>  hw/arm/xlnx-zynqmp.c         | 26 ++++++++++++++++++++++++++
>  include/hw/arm/xlnx-zynqmp.h |  3 +++
>  2 files changed, 29 insertions(+)
> 
> diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
> index b84d153..e5f0d9f 100644
> --- a/hw/arm/xlnx-zynqmp.c
> +++ b/hw/arm/xlnx-zynqmp.c
> @@ -81,6 +81,14 @@ static const int uart_intr[XLNX_ZYNQMP_NUM_UARTS] = {
>      21, 22,
>  };
>  
> +static const uint64_t can_addr[XLNX_ZYNQMP_NUM_CAN] = {
> +    0xFF060000, 0xFF070000,
> +};
> +
> +static const int can_intr[XLNX_ZYNQMP_NUM_CAN] = {
> +    23, 24,
> +};
> +
>  static const uint64_t sdhci_addr[XLNX_ZYNQMP_NUM_SDHCI] = {
>      0xFF160000, 0xFF170000,
>  };
> @@ -254,6 +262,11 @@ static void xlnx_zynqmp_init(Object *obj)
>                                TYPE_CADENCE_UART);
>      }
>  
> +    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
> +        sysbus_init_child_obj(obj, "can[*]", &s->can[i], sizeof(s->can[i]),
> +                              TYPE_XLNX_ZYNQMP_CAN);
> +    }
> +
>      sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata),
>                            TYPE_SYSBUS_AHCI);
>  
> @@ -508,6 +521,19 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
>                             gic_spi[uart_intr[i]]);
>      }
>  
> +    for (i = 0; i < XLNX_ZYNQMP_NUM_CAN; i++) {
> +        object_property_set_int(OBJECT(&s->can[i]), i, "ctrl-idx",
> +                                &error_abort);
> +        object_property_set_bool(OBJECT(&s->can[i]), true, "realized", &err);
> +        if (err) {
> +            error_propagate(errp, err);
> +            return;
> +        }
> +        sysbus_mmio_map(SYS_BUS_DEVICE(&s->can[i]), 0, can_addr[i]);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->can[i]), 0,
> +                           gic_spi[can_intr[i]]);
> +    }
> +
>      object_property_set_int(OBJECT(&s->sata), SATA_NUM_PORTS, "num-ports",
>                              &error_abort);
>      object_property_set_bool(OBJECT(&s->sata), true, "realized", &err);
> diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
> index 53076fa..2be0ff9 100644
> --- a/include/hw/arm/xlnx-zynqmp.h
> +++ b/include/hw/arm/xlnx-zynqmp.h
> @@ -22,6 +22,7 @@
>  #include "hw/intc/arm_gic.h"
>  #include "hw/net/cadence_gem.h"
>  #include "hw/char/cadence_uart.h"
> +#include "hw/net/xlnx-zynqmp-can.h"
>  #include "hw/ide/ahci.h"
>  #include "hw/sd/sdhci.h"
>  #include "hw/ssi/xilinx_spips.h"
> @@ -41,6 +42,7 @@
>  #define XLNX_ZYNQMP_NUM_RPU_CPUS 2
>  #define XLNX_ZYNQMP_NUM_GEMS 4
>  #define XLNX_ZYNQMP_NUM_UARTS 2
> +#define XLNX_ZYNQMP_NUM_CAN 2
>  #define XLNX_ZYNQMP_NUM_SDHCI 2
>  #define XLNX_ZYNQMP_NUM_SPIS 2
>  #define XLNX_ZYNQMP_NUM_GDMA_CH 8
> @@ -92,6 +94,7 @@ typedef struct XlnxZynqMPState {
>  
>      CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS];
>      CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS];
> +    XlnxZynqMPCANState    can[XLNX_ZYNQMP_NUM_CAN];
>      SysbusAHCIState sata;
>      SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
>      XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
> -- 
> 2.7.4
> 
> 


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

* Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
  2020-04-28 16:28   ` Edgar E. Iglesias
@ 2020-04-28 19:20   ` Francisco Iglesias
  2020-04-29 17:27     ` Vikram Garhwal
  1 sibling, 1 reply; 13+ messages in thread
From: Francisco Iglesias @ 2020-04-28 19:20 UTC (permalink / raw)
  To: Vikram Garhwal
  Cc: Peter Maydell, Jason Wang, Alistair Francis, qemu-devel,
	open list:Xilinx ZynqMP, Edgar E. Iglesias

Hi Vikram,

A couple of more comments here also.

On [2020 Apr 22] Wed 17:56: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     | 1113 ++++++++++++++++++++++++++++++++++++++
>  include/hw/net/xlnx-zynqmp-can.h |   76 +++
>  3 files changed, 1190 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..31799c0
> --- /dev/null
> +++ b/hw/net/can/xlnx-zynqmp-can.c
> @@ -0,0 +1,1113 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2020 Xilinx Inc.
> + *
> + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> + *
> + * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
> + * Pavel Pisa
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), 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(XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +
> +    can_update_irq(s);
> +}
> +
> +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s)
> +{
> +    /* Reset all the configuration registers. */
> +    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
> +    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
> +    register_reset(
> +              &s->reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s)
> +{
> +    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
> +    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
> +
> +    /* Put XlnxZynqMPCANState in configuration mode. */
> +    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
> +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
> +
> +    can_update_irq(s);
> +}
> +
> +static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
> +{
> +    /* 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 XlnxZynqMPCANState is set in normal mode.
> +         */
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
> +        /* Set wakeup interrupt bit. */
> +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> +                            wakeup_irq_val);
> +    }
> +
> +    can_update_irq(s);
> +}
> +
> +static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
> +{
> +    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
> +    update_status_register_mode_bits(s);
> +}
> +
> +static void generate_frame(qemu_can_frame *frame, uint32_t *data)
> +{
> +    frame->can_id = 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(XlnxZynqMPCANState *s)
> +{
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data while"
> +                      " XlnxZynqMPCANState%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"
> +                      " XlnxZynqMPCANState%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"
> +                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
> +                         s->cfg.ctrl_idx);
> +        return false;
> +    }
> +
> +    return true;
> +}
> +
> +static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
> +{
> +    qemu_can_frame frame;
> +    uint32_t data[CAN_FRAME_SIZE];
> +    int i;
> +    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
> +                 * transmits a recessive bitstream on to the XlnxZynqMPCANState
> +                 * Bus. Any message transmitted is looped back to the RX line
> +                 * and acknowledged. The XlnxZynqMPCANState 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)
> +{
> +    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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 XlnxZynqMPCANState core will enter Normal,
> +         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP, SNOOP
> +         * register states.
> +         */
> +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> +
> +        /* 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)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> +    uint32_t val = val64;
> +    uint8_t multi_mode = 0;

The 0 init can be removed (since the variable is always set below): 

uint8_t multi_mode;


> +
> +    /*
> +     * 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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s, const qemu_can_frame *frame)
> +{
> +    uint32_t filter_pass = 0;

We could consider a bool here (but above is ok for me also).

bool filter_pass = false;


> +
> +    /* 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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *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 bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
> +{
> +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> +                                         bus_client);
> +
> +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> +        DB_PRINT("XlnxZynqMPCANState%d Controller is reset\n", s->cfg.ctrl_idx);
> +        return false;
> +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
> +        DB_PRINT("XlnxZynqMPCANState%d is disabled. Incoming messages will be"
> +                 "discarded\n", s->cfg.ctrl_idx);
> +        return false;
> +    } else {
> +        return true;
> +    }
> +}
> +
> +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> +                               const qemu_can_frame *buf, size_t buf_size) {
> +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> +                                        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 XlnxZynqMPCANState bus\n");
> +        return 0;
> +    }
> +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> +        /*
> +         * XlnxZynqMPCANState will not participate in normal bus communication
> +         *  and does not receive any messages transmitted by other CAN nodes.
> +         */
> +        DB_PRINT("XlnxZynqMPCANState 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))) {
> +        /*
> +         * XlnxZynqMPCANState 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 XlnxZynqMPCANState is not configured"
> +                  " 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(XlnxZynqMPCANState *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)
> +{
> +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> +
> +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCANState"
> +                   " 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");

Add a 'return' here (as above).

Best regards,
Francisco Iglesias

> +        }
> +
> +    } 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)
> +{
> +    XlnxZynqMPCANState *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, XlnxZynqMPCANState),
> +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX),
> +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCANState),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static Property xlnx_zynqmp_can_properties[] = {
> +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCANState, 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(XlnxZynqMPCANState),
> +    .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..3038542
> --- /dev/null
> +++ b/include/hw/net/xlnx-zynqmp-can.h
> @@ -0,0 +1,76 @@
> +/*
> + * QEMU model of the Xilinx CAN device.
> + *
> + * Copyright (c) 2020 Xilinx Inc.
> + *
> + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> + *
> + * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
> + * Pavel Pisa.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), 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(XlnxZynqMPCANState, (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 XlnxZynqMPCANState {
> +    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;
> +
> +} XlnxZynqMPCANState;
> +
> +#endif
> -- 
> 2.7.4
> 
> 


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

* RE: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-28 19:20   ` Francisco Iglesias
@ 2020-04-29 17:27     ` Vikram Garhwal
  0 siblings, 0 replies; 13+ messages in thread
From: Vikram Garhwal @ 2020-04-29 17:27 UTC (permalink / raw)
  To: Francisco Iglesias
  Cc: Peter Maydell, Jason Wang, Alistair Francis, qemu-devel,
	open list:Xilinx ZynqMP, Edgar E. Iglesias

Hi Francisco,
Thanks for the review. Comments inline.

Regards,
Vikram

> -----Original Message-----
> From: Francisco Iglesias <frasse.iglesias@gmail.com>
> Sent: Tuesday, April 28, 2020 12:20 PM
> To: Vikram Garhwal <fnuv@xilinx.com>
> Cc: qemu-devel@nongnu.org; Peter Maydell <peter.maydell@linaro.org>;
> Jason Wang <jasowang@redhat.com>; Alistair Francis
> <alistair@alistair23.me>; open list:Xilinx ZynqMP <qemu-
> arm@nongnu.org>; Edgar E. Iglesias <edgar.iglesias@gmail.com>
> Subject: Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN
> controller
> 
> Hi Vikram,
> 
> A couple of more comments here also.
> 
> On [2020 Apr 22] Wed 17:56: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     | 1113
> ++++++++++++++++++++++++++++++++++++++
> >  include/hw/net/xlnx-zynqmp-can.h |   76 +++
> >  3 files changed, 1190 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..31799c0
> > --- /dev/null
> > +++ b/hw/net/can/xlnx-zynqmp-can.c
> > @@ -0,0 +1,1113 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2020 Xilinx Inc.
> > + *
> > + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> > + *
> > + * Based on QEMU CAN Device emulation implemented by Jin Yang,
> Deniz
> > +Eren and
> > + * Pavel Pisa
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), 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(XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s) {
> > +    /* Reset all the configuration registers. */
> > +    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
> > +    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
> > +    register_reset(
> > +              &s-
> >reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s) {
> > +    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
> > +    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
> > +
> > +    /* Put XlnxZynqMPCANState in configuration mode. */
> > +    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW,
> 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
> > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST,
> 0);
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
> {
> > +    /* 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 XlnxZynqMPCANState is set in normal
> mode.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
> > +        /* Set wakeup interrupt bit. */
> > +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> > +                            wakeup_irq_val);
> > +    }
> > +
> > +    can_update_irq(s);
> > +}
> > +
> > +static void can_exit_sleep_mode(XlnxZynqMPCANState *s) {
> > +    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
> > +    update_status_register_mode_bits(s);
> > +}
> > +
> > +static void generate_frame(qemu_can_frame *frame, uint32_t *data) {
> > +    frame->can_id = 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(XlnxZynqMPCANState *s) {
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> while"
> > +                      " XlnxZynqMPCANState%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"
> > +                      " XlnxZynqMPCANState%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"
> > +                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
> > +                         s->cfg.ctrl_idx);
> > +        return false;
> > +    }
> > +
> > +    return true;
> > +}
> > +
> > +static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) {
> > +    qemu_can_frame frame;
> > +    uint32_t data[CAN_FRAME_SIZE];
> > +    int i;
> > +    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
> > +                 * transmits a recessive bitstream on to the
> XlnxZynqMPCANState
> > +                 * Bus. Any message transmitted is looped back to the RX line
> > +                 * and acknowledged. The XlnxZynqMPCANState 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)
> > +{
> > +    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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 XlnxZynqMPCANState core will enter
> Normal,
> > +         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP,
> SNOOP
> > +         * register states.
> > +         */
> > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> > +
> > +        /* 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)
> > +{
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > +    uint32_t val = val64;
> > +    uint8_t multi_mode = 0;
> 
> The 0 init can be removed (since the variable is always set below):
> 
> uint8_t multi_mode;
> 
[Vikram Garhwal] init to 0 will be removed in v4.
> 
> > +
> > +    /*
> > +     * 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) {
> > +    XlnxZynqMPCANState *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)
> > +{
> > +    XlnxZynqMPCANState *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)
> > +{
> > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s, const
> > +qemu_can_frame *frame) {
> > +    uint32_t filter_pass = 0;
> 
> We could consider a bool here (but above is ok for me also).
> 
> bool filter_pass = false;
[Vikram Garhwal] Yeah, bool is better fit for the filter_pass. 
> 
> 
> > +
> > +    /* 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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *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 bool xlnx_zynqmp_can_can_receive(CanBusClientState *client) {
> > +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> > +                                         bus_client);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > +        DB_PRINT("XlnxZynqMPCANState%d Controller is reset\n", s-
> >cfg.ctrl_idx);
> > +        return false;
> > +    } else if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER,
> CEN)) == 0) {
> > +        DB_PRINT("XlnxZynqMPCANState%d is disabled. Incoming messages
> will be"
> > +                 "discarded\n", s->cfg.ctrl_idx);
> > +        return false;
> > +    } else {
> > +        return true;
> > +    }
> > +}
> > +
> > +static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
> > +                               const qemu_can_frame *buf, size_t buf_size) {
> > +    XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
> > +                                        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 XlnxZynqMPCANState bus\n");
> > +        return 0;
> > +    }
> > +    if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, LBACK)) {
> > +        /*
> > +         * XlnxZynqMPCANState will not participate in normal bus
> communication
> > +         *  and does not receive any messages transmitted by other CAN
> nodes.
> > +         */
> > +        DB_PRINT("XlnxZynqMPCANState 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))) {
> > +        /*
> > +         * XlnxZynqMPCANState 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 XlnxZynqMPCANState is not
> configured"
> > +                  " 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(XlnxZynqMPCANState *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) {
> > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(dev);
> > +
> > +    if (s->cfg.ctrl_idx > MAX_CAN_CTRLS) {
> > +        error_setg(errp, "ctrl-idx: %d exceeds max XlnxZynqMPCANState"
> > +                   " 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");
> 
> Add a 'return' here (as above).
[Vikram Garhwal] Good catch! This one should return as it will be throwing error. Addressing this in v4.
> 
> Best regards,
> Francisco Iglesias
> 
> > +        }
> > +
> > +    } 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) {
> > +    XlnxZynqMPCANState *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, XlnxZynqMPCANState),
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState,
> XLNX_ZYNQMP_CAN_R_MAX),
> > +        VMSTATE_UINT8(cfg.ctrl_idx, XlnxZynqMPCANState),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property xlnx_zynqmp_can_properties[] = {
> > +    DEFINE_PROP_UINT8("ctrl-idx", XlnxZynqMPCANState, 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(XlnxZynqMPCANState),
> > +    .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..3038542
> > --- /dev/null
> > +++ b/include/hw/net/xlnx-zynqmp-can.h
> > @@ -0,0 +1,76 @@
> > +/*
> > + * QEMU model of the Xilinx CAN device.
> > + *
> > + * Copyright (c) 2020 Xilinx Inc.
> > + *
> > + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> > + *
> > + * Based on QEMU CAN Device emulation implemented by Jin Yang,
> Deniz
> > +Eren and
> > + * Pavel Pisa.
> > + *
> > + * Permission is hereby granted, free of charge, to any person
> > +obtaining a copy
> > + * of this software and associated documentation files (the
> > +"Software"), 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(XlnxZynqMPCANState, (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 XlnxZynqMPCANState {
> > +    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;
> > +
> > +} XlnxZynqMPCANState;
> > +
> > +#endif
> > --
> > 2.7.4
> >
> >


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

* Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN controller
  2020-04-28 17:35     ` Vikram Garhwal
@ 2020-04-30 13:16       ` Edgar E. Iglesias
  0 siblings, 0 replies; 13+ messages in thread
From: Edgar E. Iglesias @ 2020-04-30 13:16 UTC (permalink / raw)
  To: Vikram Garhwal
  Cc: damien.hedde, Peter Maydell, Jason Wang, Alistair Francis,
	qemu-devel, open list:Xilinx ZynqMP, Francisco Eduardo Iglesias

On Tue, Apr 28, 2020 at 05:35:46PM +0000, Vikram Garhwal wrote:
> Hi Edgar,
> Thanks for review.
> 
> > -----Original Message-----
> > From: Edgar E. Iglesias <edgar.iglesias@gmail.com>
> > Sent: Tuesday, April 28, 2020 9:28 AM
> > To: Vikram Garhwal <fnuv@xilinx.com>
> > Cc: qemu-devel@nongnu.org; Jason Wang <jasowang@redhat.com>;
> > Alistair Francis <alistair@alistair23.me>; Peter Maydell
> > <peter.maydell@linaro.org>; open list:Xilinx ZynqMP <qemu-
> > arm@nongnu.org>; damien.hedde@greensocs.com; Francisco Eduardo
> > Iglesias <figlesia@xilinx.com>
> > Subject: Re: [PATCH v3 1/4] hw/net/can: Introduce Xilinx ZynqMP CAN
> > controller
> > 
> > On Wed, Apr 22, 2020 at 05:56:06PM -0700, 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.
> > 
> > + Damien (there's a reset question for you inline) Francisco
> > 
> > Nice Vikram!
> > 
> > I've copied Francisco since he was quite invovled with this too.
> > 
> > Some comments inline.
> > 
> > Cheers,
> > Edgar
> > 
> > >
> > > Signed-off-by: Vikram Garhwal <fnu.vikram@xilinx.com>
> > > ---
> > >  hw/net/can/Makefile.objs         |    1 +
> > >  hw/net/can/xlnx-zynqmp-can.c     | 1113
> > ++++++++++++++++++++++++++++++++++++++
> > >  include/hw/net/xlnx-zynqmp-can.h |   76 +++
> > >  3 files changed, 1190 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..31799c0
> > > --- /dev/null
> > > +++ b/hw/net/can/xlnx-zynqmp-can.c
> > > @@ -0,0 +1,1113 @@
> > > +/*
> > > + * QEMU model of the Xilinx CAN device.
> > > + *
> > > + * Copyright (c) 2020 Xilinx Inc.
> > > + *
> > > + * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
> > > + *
> > > + * Based on QEMU CAN Device emulation implemented by Jin Yang,
> > Deniz
> > > +Eren and
> > > + * Pavel Pisa
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person
> > > +obtaining a copy
> > > + * of this software and associated documentation files (the
> > > +"Software"), 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(XlnxZynqMPCANState *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];
> > 
> > Since you're working with s->regs[], it would be cleaner to make irq the
> > same type as the regs (i.e uint32_t).
> [Vikram Garhwal] I will change the irq data type.
> > 
> > > +
> > > +    qemu_set_irq(s->irq, irq);
> > > +}
> > > +
> > > +static void can_ier_post_write(RegisterInfo *reg, uint64_t val64) {
> > > +    XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
> > > +
> > > +    can_update_irq(s);
> > > +}
> > > +
> > > +static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val64)
> > > +{
> > > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *s) {
> > > +    /* Reset all the configuration registers. */
> > > +    register_reset(&s->reg_info[R_SOFTWARE_RESET_REGISTER]);
> > > +    register_reset(&s->reg_info[R_MODE_SELECT_REGISTER]);
> > > +    register_reset(
> > > +              &s-
> > >reg_info[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_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(XlnxZynqMPCANState *s) {
> > > +    register_reset(&s->reg_info[R_ERROR_COUNTER_REGISTER]);
> > > +    register_reset(&s->reg_info[R_ERROR_STATUS_REGISTER]);
> > > +
> > > +    /* Put XlnxZynqMPCANState in configuration mode. */
> > > +    ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> > 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF,
> > 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR,
> > 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW,
> > 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
> > > +    ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST,
> > 0);
> > > +
> > > +    can_update_irq(s);
> > > +}
> > > +
> > > +static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
> > {
> > > +    /* 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);
> > 
> > Looks like you have too many layers of parentheses here?
> > 
> [Vikram Garhwal] I will make this look more cleaner.
> > > +
> > > +    /* 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));
> > 
> > Here too.
> > 
> > 
> > > +
> > > +    /* 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 XlnxZynqMPCANState is set in normal
> > mode.
> > > +         */
> > > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
> > > +        /* Set wakeup interrupt bit. */
> > > +        ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
> > > +                            wakeup_irq_val);
> > > +    }
> > > +
> > > +    can_update_irq(s);
> > > +}
> > > +
> > > +static void can_exit_sleep_mode(XlnxZynqMPCANState *s) {
> > > +    ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
> > > +    update_status_register_mode_bits(s);
> > > +}
> > > +
> > > +static void generate_frame(qemu_can_frame *frame, uint32_t *data) {
> > > +    frame->can_id = 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(XlnxZynqMPCANState *s) {
> > > +    if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
> > > +        qemu_log_mask(LOG_GUEST_ERROR, "Attempting to transfer data
> > while"
> > > +                      " XlnxZynqMPCANState%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"
> > > +                      " XlnxZynqMPCANState%d is in configuration mode.Reset
> > the"
> > 
> > Missing space after dot "mode.Reset".                                  ^^^
> [Vikram Garhwal] I will add the missing space in v4.
> > 
> > > +                      " 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"
> > > +                        " XlnxZynqMPCANState%d is in SNOOP MODE\n",
> > > +                         s->cfg.ctrl_idx);
> > > +        return false;
> > > +    }
> > > +
> > > +    return true;
> > > +}
> > > +
> > > +static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo) {
> > > +    qemu_can_frame frame;
> > > +    uint32_t data[CAN_FRAME_SIZE];
> > > +    int i;
> > > +    bool can_tx = 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 is in loopback. In Loopback mode, the CAN core
> > > +                 * transmits a recessive bitstream on to the
> > XlnxZynqMPCANState
> > > +                 * Bus. Any message transmitted is looped back to the RX line
> > > +                 * and acknowledged. The XlnxZynqMPCANState 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)
> > > +{
> > > +    XlnxZynqMPCANState *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 XlnxZynqMPCANState%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);
> > > +
> > 
> > I would drop this blank line.
> [Vikram Garhwal] I will remove it in next patch.
> > 
> > 
> > > +    } else {
> > > +        /*
> > > +         * Leave config mode. Now XlnxZynqMPCANState core will enter
> > Normal,
> > > +         * Sleep, snoop or Loopback mode depending upon LBACK, SLEEP,
> > SNOOP
> > > +         * register states.
> > > +         */
> > > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
> > > +
> > > +        /* 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)
> > > +{
> > > +    XlnxZynqMPCANState *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) {
> > > +    XlnxZynqMPCANState *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)
> > > +{
> > > +    XlnxZynqMPCANState *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)
> > > +{
> > > +    XlnxZynqMPCANState *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(XlnxZynqMPCANState *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);
> > > +
> > 
> > I would drop this blank line...
> [Vikram Garhwal] Will remove this in v4.
> > 
> > > +    } 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) {
> > > +    XlnxZynqMPCANState *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) {
> > > +    XlnxZynqMPCANState *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);
> > > +
> > 
> > Another odd blank line.
> [Vikram Garhwal] Will remove this in v4.
> > 
> > > +    } else {
> > > +        ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
> > > +    }
> > > +}
> > > +
> > > +static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t
> > > +val64) {
> > > +    XlnxZynqMPCANState *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) {
> > > +    XlnxZynqMPCANState *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);
> > 
> > Should this be a LOG_GUEST_ERROR?
> [Vikram Garhwal] Thanks for catching this. This one affects functionality of CAN so LOG_GUEST_ERROR will do better here.
> > 
> > 
> > > +    }
> > > +
> > > +    return s->regs[reg_idx];
> > > +}
> > > +
> > > +static void can_tx_post_write(RegisterInfo *reg, uint64_t val64) {
> > > +    XlnxZynqMPCANState *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");
> > 
> > Doesn't this trigger any SW visible event like an interrupt, set some status
> > bit?
> [Vikram Garhwal] Yeah, at the end of this function it calls can_update_irq this will set the interrupt. 
> > We should probably LOG_GUEST_ERROR here too...

Not really, can_update_irq flags if the FIFO is full but it does not flag
anything if we lost data because SW tried to send something while the FIFO
was full. I couldn't see anything in the specs for that case but
you may want to LOG_GUEST_ERROR the else branch of the if-statement
rather than DB_PRINT.

Cheers,
Edgar



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

end of thread, other threads:[~2020-04-30 13:21 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-23  0:56 [PATCH v3 0/4] Introduce Xilinx ZynqMP CAN controller Vikram Garhwal
2020-04-23  0:56 ` [PATCH v3 1/4] hw/net/can: " Vikram Garhwal
2020-04-28 16:28   ` Edgar E. Iglesias
2020-04-28 17:35     ` Vikram Garhwal
2020-04-30 13:16       ` Edgar E. Iglesias
2020-04-28 19:20   ` Francisco Iglesias
2020-04-29 17:27     ` Vikram Garhwal
2020-04-23  0:56 ` [PATCH v3 2/4] xlnx-zynqmp: Connect " Vikram Garhwal
2020-04-28 16:33   ` Edgar E. Iglesias
2020-04-28 18:56   ` Francisco Iglesias
2020-04-23  0:56 ` [PATCH v3 3/4] tests/qtest: Introduce tests for Xilinx ZyqnMP " Vikram Garhwal
2020-04-23  0:56 ` [PATCH v3 4/4] MAINTAINERS: Add maintainer entry for Xilinx ZynqMP CAN Vikram Garhwal
2020-04-28 16:37   ` Edgar E. Iglesias

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.