aboutsummaryrefslogtreecommitdiffstats
path: root/hw/net/can
diff options
context:
space:
mode:
Diffstat (limited to 'hw/net/can')
-rw-r--r--hw/net/can/can_kvaser_pci.c324
-rw-r--r--hw/net/can/can_mioe3680_pci.c267
-rw-r--r--hw/net/can/can_pcm3680_pci.c268
-rw-r--r--hw/net/can/can_sja1000.c988
-rw-r--r--hw/net/can/can_sja1000.h147
-rw-r--r--hw/net/can/ctu_can_fd_frame.h189
-rw-r--r--hw/net/can/ctu_can_fd_regs.h971
-rw-r--r--hw/net/can/ctucan_core.c687
-rw-r--r--hw/net/can/ctucan_core.h126
-rw-r--r--hw/net/can/ctucan_pci.c281
-rw-r--r--hw/net/can/meson.build7
-rw-r--r--hw/net/can/trace-events9
-rw-r--r--hw/net/can/trace.h1
-rw-r--r--hw/net/can/xlnx-zynqmp-can.c1160
14 files changed, 5425 insertions, 0 deletions
diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c
new file mode 100644
index 000000000..168b3a620
--- /dev/null
+++ b/hw/net/can/can_kvaser_pci.c
@@ -0,0 +1,324 @@
+/*
+ * Kvaser PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Partially based on educational PCIexpress APOHW hardware
+ * emulator used fro class A0B36APO at CTU FEE course by
+ * Rostislav Lisovy and Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * 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 "qemu/event_notifier.h"
+#include "qemu/module.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+#include "qom/object.h"
+
+#define TYPE_CAN_PCI_DEV "kvaser_pci"
+
+typedef struct KvaserPCIState KvaserPCIState;
+DECLARE_INSTANCE_CHECKER(KvaserPCIState, KVASER_PCI_DEV,
+ TYPE_CAN_PCI_DEV)
+
+#ifndef KVASER_PCI_VENDOR_ID1
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#endif
+
+#ifndef KVASER_PCI_DEVICE_ID1
+#define KVASER_PCI_DEVICE_ID1 0x8406
+#endif
+
+#define KVASER_PCI_S5920_RANGE 0x80
+#define KVASER_PCI_SJA_RANGE 0x80
+#define KVASER_PCI_XILINX_RANGE 0x8
+
+#define KVASER_PCI_BYTES_PER_SJA 0x20
+
+#define S5920_OMB 0x0C
+#define S5920_IMB 0x1C
+#define S5920_MBEF 0x34
+#define S5920_INTCSR 0x38
+#define S5920_RCR 0x3C
+#define S5920_PTCR 0x60
+
+#define S5920_INTCSR_ADDON_INTENABLE_M 0x2000
+#define S5920_INTCSR_INTERRUPT_ASSERTED_M 0x800000
+
+#define KVASER_PCI_XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define KVASER_PCI_XILINX_VERSION_NUMBER 13
+
+struct KvaserPCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion s5920_io;
+ MemoryRegion sja_io;
+ MemoryRegion xilinx_io;
+
+ CanSJA1000State sja_state;
+ qemu_irq irq;
+
+ uint32_t s5920_intcsr;
+ uint32_t s5920_irqstate;
+
+ CanBusState *canbus;
+};
+
+static void kvaser_pci_irq_handler(void *opaque, int irq_num, int level)
+{
+ KvaserPCIState *d = (KvaserPCIState *)opaque;
+
+ d->s5920_irqstate = level;
+ if (d->s5920_intcsr & S5920_INTCSR_ADDON_INTENABLE_M) {
+ pci_set_irq(&d->dev, level);
+ }
+}
+
+static void kvaser_pci_reset(DeviceState *dev)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(dev);
+ CanSJA1000State *s = &d->sja_state;
+
+ can_sja_hardware_reset(s);
+}
+
+static uint64_t kvaser_pci_s5920_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ uint64_t val;
+
+ switch (addr) {
+ case S5920_INTCSR:
+ val = d->s5920_intcsr;
+ val &= ~S5920_INTCSR_INTERRUPT_ASSERTED_M;
+ if (d->s5920_irqstate) {
+ val |= S5920_INTCSR_INTERRUPT_ASSERTED_M;
+ }
+ return val;
+ }
+ return 0;
+}
+
+static void kvaser_pci_s5920_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+
+ switch (addr) {
+ case S5920_INTCSR:
+ if (d->s5920_irqstate &&
+ ((d->s5920_intcsr ^ data) & S5920_INTCSR_ADDON_INTENABLE_M)) {
+ pci_set_irq(&d->dev, !!(data & S5920_INTCSR_ADDON_INTENABLE_M));
+ }
+ d->s5920_intcsr = data;
+ break;
+ }
+}
+
+static uint64_t kvaser_pci_sja_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state;
+
+ if (addr >= KVASER_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void kvaser_pci_sja_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ KvaserPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state;
+
+ if (addr >= KVASER_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static uint64_t kvaser_pci_xilinx_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (addr) {
+ case KVASER_PCI_XILINX_VERINT:
+ return (KVASER_PCI_XILINX_VERSION_NUMBER << 4) | 0;
+ }
+
+ return 0;
+}
+
+static void kvaser_pci_xilinx_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+
+}
+
+static const MemoryRegionOps kvaser_pci_s5920_io_ops = {
+ .read = kvaser_pci_s5920_io_read,
+ .write = kvaser_pci_s5920_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static const MemoryRegionOps kvaser_pci_sja_io_ops = {
+ .read = kvaser_pci_sja_io_read,
+ .write = kvaser_pci_sja_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps kvaser_pci_xilinx_io_ops = {
+ .read = kvaser_pci_xilinx_io_read,
+ .write = kvaser_pci_xilinx_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void kvaser_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
+ CanSJA1000State *s = &d->sja_state;
+ uint8_t *pci_conf;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = qemu_allocate_irq(kvaser_pci_irq_handler, d, 0);
+
+ can_sja_init(s, d->irq);
+
+ if (can_sja_connect_to_bus(s, d->canbus) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+
+ memory_region_init_io(&d->s5920_io, OBJECT(d), &kvaser_pci_s5920_io_ops,
+ d, "kvaser_pci-s5920", KVASER_PCI_S5920_RANGE);
+ memory_region_init_io(&d->sja_io, OBJECT(d), &kvaser_pci_sja_io_ops,
+ d, "kvaser_pci-sja", KVASER_PCI_SJA_RANGE);
+ memory_region_init_io(&d->xilinx_io, OBJECT(d), &kvaser_pci_xilinx_io_ops,
+ d, "kvaser_pci-xilinx", KVASER_PCI_XILINX_RANGE);
+
+ pci_register_bar(&d->dev, /*BAR*/ 0, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->s5920_io);
+ pci_register_bar(&d->dev, /*BAR*/ 1, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io);
+ pci_register_bar(&d->dev, /*BAR*/ 2, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->xilinx_io);
+}
+
+static void kvaser_pci_exit(PCIDevice *pci_dev)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(pci_dev);
+ CanSJA1000State *s = &d->sja_state;
+
+ can_sja_disconnect(s);
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_kvaser_pci = {
+ .name = "kvaser_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, KvaserPCIState),
+ /* Load this before sja_state. */
+ VMSTATE_UINT32(s5920_intcsr, KvaserPCIState),
+ VMSTATE_STRUCT(sja_state, KvaserPCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void kvaser_pci_instance_init(Object *obj)
+{
+ KvaserPCIState *d = KVASER_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus", TYPE_CAN_BUS,
+ (Object **)&d->canbus,
+ qdev_prop_allow_set_link_before_realize,
+ 0);
+}
+
+static void kvaser_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = kvaser_pci_realize;
+ k->exit = kvaser_pci_exit;
+ k->vendor_id = KVASER_PCI_VENDOR_ID1;
+ k->device_id = KVASER_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x00ff00;
+ dc->desc = "Kvaser PCICANx";
+ dc->vmsd = &vmstate_kvaser_pci;
+ dc->reset = kvaser_pci_reset;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo kvaser_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(KvaserPCIState),
+ .class_init = kvaser_pci_class_init,
+ .instance_init = kvaser_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void kvaser_pci_register_types(void)
+{
+ type_register_static(&kvaser_pci_info);
+}
+
+type_init(kvaser_pci_register_types)
diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c
new file mode 100644
index 000000000..7a79e2605
--- /dev/null
+++ b/hw/net/can/can_mioe3680_pci.c
@@ -0,0 +1,267 @@
+/*
+ * MIOe-3680 PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang 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 "qemu/event_notifier.h"
+#include "qemu/module.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+#include "qom/object.h"
+
+#define TYPE_CAN_PCI_DEV "mioe3680_pci"
+
+typedef struct Mioe3680PCIState Mioe3680PCIState;
+DECLARE_INSTANCE_CHECKER(Mioe3680PCIState, MIOe3680_PCI_DEV,
+ TYPE_CAN_PCI_DEV)
+
+/* the PCI device and vendor IDs */
+#ifndef MIOe3680_PCI_VENDOR_ID1
+#define MIOe3680_PCI_VENDOR_ID1 0x13fe
+#endif
+
+#ifndef MIOe3680_PCI_DEVICE_ID1
+#define MIOe3680_PCI_DEVICE_ID1 0xc302
+#endif
+
+#define MIOe3680_PCI_SJA_COUNT 2
+#define MIOe3680_PCI_SJA_RANGE 0x400
+
+#define MIOe3680_PCI_BYTES_PER_SJA 0x80
+
+struct Mioe3680PCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion sja_io[MIOe3680_PCI_SJA_COUNT];
+
+ CanSJA1000State sja_state[MIOe3680_PCI_SJA_COUNT];
+ qemu_irq irq;
+
+ char *model; /* The model that support, only SJA1000 now. */
+ CanBusState *canbus[MIOe3680_PCI_SJA_COUNT];
+};
+
+static void mioe3680_pci_reset(DeviceState *dev)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(dev);
+ int i;
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_hardware_reset(&d->sja_state[i]);
+ }
+}
+
+static uint64_t mioe3680_pci_sja1_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr >> 2, size);
+}
+
+static void mioe3680_pci_sja1_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr >> 2, data, size);
+}
+
+static uint64_t mioe3680_pci_sja2_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr >> 2, size);
+}
+
+static void mioe3680_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Mioe3680PCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= MIOe3680_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr >> 2, data, size);
+}
+
+static const MemoryRegionOps mioe3680_pci_sja1_io_ops = {
+ .read = mioe3680_pci_sja1_io_read,
+ .write = mioe3680_pci_sja1_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps mioe3680_pci_sja2_io_ops = {
+ .read = mioe3680_pci_sja2_io_read,
+ .write = mioe3680_pci_sja2_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void mioe3680_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = pci_allocate_irq(&d->dev);
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_init(&d->sja_state[i], d->irq);
+ }
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+ }
+
+ memory_region_init_io(&d->sja_io[0], OBJECT(d), &mioe3680_pci_sja1_io_ops,
+ d, "mioe3680_pci-sja1", MIOe3680_PCI_SJA_RANGE);
+ memory_region_init_io(&d->sja_io[1], OBJECT(d), &mioe3680_pci_sja2_io_ops,
+ d, "mioe3680_pci-sja2", MIOe3680_PCI_SJA_RANGE);
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io[i]);
+ }
+}
+
+static void mioe3680_pci_exit(PCIDevice *pci_dev)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(pci_dev);
+ int i;
+
+ for (i = 0 ; i < MIOe3680_PCI_SJA_COUNT; i++) {
+ can_sja_disconnect(&d->sja_state[i]);
+ }
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_mioe3680_pci = {
+ .name = "mioe3680_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState),
+ VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_STRUCT(sja_state[1], Mioe3680PCIState, 0, vmstate_can_sja,
+ CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mioe3680_pci_instance_init(Object *obj)
+{
+ Mioe3680PCIState *d = MIOe3680_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&d->canbus[0],
+ qdev_prop_allow_set_link_before_realize,
+ 0);
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&d->canbus[1],
+ qdev_prop_allow_set_link_before_realize,
+ 0);
+}
+
+static void mioe3680_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = mioe3680_pci_realize;
+ k->exit = mioe3680_pci_exit;
+ k->vendor_id = MIOe3680_PCI_VENDOR_ID1;
+ k->device_id = MIOe3680_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x000c09;
+ k->subsystem_vendor_id = MIOe3680_PCI_VENDOR_ID1;
+ k->subsystem_id = MIOe3680_PCI_DEVICE_ID1;
+ dc->desc = "Mioe3680 PCICANx";
+ dc->vmsd = &vmstate_mioe3680_pci;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = mioe3680_pci_reset;
+}
+
+static const TypeInfo mioe3680_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(Mioe3680PCIState),
+ .class_init = mioe3680_pci_class_init,
+ .instance_init = mioe3680_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void mioe3680_pci_register_types(void)
+{
+ type_register_static(&mioe3680_pci_info);
+}
+
+type_init(mioe3680_pci_register_types)
diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c
new file mode 100644
index 000000000..8ef4e74af
--- /dev/null
+++ b/hw/net/can/can_pcm3680_pci.c
@@ -0,0 +1,268 @@
+/*
+ * PCM-3680i PCI CAN device (SJA1000 based) emulation
+ *
+ * Copyright (c) 2016 Deniz Eren (deniz.eren@icloud.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang 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 "qemu/event_notifier.h"
+#include "qemu/module.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+#include "qom/object.h"
+
+#define TYPE_CAN_PCI_DEV "pcm3680_pci"
+
+typedef struct Pcm3680iPCIState Pcm3680iPCIState;
+DECLARE_INSTANCE_CHECKER(Pcm3680iPCIState, PCM3680i_PCI_DEV,
+ TYPE_CAN_PCI_DEV)
+
+/* the PCI device and vendor IDs */
+#ifndef PCM3680i_PCI_VENDOR_ID1
+#define PCM3680i_PCI_VENDOR_ID1 0x13fe
+#endif
+
+#ifndef PCM3680i_PCI_DEVICE_ID1
+#define PCM3680i_PCI_DEVICE_ID1 0xc002
+#endif
+
+#define PCM3680i_PCI_SJA_COUNT 2
+#define PCM3680i_PCI_SJA_RANGE 0x100
+
+#define PCM3680i_PCI_BYTES_PER_SJA 0x20
+
+struct Pcm3680iPCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion sja_io[PCM3680i_PCI_SJA_COUNT];
+
+ CanSJA1000State sja_state[PCM3680i_PCI_SJA_COUNT];
+ qemu_irq irq;
+
+ char *model; /* The model that support, only SJA1000 now. */
+ CanBusState *canbus[PCM3680i_PCI_SJA_COUNT];
+};
+
+static void pcm3680i_pci_reset(DeviceState *dev)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(dev);
+ int i;
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_hardware_reset(&d->sja_state[i]);
+ }
+}
+
+static uint64_t pcm3680i_pci_sja1_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void pcm3680i_pci_sja1_io_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[0];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static uint64_t pcm3680i_pci_sja2_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return 0;
+ }
+
+ return can_sja_mem_read(s, addr, size);
+}
+
+static void pcm3680i_pci_sja2_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ Pcm3680iPCIState *d = opaque;
+ CanSJA1000State *s = &d->sja_state[1];
+
+ if (addr >= PCM3680i_PCI_BYTES_PER_SJA) {
+ return;
+ }
+
+ can_sja_mem_write(s, addr, data, size);
+}
+
+static const MemoryRegionOps pcm3680i_pci_sja1_io_ops = {
+ .read = pcm3680i_pci_sja1_io_read,
+ .write = pcm3680i_pci_sja1_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps pcm3680i_pci_sja2_io_ops = {
+ .read = pcm3680i_pci_sja2_io_read,
+ .write = pcm3680i_pci_sja2_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void pcm3680i_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = pci_allocate_irq(&d->dev);
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_init(&d->sja_state[i], d->irq);
+ }
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ if (can_sja_connect_to_bus(&d->sja_state[i], d->canbus[i]) < 0) {
+ error_setg(errp, "can_sja_connect_to_bus failed");
+ return;
+ }
+ }
+
+ memory_region_init_io(&d->sja_io[0], OBJECT(d), &pcm3680i_pci_sja1_io_ops,
+ d, "pcm3680i_pci-sja1", PCM3680i_PCI_SJA_RANGE);
+
+ memory_region_init_io(&d->sja_io[1], OBJECT(d), &pcm3680i_pci_sja2_io_ops,
+ d, "pcm3680i_pci-sja2", PCM3680i_PCI_SJA_RANGE);
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ pci_register_bar(&d->dev, /*BAR*/ i, PCI_BASE_ADDRESS_SPACE_IO,
+ &d->sja_io[i]);
+ }
+}
+
+static void pcm3680i_pci_exit(PCIDevice *pci_dev)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(pci_dev);
+ int i;
+
+ for (i = 0; i < PCM3680i_PCI_SJA_COUNT; i++) {
+ can_sja_disconnect(&d->sja_state[i]);
+ }
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_pcm3680i_pci = {
+ .name = "pcm3680i_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState),
+ VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0,
+ vmstate_can_sja, CanSJA1000State),
+ VMSTATE_STRUCT(sja_state[1], Pcm3680iPCIState, 0,
+ vmstate_can_sja, CanSJA1000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pcm3680i_pci_instance_init(Object *obj)
+{
+ Pcm3680iPCIState *d = PCM3680i_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&d->canbus[0],
+ qdev_prop_allow_set_link_before_realize,
+ 0);
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&d->canbus[1],
+ qdev_prop_allow_set_link_before_realize,
+ 0);
+}
+
+static void pcm3680i_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = pcm3680i_pci_realize;
+ k->exit = pcm3680i_pci_exit;
+ k->vendor_id = PCM3680i_PCI_VENDOR_ID1;
+ k->device_id = PCM3680i_PCI_DEVICE_ID1;
+ k->revision = 0x00;
+ k->class_id = 0x000c09;
+ k->subsystem_vendor_id = PCM3680i_PCI_VENDOR_ID1;
+ k->subsystem_id = PCM3680i_PCI_DEVICE_ID1;
+ dc->desc = "Pcm3680i PCICANx";
+ dc->vmsd = &vmstate_pcm3680i_pci;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = pcm3680i_pci_reset;
+}
+
+static const TypeInfo pcm3680i_pci_info = {
+ .name = TYPE_CAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(Pcm3680iPCIState),
+ .class_init = pcm3680i_pci_class_init,
+ .instance_init = pcm3680i_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void pcm3680i_pci_register_types(void)
+{
+ type_register_static(&pcm3680i_pci_info);
+}
+
+type_init(pcm3680i_pci_register_types)
diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c
new file mode 100644
index 000000000..34eea684c
--- /dev/null
+++ b/hw/net/can/can_sja1000.c
@@ -0,0 +1,988 @@
+/*
+ * CAN device - SJA1000 chip emulation for QEMU
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * 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 "qemu/log.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#ifndef DEBUG_FILTER
+#define DEBUG_FILTER 0
+#endif /*DEBUG_FILTER*/
+
+#ifndef DEBUG_CAN
+#define DEBUG_CAN 0
+#endif /*DEBUG_CAN*/
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_CAN) { \
+ qemu_log("[cansja]: " fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+static void can_sja_software_reset(CanSJA1000State *s)
+{
+ s->mode &= ~0x31;
+ s->mode |= 0x01;
+ s->status_pel &= ~0x37;
+ s->status_pel |= 0x34;
+
+ s->rxbuf_start = 0x00;
+ s->rxmsg_cnt = 0x00;
+ s->rx_cnt = 0x00;
+}
+
+void can_sja_hardware_reset(CanSJA1000State *s)
+{
+ /* Reset by hardware, p10 */
+ s->mode = 0x01;
+ s->status_pel = 0x3c;
+ s->interrupt_pel = 0x00;
+ s->clock = 0x00;
+ s->rxbuf_start = 0x00;
+ s->rxmsg_cnt = 0x00;
+ s->rx_cnt = 0x00;
+
+ s->control = 0x01;
+ s->status_bas = 0x0c;
+ s->interrupt_bas = 0x00;
+
+ qemu_irq_lower(s->irq);
+}
+
+static
+void can_sja_single_filter(struct qemu_can_filter *filter,
+ const uint8_t *acr, const uint8_t *amr, int extended)
+{
+ if (extended) {
+ filter->can_id = (uint32_t)acr[0] << 21;
+ filter->can_id |= (uint32_t)acr[1] << 13;
+ filter->can_id |= (uint32_t)acr[2] << 5;
+ filter->can_id |= (uint32_t)acr[3] >> 3;
+ if (acr[3] & 4) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 21;
+ filter->can_mask |= (uint32_t)amr[1] << 13;
+ filter->can_mask |= (uint32_t)amr[2] << 5;
+ filter->can_mask |= (uint32_t)amr[3] >> 3;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK;
+ if (!(amr[3] & 4)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ } else {
+ filter->can_id = (uint32_t)acr[0] << 3;
+ filter->can_id |= (uint32_t)acr[1] >> 5;
+ if (acr[1] & 0x10) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 3;
+ filter->can_mask |= (uint32_t)amr[1] << 5;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
+ if (!(amr[1] & 0x10)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ }
+}
+
+static
+void can_sja_dual_filter(struct qemu_can_filter *filter,
+ const uint8_t *acr, const uint8_t *amr, int extended)
+{
+ if (extended) {
+ filter->can_id = (uint32_t)acr[0] << 21;
+ filter->can_id |= (uint32_t)acr[1] << 13;
+
+ filter->can_mask = (uint32_t)amr[0] << 21;
+ filter->can_mask |= (uint32_t)amr[1] << 13;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK & ~0x1fff;
+ } else {
+ filter->can_id = (uint32_t)acr[0] << 3;
+ filter->can_id |= (uint32_t)acr[1] >> 5;
+ if (acr[1] & 0x10) {
+ filter->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ filter->can_mask = (uint32_t)amr[0] << 3;
+ filter->can_mask |= (uint32_t)amr[1] >> 5;
+ filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK;
+ if (!(amr[1] & 0x10)) {
+ filter->can_mask |= QEMU_CAN_RTR_FLAG;
+ }
+ }
+}
+
+/* Details in DS-p22, what we need to do here is to test the data. */
+static
+int can_sja_accept_filter(CanSJA1000State *s,
+ const qemu_can_frame *frame)
+{
+
+ struct qemu_can_filter filter;
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ if (s->mode & (1 << 3)) { /* Single mode. */
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ can_sja_single_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ if (!can_bus_filter_match(&filter, frame->can_id)) {
+ return 0;
+ }
+ } else { /* SFF */
+ can_sja_single_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ if (!can_bus_filter_match(&filter, frame->can_id)) {
+ return 0;
+ }
+
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ return 1;
+ }
+
+ if (frame->can_dlc == 0) {
+ return 1;
+ }
+
+ if ((frame->data[0] & ~(s->code_mask[6])) !=
+ (s->code_mask[2] & ~(s->code_mask[6]))) {
+ return 0;
+ }
+
+ if (frame->can_dlc < 2) {
+ return 1;
+ }
+
+ if ((frame->data[1] & ~(s->code_mask[7])) ==
+ (s->code_mask[3] & ~(s->code_mask[7]))) {
+ return 1;
+ }
+
+ return 0;
+ }
+ } else { /* Dual mode */
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ can_sja_dual_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ can_sja_dual_filter(&filter,
+ s->code_mask + 2, s->code_mask + 6, 1);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ return 0;
+ } else {
+ can_sja_dual_filter(&filter,
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ uint8_t expect;
+ uint8_t mask;
+ expect = s->code_mask[1] << 4;
+ expect |= s->code_mask[3] & 0x0f;
+
+ mask = s->code_mask[5] << 4;
+ mask |= s->code_mask[7] & 0x0f;
+ mask = ~mask & 0xff;
+
+ if ((frame->data[0] & mask) ==
+ (expect & mask)) {
+ return 1;
+ }
+ }
+
+ can_sja_dual_filter(&filter,
+ s->code_mask + 2, s->code_mask + 6, 0);
+
+ if (can_bus_filter_match(&filter, frame->can_id)) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void can_display_msg(const char *prefix, const qemu_can_frame *msg)
+{
+ int i;
+ FILE *logfile = qemu_log_lock();
+
+ qemu_log("%s%03X [%01d] %s %s",
+ prefix,
+ msg->can_id & QEMU_CAN_EFF_MASK,
+ msg->can_dlc,
+ msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
+ msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");
+
+ for (i = 0; i < msg->can_dlc; i++) {
+ qemu_log(" %02X", msg->data[i]);
+ }
+ qemu_log("\n");
+ qemu_log_flush();
+ qemu_log_unlock(logfile);
+}
+
+static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame)
+{
+ uint8_t i;
+
+ frame->flags = 0;
+ frame->can_id = 0;
+ if (buff[0] & 0x40) { /* RTR */
+ frame->can_id = QEMU_CAN_RTR_FLAG;
+ }
+ frame->can_dlc = buff[0] & 0x0f;
+
+ if (frame->can_dlc > 8) {
+ frame->can_dlc = 8;
+ }
+
+ if (buff[0] & 0x80) { /* Extended */
+ frame->can_id |= QEMU_CAN_EFF_FLAG;
+ frame->can_id |= buff[1] << 21; /* ID.28~ID.21 */
+ frame->can_id |= buff[2] << 13; /* ID.20~ID.13 */
+ frame->can_id |= buff[3] << 5;
+ frame->can_id |= buff[4] >> 3;
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[5 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+ } else {
+ frame->can_id |= buff[1] << 3;
+ frame->can_id |= buff[2] >> 5;
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[3 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+ }
+}
+
+
+static void buff2frame_bas(const uint8_t *buff, qemu_can_frame *frame)
+{
+ uint8_t i;
+
+ frame->flags = 0;
+ frame->can_id = ((buff[0] << 3) & (0xff << 3)) + ((buff[1] >> 5) & 0x07);
+ if (buff[1] & 0x10) { /* RTR */
+ frame->can_id = QEMU_CAN_RTR_FLAG;
+ }
+ frame->can_dlc = buff[1] & 0x0f;
+
+ if (frame->can_dlc > 8) {
+ frame->can_dlc = 8;
+ }
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = buff[2 + i];
+ }
+ for (; i < 8; i++) {
+ frame->data[i] = 0;
+ }
+}
+
+
+static int frame2buff_pel(const qemu_can_frame *frame, uint8_t *buff)
+{
+ int i;
+ int dlen = frame->can_dlc;
+
+ if (frame->can_id & QEMU_CAN_ERR_FLAG) { /* error frame, NOT support now. */
+ return -1;
+ }
+
+ if (dlen > 8) {
+ return -1;
+ }
+
+ buff[0] = 0x0f & frame->can_dlc; /* DLC */
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ buff[0] |= (1 << 6);
+ }
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
+ buff[0] |= (1 << 7);
+ buff[1] = extract32(frame->can_id, 21, 8); /* ID.28~ID.21 */
+ buff[2] = extract32(frame->can_id, 13, 8); /* ID.20~ID.13 */
+ buff[3] = extract32(frame->can_id, 5, 8); /* ID.12~ID.05 */
+ buff[4] = extract32(frame->can_id, 0, 5) << 3; /* ID.04~ID.00,xxx */
+ for (i = 0; i < dlen; i++) {
+ buff[5 + i] = frame->data[i];
+ }
+ return dlen + 5;
+ } else { /* SFF */
+ buff[1] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
+ buff[2] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
+ for (i = 0; i < dlen; i++) {
+ buff[3 + i] = frame->data[i];
+ }
+
+ return dlen + 3;
+ }
+
+ return -1;
+}
+
+static int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff)
+{
+ int i;
+ int dlen = frame->can_dlc;
+
+ /*
+ * EFF, no support for BasicMode
+ * No use for Error frames now,
+ * they could be used in future to update SJA1000 error state
+ */
+ if ((frame->can_id & QEMU_CAN_EFF_FLAG) ||
+ (frame->can_id & QEMU_CAN_ERR_FLAG)) {
+ return -1;
+ }
+
+ if (dlen > 8) {
+ return -1;
+ }
+
+ buff[0] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
+ buff[1] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
+ buff[1] |= (1 << 4);
+ }
+ buff[1] |= frame->can_dlc & 0x0f;
+ for (i = 0; i < dlen; i++) {
+ buff[2 + i] = frame->data[i];
+ }
+
+ return dlen + 2;
+}
+
+static void can_sja_update_pel_irq(CanSJA1000State *s)
+{
+ if (s->interrupt_en & s->interrupt_pel) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void can_sja_update_bas_irq(CanSJA1000State *s)
+{
+ if ((s->control >> 1) & s->interrupt_bas) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ qemu_can_frame frame;
+ uint32_t tmp;
+ uint8_t tmp8, count;
+
+
+ DPRINTF("write 0x%02llx addr 0x%02x\n",
+ (unsigned long long)val, (unsigned int)addr);
+
+ if (addr > CAN_SJA_MEM_SIZE) {
+ return ;
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ switch (addr) {
+ case SJA_MOD: /* Mode register */
+ s->mode = 0x1f & val;
+ if ((s->mode & 0x01) && ((val & 0x01) == 0)) {
+ /* Go to operation mode from reset mode. */
+ if (s->mode & (1 << 3)) { /* Single mode. */
+ /* For EFF */
+ can_sja_single_filter(&s->filter[0],
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ /* For SFF */
+ can_sja_single_filter(&s->filter[1],
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ can_bus_client_set_filters(&s->bus_client, s->filter, 2);
+ } else { /* Dual mode */
+ /* For EFF */
+ can_sja_dual_filter(&s->filter[0],
+ s->code_mask + 0, s->code_mask + 4, 1);
+
+ can_sja_dual_filter(&s->filter[1],
+ s->code_mask + 2, s->code_mask + 6, 1);
+
+ /* For SFF */
+ can_sja_dual_filter(&s->filter[2],
+ s->code_mask + 0, s->code_mask + 4, 0);
+
+ can_sja_dual_filter(&s->filter[3],
+ s->code_mask + 2, s->code_mask + 6, 0);
+
+ can_bus_client_set_filters(&s->bus_client, s->filter, 4);
+ }
+
+ s->rxmsg_cnt = 0;
+ s->rx_cnt = 0;
+ }
+ break;
+
+ case SJA_CMR: /* Command register. */
+ if (0x01 & val) { /* Send transmission request. */
+ buff2frame_pel(s->tx_buff, &frame);
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: Tx request " , &frame);
+ }
+
+ /*
+ * Clear transmission complete status,
+ * and Transmit Buffer Status.
+ * write to the backends.
+ */
+ s->status_pel &= ~(3 << 2);
+
+ can_bus_client_send(&s->bus_client, &frame, 1);
+
+ /*
+ * Set transmission complete status
+ * and Transmit Buffer Status.
+ */
+ s->status_pel |= (3 << 2);
+
+ /* Clear transmit status. */
+ s->status_pel &= ~(1 << 5);
+ s->interrupt_pel |= 0x02;
+ can_sja_update_pel_irq(s);
+ }
+ if (0x04 & val) { /* Release Receive Buffer */
+ if (s->rxmsg_cnt <= 0) {
+ break;
+ }
+
+ tmp8 = s->rx_buff[s->rxbuf_start]; count = 0;
+ if (tmp8 & (1 << 7)) { /* EFF */
+ count += 2;
+ }
+ count += 3;
+ if (!(tmp8 & (1 << 6))) { /* DATA */
+ count += (tmp8 & 0x0f);
+ }
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message released from "
+ "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
+ }
+
+ s->rxbuf_start += count;
+ s->rxbuf_start %= SJA_RCV_BUF_LEN;
+
+ s->rx_cnt -= count;
+ s->rxmsg_cnt--;
+ if (s->rxmsg_cnt == 0) {
+ s->status_pel &= ~(1 << 0);
+ s->interrupt_pel &= ~(1 << 0);
+ can_sja_update_pel_irq(s);
+ }
+ }
+ if (0x08 & val) { /* Clear data overrun */
+ s->status_pel &= ~(1 << 1);
+ s->interrupt_pel &= ~(1 << 3);
+ can_sja_update_pel_irq(s);
+ }
+ break;
+ case SJA_SR: /* Status register */
+ case SJA_IR: /* Interrupt register */
+ break; /* Do nothing */
+ case SJA_IER: /* Interrupt enable register */
+ s->interrupt_en = val;
+ break;
+ case 16: /* RX frame information addr16-28. */
+ s->status_pel |= (1 << 5); /* Set transmit status. */
+ /* fallthrough */
+ case 17 ... 28:
+ if (s->mode & 0x01) { /* Reset mode */
+ if (addr < 24) {
+ s->code_mask[addr - 16] = val;
+ }
+ } else { /* Operation mode */
+ s->tx_buff[addr - 16] = val; /* Store to TX buffer directly. */
+ }
+ break;
+ case SJA_CDR:
+ s->clock = val;
+ break;
+ }
+ } else { /* Basic Mode */
+ switch (addr) {
+ case SJA_BCAN_CTR: /* Control register, addr 0 */
+ if ((s->control & 0x01) && ((val & 0x01) == 0)) {
+ /* Go to operation mode from reset mode. */
+ s->filter[0].can_id = (s->code << 3) & (0xff << 3);
+ tmp = (~(s->mask << 3)) & (0xff << 3);
+ tmp |= QEMU_CAN_EFF_FLAG; /* Only Basic CAN Frame. */
+ s->filter[0].can_mask = tmp;
+ can_bus_client_set_filters(&s->bus_client, s->filter, 1);
+
+ s->rxmsg_cnt = 0;
+ s->rx_cnt = 0;
+ } else if (!(s->control & 0x01) && !(val & 0x01)) {
+ can_sja_software_reset(s);
+ }
+
+ s->control = 0x1f & val;
+ break;
+ case SJA_BCAN_CMR: /* Command register, addr 1 */
+ if (0x01 & val) { /* Send transmission request. */
+ buff2frame_bas(s->tx_buff, &frame);
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: Tx request " , &frame);
+ }
+
+ /*
+ * Clear transmission complete status,
+ * and Transmit Buffer Status.
+ */
+ s->status_bas &= ~(3 << 2);
+
+ /* write to the backends. */
+ can_bus_client_send(&s->bus_client, &frame, 1);
+
+ /*
+ * Set transmission complete status,
+ * and Transmit Buffer Status.
+ */
+ s->status_bas |= (3 << 2);
+
+ /* Clear transmit status. */
+ s->status_bas &= ~(1 << 5);
+ s->interrupt_bas |= 0x02;
+ can_sja_update_bas_irq(s);
+ }
+ if (0x04 & val) { /* Release Receive Buffer */
+ if (s->rxmsg_cnt <= 0) {
+ break;
+ }
+
+ tmp8 = s->rx_buff[(s->rxbuf_start + 1) % SJA_RCV_BUF_LEN];
+ count = 2 + (tmp8 & 0x0f);
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message released from "
+ "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count);
+ }
+
+ s->rxbuf_start += count;
+ s->rxbuf_start %= SJA_RCV_BUF_LEN;
+ s->rx_cnt -= count;
+ s->rxmsg_cnt--;
+
+ if (s->rxmsg_cnt == 0) {
+ s->status_bas &= ~(1 << 0);
+ s->interrupt_bas &= ~(1 << 0);
+ can_sja_update_bas_irq(s);
+ }
+ }
+ if (0x08 & val) { /* Clear data overrun */
+ s->status_bas &= ~(1 << 1);
+ s->interrupt_bas &= ~(1 << 3);
+ can_sja_update_bas_irq(s);
+ }
+ break;
+ case 4:
+ s->code = val;
+ break;
+ case 5:
+ s->mask = val;
+ break;
+ case 10:
+ s->status_bas |= (1 << 5); /* Set transmit status. */
+ /* fallthrough */
+ case 11 ... 19:
+ if ((s->control & 0x01) == 0) { /* Operation mode */
+ s->tx_buff[addr - 10] = val; /* Store to TX buffer directly. */
+ }
+ break;
+ case SJA_CDR:
+ s->clock = val;
+ break;
+ }
+ }
+}
+
+uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size)
+{
+ uint64_t temp = 0;
+
+ DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
+
+ if (addr > CAN_SJA_MEM_SIZE) {
+ return 0;
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ switch (addr) {
+ case SJA_MOD: /* Mode register, addr 0 */
+ temp = s->mode;
+ break;
+ case SJA_CMR: /* Command register, addr 1 */
+ temp = 0x00; /* Command register, cannot be read. */
+ break;
+ case SJA_SR: /* Status register, addr 2 */
+ temp = s->status_pel;
+ break;
+ case SJA_IR: /* Interrupt register, addr 3 */
+ temp = s->interrupt_pel;
+ s->interrupt_pel = 0;
+ if (s->rxmsg_cnt) {
+ s->interrupt_pel |= (1 << 0); /* Receive interrupt. */
+ }
+ can_sja_update_pel_irq(s);
+ break;
+ case SJA_IER: /* Interrupt enable register, addr 4 */
+ temp = s->interrupt_en;
+ break;
+ case 5: /* Reserved */
+ case 6: /* Bus timing 0, hardware related, not support now. */
+ case 7: /* Bus timing 1, hardware related, not support now. */
+ case 8: /*
+ * Output control register, hardware related,
+ * not supported for now.
+ */
+ case 9: /* Test. */
+ case 10 ... 15: /* Reserved */
+ temp = 0x00;
+ break;
+
+ case 16 ... 28:
+ if (s->mode & 0x01) { /* Reset mode */
+ if (addr < 24) {
+ temp = s->code_mask[addr - 16];
+ } else {
+ temp = 0x00;
+ }
+ } else { /* Operation mode */
+ temp = s->rx_buff[(s->rxbuf_start + addr - 16) %
+ SJA_RCV_BUF_LEN];
+ }
+ break;
+ case SJA_CDR:
+ temp = s->clock;
+ break;
+ default:
+ temp = 0xff;
+ }
+ } else { /* Basic Mode */
+ switch (addr) {
+ case SJA_BCAN_CTR: /* Control register, addr 0 */
+ temp = s->control;
+ break;
+ case SJA_BCAN_SR: /* Status register, addr 2 */
+ temp = s->status_bas;
+ break;
+ case SJA_BCAN_IR: /* Interrupt register, addr 3 */
+ temp = s->interrupt_bas;
+ s->interrupt_bas = 0;
+ if (s->rxmsg_cnt) {
+ s->interrupt_bas |= (1 << 0); /* Receive interrupt. */
+ }
+ can_sja_update_bas_irq(s);
+ break;
+ case 4:
+ temp = s->code;
+ break;
+ case 5:
+ temp = s->mask;
+ break;
+ case 20 ... 29:
+ temp = s->rx_buff[(s->rxbuf_start + addr - 20) % SJA_RCV_BUF_LEN];
+ break;
+ case 31:
+ temp = s->clock;
+ break;
+ default:
+ temp = 0xff;
+ break;
+ }
+ }
+ DPRINTF("read addr 0x%02x, %d bytes, content 0x%02lx\n",
+ (int)addr, size, (long unsigned int)temp);
+
+ return temp;
+}
+
+bool can_sja_can_receive(CanBusClientState *client)
+{
+ CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ if (s->mode & 0x01) { /* reset mode. */
+ return false;
+ }
+ } else { /* BasicCAN mode */
+ if (s->control & 0x01) {
+ return false;
+ }
+ }
+
+ return true; /* always return true, when operation mode */
+}
+
+ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames,
+ size_t frames_cnt)
+{
+ CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client);
+ static uint8_t rcv[SJA_MSG_MAX_LEN];
+ int i;
+ int ret = -1;
+ const qemu_can_frame *frame = frames;
+
+ if (frames_cnt <= 0) {
+ return 0;
+ }
+ if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: ignor fd frame ", frame);
+ }
+ return 1;
+ }
+
+ if (DEBUG_FILTER) {
+ can_display_msg("[cansja]: receive ", frame);
+ }
+
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+
+ /* the CAN controller is receiving a message */
+ s->status_pel |= (1 << 4);
+
+ if (can_sja_accept_filter(s, frame) == 0) {
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: filter rejects message\n");
+ }
+ return ret;
+ }
+
+ ret = frame2buff_pel(frame, rcv);
+ if (ret < 0) {
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message store failed\n");
+ }
+ return ret; /* maybe not support now. */
+ }
+
+ if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
+ s->status_pel |= (1 << 1); /* Overrun status */
+ s->interrupt_pel |= (1 << 3);
+ s->status_pel &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: receive FIFO overrun\n");
+ }
+ can_sja_update_pel_irq(s);
+ return ret;
+ }
+ s->rx_cnt += ret;
+ s->rxmsg_cnt++;
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message stored in receive FIFO\n");
+ }
+
+ for (i = 0; i < ret; i++) {
+ s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
+ }
+ s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
+
+ s->status_pel |= 0x01; /* Set the Receive Buffer Status. DS-p23 */
+ s->interrupt_pel |= 0x01;
+ s->status_pel &= ~(1 << 4);
+ s->status_pel |= (1 << 0);
+ can_sja_update_pel_irq(s);
+ } else { /* BasicCAN mode */
+
+ /* the CAN controller is receiving a message */
+ s->status_bas |= (1 << 4);
+
+ ret = frame2buff_bas(frame, rcv);
+ if (ret < 0) {
+ s->status_bas &= ~(1 << 4);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message store failed\n");
+ }
+ return ret; /* maybe not support now. */
+ }
+
+ if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */
+ s->status_bas |= (1 << 1); /* Overrun status */
+ s->status_bas &= ~(1 << 4);
+ s->interrupt_bas |= (1 << 3);
+ can_sja_update_bas_irq(s);
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: receive FIFO overrun\n");
+ }
+ return ret;
+ }
+ s->rx_cnt += ret;
+ s->rxmsg_cnt++;
+
+ if (DEBUG_FILTER) {
+ qemu_log("[cansja]: message stored\n");
+ }
+
+ for (i = 0; i < ret; i++) {
+ s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i];
+ }
+ s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */
+
+ s->status_bas |= 0x01; /* Set the Receive Buffer Status. DS-p15 */
+ s->status_bas &= ~(1 << 4);
+ s->interrupt_bas |= (1 << 0);
+ can_sja_update_bas_irq(s);
+ }
+ return 1;
+}
+
+static CanBusClientInfo can_sja_bus_client_info = {
+ .can_receive = can_sja_can_receive,
+ .receive = can_sja_receive,
+};
+
+
+int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus)
+{
+ s->bus_client.info = &can_sja_bus_client_info;
+
+ if (!bus) {
+ return -EINVAL;
+ }
+
+ if (can_bus_insert_client(bus, &s->bus_client) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void can_sja_disconnect(CanSJA1000State *s)
+{
+ can_bus_remove_client(&s->bus_client);
+}
+
+int can_sja_init(CanSJA1000State *s, qemu_irq irq)
+{
+ s->irq = irq;
+
+ qemu_irq_lower(s->irq);
+
+ can_sja_hardware_reset(s);
+
+ return 0;
+}
+
+const VMStateDescription vmstate_qemu_can_filter = {
+ .name = "qemu_can_filter",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(can_id, qemu_can_filter),
+ VMSTATE_UINT32(can_mask, qemu_can_filter),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int can_sja_post_load(void *opaque, int version_id)
+{
+ CanSJA1000State *s = opaque;
+ if (s->clock & 0x80) { /* PeliCAN Mode */
+ can_sja_update_pel_irq(s);
+ } else {
+ can_sja_update_bas_irq(s);
+ }
+ return 0;
+}
+
+/* VMState is needed for live migration of QEMU images */
+const VMStateDescription vmstate_can_sja = {
+ .name = "can_sja",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = can_sja_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(mode, CanSJA1000State),
+
+ VMSTATE_UINT8(status_pel, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_pel, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_en, CanSJA1000State),
+ VMSTATE_UINT8(rxmsg_cnt, CanSJA1000State),
+ VMSTATE_UINT8(rxbuf_start, CanSJA1000State),
+ VMSTATE_UINT8(clock, CanSJA1000State),
+
+ VMSTATE_BUFFER(code_mask, CanSJA1000State),
+ VMSTATE_BUFFER(tx_buff, CanSJA1000State),
+
+ VMSTATE_BUFFER(rx_buff, CanSJA1000State),
+
+ VMSTATE_UINT32(rx_ptr, CanSJA1000State),
+ VMSTATE_UINT32(rx_cnt, CanSJA1000State),
+
+ VMSTATE_UINT8(control, CanSJA1000State),
+
+ VMSTATE_UINT8(status_bas, CanSJA1000State),
+ VMSTATE_UINT8(interrupt_bas, CanSJA1000State),
+ VMSTATE_UINT8(code, CanSJA1000State),
+ VMSTATE_UINT8(mask, CanSJA1000State),
+
+ VMSTATE_STRUCT_ARRAY(filter, CanSJA1000State, 4, 0,
+ vmstate_qemu_can_filter, qemu_can_filter),
+
+
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/net/can/can_sja1000.h b/hw/net/can/can_sja1000.h
new file mode 100644
index 000000000..7ca9cd681
--- /dev/null
+++ b/hw/net/can/can_sja1000.h
@@ -0,0 +1,147 @@
+/*
+ * CAN device - SJA1000 chip emulation for QEMU
+ *
+ * Copyright (c) 2013-2014 Jin Yang
+ * Copyright (c) 2014-2018 Pavel Pisa
+ *
+ * Initial development supported by Google GSoC 2013 from RTEMS project slot
+ *
+ * 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_SJA1000_H
+#define HW_CAN_SJA1000_H
+
+#include "exec/hwaddr.h"
+#include "net/can_emu.h"
+
+#define CAN_SJA_MEM_SIZE 128
+
+/* The max size for a message buffer, EFF and DLC=8, DS-p39 */
+#define SJA_MSG_MAX_LEN 13
+/* The receive buffer size. */
+#define SJA_RCV_BUF_LEN 64
+
+typedef struct CanSJA1000State {
+ /* PeliCAN state and registers sorted by address */
+ uint8_t mode; /* 0 .. Mode register, DS-p26 */
+ /* 1 .. Command register */
+ uint8_t status_pel; /* 2 .. Status register, p15 */
+ uint8_t interrupt_pel; /* 3 .. Interrupt register */
+ uint8_t interrupt_en; /* 4 .. Interrupt Enable register */
+ uint8_t rxmsg_cnt; /* 29 .. RX message counter. DS-p49 */
+ uint8_t rxbuf_start; /* 30 .. RX buffer start address, DS-p49 */
+ uint8_t clock; /* 31 .. Clock Divider register, DS-p55 */
+
+ uint8_t code_mask[8]; /* 16~23 */
+ uint8_t tx_buff[13]; /* 96~108 .. transmit buffer */
+ /* 10~19 .. transmit buffer for BasicCAN */
+
+ uint8_t rx_buff[SJA_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */
+ uint32_t rx_ptr; /* Count by bytes. */
+ uint32_t rx_cnt; /* Count by bytes. */
+
+ /* PeliCAN state and registers sorted by address */
+ uint8_t control; /* 0 .. Control register */
+ /* 1 .. Command register */
+ uint8_t status_bas; /* 2 .. Status register */
+ uint8_t interrupt_bas; /* 3 .. Interrupt register */
+ uint8_t code; /* 4 .. Acceptance code register */
+ uint8_t mask; /* 5 .. Acceptance mask register */
+
+ qemu_can_filter filter[4];
+
+ qemu_irq irq;
+ CanBusClientState bus_client;
+} CanSJA1000State;
+
+/* PeliCAN mode */
+enum SJA1000_PeliCAN_regs {
+ SJA_MOD = 0x00, /* Mode control register */
+ SJA_CMR = 0x01, /* Command register */
+ SJA_SR = 0x02, /* Status register */
+ SJA_IR = 0x03, /* Interrupt register */
+ SJA_IER = 0x04, /* Interrupt Enable */
+ SJA_BTR0 = 0x06, /* Bus Timing register 0 */
+ SJA_BTR1 = 0x07, /* Bus Timing register 1 */
+ SJA_OCR = 0x08, /* Output Control register */
+ SJA_ALC = 0x0b, /* Arbitration Lost Capture */
+ SJA_ECC = 0x0c, /* Error Code Capture */
+ SJA_EWLR = 0x0d, /* Error Warning Limit */
+ SJA_RXERR = 0x0e, /* RX Error Counter */
+ SJA_TXERR0 = 0x0e, /* TX Error Counter */
+ SJA_TXERR1 = 0x0f,
+ SJA_RMC = 0x1d, /* Rx Message Counter
+ * number of messages in RX FIFO
+ */
+ SJA_RBSA = 0x1e, /* Rx Buffer Start Addr
+ * address of current message
+ */
+ SJA_FRM = 0x10, /* Transmit Buffer
+ * write: Receive Buffer
+ * read: Frame Information
+ */
+/*
+ * ID bytes (11 bits in 0 and 1 for standard message or
+ * 16 bits in 0,1 and 13 bits in 2,3 for extended message)
+ * The most significant bit of ID is placed in MSB
+ * position of ID0 register.
+ */
+ SJA_ID0 = 0x11, /* ID for standard and extended frames */
+ SJA_ID1 = 0x12,
+ SJA_ID2 = 0x13, /* ID cont. for extended frames */
+ SJA_ID3 = 0x14,
+
+ SJA_DATS = 0x13, /* Data start standard frame */
+ SJA_DATE = 0x15, /* Data start extended frame */
+ SJA_ACR0 = 0x10, /* Acceptance Code (4 bytes) in RESET mode */
+ SJA_AMR0 = 0x14, /* Acceptance Mask (4 bytes) in RESET mode */
+ SJA_PeliCAN_AC_LEN = 4, /* 4 bytes */
+ SJA_CDR = 0x1f /* Clock Divider */
+};
+
+
+/* BasicCAN mode */
+enum SJA1000_BasicCAN_regs {
+ SJA_BCAN_CTR = 0x00, /* Control register */
+ SJA_BCAN_CMR = 0x01, /* Command register */
+ SJA_BCAN_SR = 0x02, /* Status register */
+ SJA_BCAN_IR = 0x03 /* Interrupt register */
+};
+
+void can_sja_hardware_reset(CanSJA1000State *s);
+
+void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val,
+ unsigned size);
+
+uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size);
+
+int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus);
+
+void can_sja_disconnect(CanSJA1000State *s);
+
+int can_sja_init(CanSJA1000State *s, qemu_irq irq);
+
+bool can_sja_can_receive(CanBusClientState *client);
+
+ssize_t can_sja_receive(CanBusClientState *client,
+ const qemu_can_frame *frames, size_t frames_cnt);
+
+extern const VMStateDescription vmstate_can_sja;
+
+#endif
diff --git a/hw/net/can/ctu_can_fd_frame.h b/hw/net/can/ctu_can_fd_frame.h
new file mode 100644
index 000000000..04d956c84
--- /dev/null
+++ b/hw/net/can/ctu_can_fd_frame.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+/* This file is autogenerated, DO NOT EDIT! */
+
+#ifndef __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__
+#define __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__
+
+/* CAN_Frame_format memory map */
+enum ctu_can_fd_can_frame_format {
+ CTU_CAN_FD_FRAME_FORM_W = 0x0,
+ CTU_CAN_FD_IDENTIFIER_W = 0x4,
+ CTU_CAN_FD_TIMESTAMP_L_W = 0x8,
+ CTU_CAN_FD_TIMESTAMP_U_W = 0xc,
+ CTU_CAN_FD_DATA_1_4_W = 0x10,
+ CTU_CAN_FD_DATA_5_8_W = 0x14,
+ CTU_CAN_FD_DATA_61_64_W = 0x4c,
+};
+
+
+/* Register descriptions: */
+union ctu_can_fd_frame_form_w {
+ uint32_t u32;
+ struct ctu_can_fd_frame_form_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FRAME_FORM_W */
+ uint32_t dlc : 4;
+ uint32_t reserved_4 : 1;
+ uint32_t rtr : 1;
+ uint32_t ide : 1;
+ uint32_t fdf : 1;
+ uint32_t reserved_8 : 1;
+ uint32_t brs : 1;
+ uint32_t esi_rsv : 1;
+ uint32_t rwcnt : 5;
+ uint32_t reserved_31_16 : 16;
+#else
+ uint32_t reserved_31_16 : 16;
+ uint32_t rwcnt : 5;
+ uint32_t esi_rsv : 1;
+ uint32_t brs : 1;
+ uint32_t reserved_8 : 1;
+ uint32_t fdf : 1;
+ uint32_t ide : 1;
+ uint32_t rtr : 1;
+ uint32_t reserved_4 : 1;
+ uint32_t dlc : 4;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_frame_form_w_rtr {
+ NO_RTR_FRAME = 0x0,
+ RTR_FRAME = 0x1,
+};
+
+enum ctu_can_fd_frame_form_w_ide {
+ BASE = 0x0,
+ EXTENDED = 0x1,
+};
+
+enum ctu_can_fd_frame_form_w_fdf {
+ NORMAL_CAN = 0x0,
+ FD_CAN = 0x1,
+};
+
+enum ctu_can_fd_frame_form_w_brs {
+ BR_NO_SHIFT = 0x0,
+ BR_SHIFT = 0x1,
+};
+
+enum ctu_can_fd_frame_form_w_esi_rsv {
+ ESI_ERR_ACTIVE = 0x0,
+ ESI_ERR_PASIVE = 0x1,
+};
+
+union ctu_can_fd_identifier_w {
+ uint32_t u32;
+ struct ctu_can_fd_identifier_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* IDENTIFIER_W */
+ uint32_t identifier_ext : 18;
+ uint32_t identifier_base : 11;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t identifier_base : 11;
+ uint32_t identifier_ext : 18;
+#endif
+ } s;
+};
+
+union ctu_can_fd_timestamp_l_w {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_l_w_s {
+ /* TIMESTAMP_L_W */
+ uint32_t time_stamp_31_0 : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_u_w {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_u_w_s {
+ /* TIMESTAMP_U_W */
+ uint32_t timestamp_l_w : 32;
+ } s;
+};
+
+union ctu_can_fd_data_1_4_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_1_4_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_1_4_W */
+ uint32_t data_1 : 8;
+ uint32_t data_2 : 8;
+ uint32_t data_3 : 8;
+ uint32_t data_4 : 8;
+#else
+ uint32_t data_4 : 8;
+ uint32_t data_3 : 8;
+ uint32_t data_2 : 8;
+ uint32_t data_1 : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_data_5_8_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_5_8_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_5_8_W */
+ uint32_t data_5 : 8;
+ uint32_t data_6 : 8;
+ uint32_t data_7 : 8;
+ uint32_t data_8 : 8;
+#else
+ uint32_t data_8 : 8;
+ uint32_t data_7 : 8;
+ uint32_t data_6 : 8;
+ uint32_t data_5 : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_data_61_64_w {
+ uint32_t u32;
+ struct ctu_can_fd_data_61_64_w_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DATA_61_64_W */
+ uint32_t data_61 : 8;
+ uint32_t data_62 : 8;
+ uint32_t data_63 : 8;
+ uint32_t data_64 : 8;
+#else
+ uint32_t data_64 : 8;
+ uint32_t data_63 : 8;
+ uint32_t data_62 : 8;
+ uint32_t data_61 : 8;
+#endif
+ } s;
+};
+
+#endif
diff --git a/hw/net/can/ctu_can_fd_regs.h b/hw/net/can/ctu_can_fd_regs.h
new file mode 100644
index 000000000..450f4b9fb
--- /dev/null
+++ b/hw/net/can/ctu_can_fd_regs.h
@@ -0,0 +1,971 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*******************************************************************************
+ *
+ * CTU CAN FD IP Core
+ *
+ * Copyright (C) 2015-2018 Ondrej Ille <ondrej.ille@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Ondrej Ille <ondrej.ille@gmail.com> self-funded
+ * Copyright (C) 2018-2019 Martin Jerabek <martin.jerabek01@gmail.com> FEE CTU
+ * Copyright (C) 2018-2020 Pavel Pisa <pisa@cmp.felk.cvut.cz> FEE CTU/self-funded
+ *
+ * Project advisors:
+ * Jiri Novak <jnovak@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *
+ * Department of Measurement (http://meas.fel.cvut.cz/)
+ * Faculty of Electrical Engineering (http://www.fel.cvut.cz)
+ * Czech Technical University (http://www.cvut.cz/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ ******************************************************************************/
+
+/* This file is autogenerated, DO NOT EDIT! */
+
+#ifndef __CTU_CAN_FD_CAN_FD_REGISTER_MAP__
+#define __CTU_CAN_FD_CAN_FD_REGISTER_MAP__
+
+/* CAN_Registers memory map */
+enum ctu_can_fd_can_registers {
+ CTU_CAN_FD_DEVICE_ID = 0x0,
+ CTU_CAN_FD_VERSION = 0x2,
+ CTU_CAN_FD_MODE = 0x4,
+ CTU_CAN_FD_SETTINGS = 0x6,
+ CTU_CAN_FD_STATUS = 0x8,
+ CTU_CAN_FD_COMMAND = 0xc,
+ CTU_CAN_FD_INT_STAT = 0x10,
+ CTU_CAN_FD_INT_ENA_SET = 0x14,
+ CTU_CAN_FD_INT_ENA_CLR = 0x18,
+ CTU_CAN_FD_INT_MASK_SET = 0x1c,
+ CTU_CAN_FD_INT_MASK_CLR = 0x20,
+ CTU_CAN_FD_BTR = 0x24,
+ CTU_CAN_FD_BTR_FD = 0x28,
+ CTU_CAN_FD_EWL = 0x2c,
+ CTU_CAN_FD_ERP = 0x2d,
+ CTU_CAN_FD_FAULT_STATE = 0x2e,
+ CTU_CAN_FD_REC = 0x30,
+ CTU_CAN_FD_TEC = 0x32,
+ CTU_CAN_FD_ERR_NORM = 0x34,
+ CTU_CAN_FD_ERR_FD = 0x36,
+ CTU_CAN_FD_CTR_PRES = 0x38,
+ CTU_CAN_FD_FILTER_A_MASK = 0x3c,
+ CTU_CAN_FD_FILTER_A_VAL = 0x40,
+ CTU_CAN_FD_FILTER_B_MASK = 0x44,
+ CTU_CAN_FD_FILTER_B_VAL = 0x48,
+ CTU_CAN_FD_FILTER_C_MASK = 0x4c,
+ CTU_CAN_FD_FILTER_C_VAL = 0x50,
+ CTU_CAN_FD_FILTER_RAN_LOW = 0x54,
+ CTU_CAN_FD_FILTER_RAN_HIGH = 0x58,
+ CTU_CAN_FD_FILTER_CONTROL = 0x5c,
+ CTU_CAN_FD_FILTER_STATUS = 0x5e,
+ CTU_CAN_FD_RX_MEM_INFO = 0x60,
+ CTU_CAN_FD_RX_POINTERS = 0x64,
+ CTU_CAN_FD_RX_STATUS = 0x68,
+ CTU_CAN_FD_RX_SETTINGS = 0x6a,
+ CTU_CAN_FD_RX_DATA = 0x6c,
+ CTU_CAN_FD_TX_STATUS = 0x70,
+ CTU_CAN_FD_TX_COMMAND = 0x74,
+ CTU_CAN_FD_TX_PRIORITY = 0x78,
+ CTU_CAN_FD_ERR_CAPT = 0x7c,
+ CTU_CAN_FD_ALC = 0x7e,
+ CTU_CAN_FD_TRV_DELAY = 0x80,
+ CTU_CAN_FD_SSP_CFG = 0x82,
+ CTU_CAN_FD_RX_FR_CTR = 0x84,
+ CTU_CAN_FD_TX_FR_CTR = 0x88,
+ CTU_CAN_FD_DEBUG_REGISTER = 0x8c,
+ CTU_CAN_FD_YOLO_REG = 0x90,
+ CTU_CAN_FD_TIMESTAMP_LOW = 0x94,
+ CTU_CAN_FD_TIMESTAMP_HIGH = 0x98,
+ CTU_CAN_FD_TXTB1_DATA_1 = 0x100,
+ CTU_CAN_FD_TXTB1_DATA_2 = 0x104,
+ CTU_CAN_FD_TXTB1_DATA_20 = 0x14c,
+ CTU_CAN_FD_TXTB2_DATA_1 = 0x200,
+ CTU_CAN_FD_TXTB2_DATA_2 = 0x204,
+ CTU_CAN_FD_TXTB2_DATA_20 = 0x24c,
+ CTU_CAN_FD_TXTB3_DATA_1 = 0x300,
+ CTU_CAN_FD_TXTB3_DATA_2 = 0x304,
+ CTU_CAN_FD_TXTB3_DATA_20 = 0x34c,
+ CTU_CAN_FD_TXTB4_DATA_1 = 0x400,
+ CTU_CAN_FD_TXTB4_DATA_2 = 0x404,
+ CTU_CAN_FD_TXTB4_DATA_20 = 0x44c,
+};
+
+
+/* Register descriptions: */
+union ctu_can_fd_device_id_version {
+ uint32_t u32;
+ struct ctu_can_fd_device_id_version_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DEVICE_ID */
+ uint32_t device_id : 16;
+ /* VERSION */
+ uint32_t ver_minor : 8;
+ uint32_t ver_major : 8;
+#else
+ uint32_t ver_major : 8;
+ uint32_t ver_minor : 8;
+ uint32_t device_id : 16;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_device_id_device_id {
+ CTU_CAN_FD_ID = 0xcafd,
+};
+
+union ctu_can_fd_mode_settings {
+ uint32_t u32;
+ struct ctu_can_fd_mode_settings_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* MODE */
+ uint32_t rst : 1;
+ uint32_t lom : 1;
+ uint32_t stm : 1;
+ uint32_t afm : 1;
+ uint32_t fde : 1;
+ uint32_t reserved_6_5 : 2;
+ uint32_t acf : 1;
+ uint32_t tstm : 1;
+ uint32_t reserved_15_9 : 7;
+ /* SETTINGS */
+ uint32_t rtrle : 1;
+ uint32_t rtrth : 4;
+ uint32_t ilbp : 1;
+ uint32_t ena : 1;
+ uint32_t nisofd : 1;
+ uint32_t pex : 1;
+ uint32_t reserved_31_25 : 7;
+#else
+ uint32_t reserved_31_25 : 7;
+ uint32_t pex : 1;
+ uint32_t nisofd : 1;
+ uint32_t ena : 1;
+ uint32_t ilbp : 1;
+ uint32_t rtrth : 4;
+ uint32_t rtrle : 1;
+ uint32_t reserved_15_9 : 7;
+ uint32_t tstm : 1;
+ uint32_t acf : 1;
+ uint32_t reserved_6_5 : 2;
+ uint32_t fde : 1;
+ uint32_t afm : 1;
+ uint32_t stm : 1;
+ uint32_t lom : 1;
+ uint32_t rst : 1;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_mode_lom {
+ LOM_DISABLED = 0x0,
+ LOM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_stm {
+ STM_DISABLED = 0x0,
+ STM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_afm {
+ AFM_DISABLED = 0x0,
+ AFM_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_mode_fde {
+ FDE_DISABLE = 0x0,
+ FDE_ENABLE = 0x1,
+};
+
+enum ctu_can_fd_mode_acf {
+ ACF_DISABLED = 0x0,
+ ACF_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_rtrle {
+ RTRLE_DISABLED = 0x0,
+ RTRLE_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_ilbp {
+ INT_LOOP_DISABLED = 0x0,
+ INT_LOOP_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_ena {
+ CTU_CAN_DISABLED = 0x0,
+ CTU_CAN_ENABLED = 0x1,
+};
+
+enum ctu_can_fd_settings_nisofd {
+ ISO_FD = 0x0,
+ NON_ISO_FD = 0x1,
+};
+
+enum ctu_can_fd_settings_pex {
+ PROTOCOL_EXCEPTION_DISABLED = 0x0,
+ PROTOCOL_EXCEPTION_ENABLED = 0x1,
+};
+
+union ctu_can_fd_status {
+ uint32_t u32;
+ struct ctu_can_fd_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* STATUS */
+ uint32_t rxne : 1;
+ uint32_t dor : 1;
+ uint32_t txnf : 1;
+ uint32_t eft : 1;
+ uint32_t rxs : 1;
+ uint32_t txs : 1;
+ uint32_t ewl : 1;
+ uint32_t idle : 1;
+ uint32_t reserved_31_8 : 24;
+#else
+ uint32_t reserved_31_8 : 24;
+ uint32_t idle : 1;
+ uint32_t ewl : 1;
+ uint32_t txs : 1;
+ uint32_t rxs : 1;
+ uint32_t eft : 1;
+ uint32_t txnf : 1;
+ uint32_t dor : 1;
+ uint32_t rxne : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_command {
+ uint32_t u32;
+ struct ctu_can_fd_command_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ uint32_t reserved_1_0 : 2;
+ /* COMMAND */
+ uint32_t rrb : 1;
+ uint32_t cdo : 1;
+ uint32_t ercrst : 1;
+ uint32_t rxfcrst : 1;
+ uint32_t txfcrst : 1;
+ uint32_t reserved_31_7 : 25;
+#else
+ uint32_t reserved_31_7 : 25;
+ uint32_t txfcrst : 1;
+ uint32_t rxfcrst : 1;
+ uint32_t ercrst : 1;
+ uint32_t cdo : 1;
+ uint32_t rrb : 1;
+ uint32_t reserved_1_0 : 2;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_stat {
+ uint32_t u32;
+ struct ctu_can_fd_int_stat_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_STAT */
+ uint32_t rxi : 1;
+ uint32_t txi : 1;
+ uint32_t ewli : 1;
+ uint32_t doi : 1;
+ uint32_t fcsi : 1;
+ uint32_t ali : 1;
+ uint32_t bei : 1;
+ uint32_t ofi : 1;
+ uint32_t rxfi : 1;
+ uint32_t bsi : 1;
+ uint32_t rbnei : 1;
+ uint32_t txbhci : 1;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t txbhci : 1;
+ uint32_t rbnei : 1;
+ uint32_t bsi : 1;
+ uint32_t rxfi : 1;
+ uint32_t ofi : 1;
+ uint32_t bei : 1;
+ uint32_t ali : 1;
+ uint32_t fcsi : 1;
+ uint32_t doi : 1;
+ uint32_t ewli : 1;
+ uint32_t txi : 1;
+ uint32_t rxi : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_ena_set {
+ uint32_t u32;
+ struct ctu_can_fd_int_ena_set_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_ENA_SET */
+ uint32_t int_ena_set : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_ena_set : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_ena_clr {
+ uint32_t u32;
+ struct ctu_can_fd_int_ena_clr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_ENA_CLR */
+ uint32_t int_ena_clr : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_ena_clr : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_mask_set {
+ uint32_t u32;
+ struct ctu_can_fd_int_mask_set_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_MASK_SET */
+ uint32_t int_mask_set : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_mask_set : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_int_mask_clr {
+ uint32_t u32;
+ struct ctu_can_fd_int_mask_clr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* INT_MASK_CLR */
+ uint32_t int_mask_clr : 12;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t int_mask_clr : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_btr {
+ uint32_t u32;
+ struct ctu_can_fd_btr_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* BTR */
+ uint32_t prop : 7;
+ uint32_t ph1 : 6;
+ uint32_t ph2 : 6;
+ uint32_t brp : 8;
+ uint32_t sjw : 5;
+#else
+ uint32_t sjw : 5;
+ uint32_t brp : 8;
+ uint32_t ph2 : 6;
+ uint32_t ph1 : 6;
+ uint32_t prop : 7;
+#endif
+ } s;
+};
+
+union ctu_can_fd_btr_fd {
+ uint32_t u32;
+ struct ctu_can_fd_btr_fd_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* BTR_FD */
+ uint32_t prop_fd : 6;
+ uint32_t reserved_6 : 1;
+ uint32_t ph1_fd : 5;
+ uint32_t reserved_12 : 1;
+ uint32_t ph2_fd : 5;
+ uint32_t reserved_18 : 1;
+ uint32_t brp_fd : 8;
+ uint32_t sjw_fd : 5;
+#else
+ uint32_t sjw_fd : 5;
+ uint32_t brp_fd : 8;
+ uint32_t reserved_18 : 1;
+ uint32_t ph2_fd : 5;
+ uint32_t reserved_12 : 1;
+ uint32_t ph1_fd : 5;
+ uint32_t reserved_6 : 1;
+ uint32_t prop_fd : 6;
+#endif
+ } s;
+};
+
+union ctu_can_fd_ewl_erp_fault_state {
+ uint32_t u32;
+ struct ctu_can_fd_ewl_erp_fault_state_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* EWL */
+ uint32_t ew_limit : 8;
+ /* ERP */
+ uint32_t erp_limit : 8;
+ /* FAULT_STATE */
+ uint32_t era : 1;
+ uint32_t erp : 1;
+ uint32_t bof : 1;
+ uint32_t reserved_31_19 : 13;
+#else
+ uint32_t reserved_31_19 : 13;
+ uint32_t bof : 1;
+ uint32_t erp : 1;
+ uint32_t era : 1;
+ uint32_t erp_limit : 8;
+ uint32_t ew_limit : 8;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rec_tec {
+ uint32_t u32;
+ struct ctu_can_fd_rec_tec_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* REC */
+ uint32_t rec_val : 9;
+ uint32_t reserved_15_9 : 7;
+ /* TEC */
+ uint32_t tec_val : 9;
+ uint32_t reserved_31_25 : 7;
+#else
+ uint32_t reserved_31_25 : 7;
+ uint32_t tec_val : 9;
+ uint32_t reserved_15_9 : 7;
+ uint32_t rec_val : 9;
+#endif
+ } s;
+};
+
+union ctu_can_fd_err_norm_err_fd {
+ uint32_t u32;
+ struct ctu_can_fd_err_norm_err_fd_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* ERR_NORM */
+ uint32_t err_norm_val : 16;
+ /* ERR_FD */
+ uint32_t err_fd_val : 16;
+#else
+ uint32_t err_fd_val : 16;
+ uint32_t err_norm_val : 16;
+#endif
+ } s;
+};
+
+union ctu_can_fd_ctr_pres {
+ uint32_t u32;
+ struct ctu_can_fd_ctr_pres_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* CTR_PRES */
+ uint32_t ctpv : 9;
+ uint32_t ptx : 1;
+ uint32_t prx : 1;
+ uint32_t enorm : 1;
+ uint32_t efd : 1;
+ uint32_t reserved_31_13 : 19;
+#else
+ uint32_t reserved_31_13 : 19;
+ uint32_t efd : 1;
+ uint32_t enorm : 1;
+ uint32_t prx : 1;
+ uint32_t ptx : 1;
+ uint32_t ctpv : 9;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_a_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_a_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_A_MASK */
+ uint32_t bit_mask_a_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_a_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_a_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_a_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_A_VAL */
+ uint32_t bit_val_a_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_a_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_b_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_b_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_B_MASK */
+ uint32_t bit_mask_b_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_b_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_b_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_b_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_B_VAL */
+ uint32_t bit_val_b_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_b_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_c_mask {
+ uint32_t u32;
+ struct ctu_can_fd_filter_c_mask_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_C_MASK */
+ uint32_t bit_mask_c_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_mask_c_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_c_val {
+ uint32_t u32;
+ struct ctu_can_fd_filter_c_val_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_C_VAL */
+ uint32_t bit_val_c_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_val_c_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_ran_low {
+ uint32_t u32;
+ struct ctu_can_fd_filter_ran_low_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_RAN_LOW */
+ uint32_t bit_ran_low_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_ran_low_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_ran_high {
+ uint32_t u32;
+ struct ctu_can_fd_filter_ran_high_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_RAN_HIGH */
+ uint32_t bit_ran_high_val : 29;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t bit_ran_high_val : 29;
+#endif
+ } s;
+};
+
+union ctu_can_fd_filter_control_filter_status {
+ uint32_t u32;
+ struct ctu_can_fd_filter_control_filter_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* FILTER_CONTROL */
+ uint32_t fanb : 1;
+ uint32_t fane : 1;
+ uint32_t fafb : 1;
+ uint32_t fafe : 1;
+ uint32_t fbnb : 1;
+ uint32_t fbne : 1;
+ uint32_t fbfb : 1;
+ uint32_t fbfe : 1;
+ uint32_t fcnb : 1;
+ uint32_t fcne : 1;
+ uint32_t fcfb : 1;
+ uint32_t fcfe : 1;
+ uint32_t frnb : 1;
+ uint32_t frne : 1;
+ uint32_t frfb : 1;
+ uint32_t frfe : 1;
+ /* FILTER_STATUS */
+ uint32_t sfa : 1;
+ uint32_t sfb : 1;
+ uint32_t sfc : 1;
+ uint32_t sfr : 1;
+ uint32_t reserved_31_20 : 12;
+#else
+ uint32_t reserved_31_20 : 12;
+ uint32_t sfr : 1;
+ uint32_t sfc : 1;
+ uint32_t sfb : 1;
+ uint32_t sfa : 1;
+ uint32_t frfe : 1;
+ uint32_t frfb : 1;
+ uint32_t frne : 1;
+ uint32_t frnb : 1;
+ uint32_t fcfe : 1;
+ uint32_t fcfb : 1;
+ uint32_t fcne : 1;
+ uint32_t fcnb : 1;
+ uint32_t fbfe : 1;
+ uint32_t fbfb : 1;
+ uint32_t fbne : 1;
+ uint32_t fbnb : 1;
+ uint32_t fafe : 1;
+ uint32_t fafb : 1;
+ uint32_t fane : 1;
+ uint32_t fanb : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_mem_info {
+ uint32_t u32;
+ struct ctu_can_fd_rx_mem_info_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_MEM_INFO */
+ uint32_t rx_buff_size : 13;
+ uint32_t reserved_15_13 : 3;
+ uint32_t rx_mem_free : 13;
+ uint32_t reserved_31_29 : 3;
+#else
+ uint32_t reserved_31_29 : 3;
+ uint32_t rx_mem_free : 13;
+ uint32_t reserved_15_13 : 3;
+ uint32_t rx_buff_size : 13;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_pointers {
+ uint32_t u32;
+ struct ctu_can_fd_rx_pointers_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_POINTERS */
+ uint32_t rx_wpp : 12;
+ uint32_t reserved_15_12 : 4;
+ uint32_t rx_rpp : 12;
+ uint32_t reserved_31_28 : 4;
+#else
+ uint32_t reserved_31_28 : 4;
+ uint32_t rx_rpp : 12;
+ uint32_t reserved_15_12 : 4;
+ uint32_t rx_wpp : 12;
+#endif
+ } s;
+};
+
+union ctu_can_fd_rx_status_rx_settings {
+ uint32_t u32;
+ struct ctu_can_fd_rx_status_rx_settings_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* RX_STATUS */
+ uint32_t rxe : 1;
+ uint32_t rxf : 1;
+ uint32_t reserved_3_2 : 2;
+ uint32_t rxfrc : 11;
+ uint32_t reserved_15 : 1;
+ /* RX_SETTINGS */
+ uint32_t rtsop : 1;
+ uint32_t reserved_31_17 : 15;
+#else
+ uint32_t reserved_31_17 : 15;
+ uint32_t rtsop : 1;
+ uint32_t reserved_15 : 1;
+ uint32_t rxfrc : 11;
+ uint32_t reserved_3_2 : 2;
+ uint32_t rxf : 1;
+ uint32_t rxe : 1;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_rx_settings_rtsop {
+ RTS_END = 0x0,
+ RTS_BEG = 0x1,
+};
+
+union ctu_can_fd_rx_data {
+ uint32_t u32;
+ struct ctu_can_fd_rx_data_s {
+ /* RX_DATA */
+ uint32_t rx_data : 32;
+ } s;
+};
+
+union ctu_can_fd_tx_status {
+ uint32_t u32;
+ struct ctu_can_fd_tx_status_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_STATUS */
+ uint32_t tx1s : 4;
+ uint32_t tx2s : 4;
+ uint32_t tx3s : 4;
+ uint32_t tx4s : 4;
+ uint32_t reserved_31_16 : 16;
+#else
+ uint32_t reserved_31_16 : 16;
+ uint32_t tx4s : 4;
+ uint32_t tx3s : 4;
+ uint32_t tx2s : 4;
+ uint32_t tx1s : 4;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_tx_status_tx1s {
+ TXT_RDY = 0x1,
+ TXT_TRAN = 0x2,
+ TXT_ABTP = 0x3,
+ TXT_TOK = 0x4,
+ TXT_ERR = 0x6,
+ TXT_ABT = 0x7,
+ TXT_ETY = 0x8,
+};
+
+union ctu_can_fd_tx_command {
+ uint32_t u32;
+ struct ctu_can_fd_tx_command_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_COMMAND */
+ uint32_t txce : 1;
+ uint32_t txcr : 1;
+ uint32_t txca : 1;
+ uint32_t reserved_7_3 : 5;
+ uint32_t txb1 : 1;
+ uint32_t txb2 : 1;
+ uint32_t txb3 : 1;
+ uint32_t txb4 : 1;
+ uint32_t reserved_31_12 : 20;
+#else
+ uint32_t reserved_31_12 : 20;
+ uint32_t txb4 : 1;
+ uint32_t txb3 : 1;
+ uint32_t txb2 : 1;
+ uint32_t txb1 : 1;
+ uint32_t reserved_7_3 : 5;
+ uint32_t txca : 1;
+ uint32_t txcr : 1;
+ uint32_t txce : 1;
+#endif
+ } s;
+};
+
+union ctu_can_fd_tx_priority {
+ uint32_t u32;
+ struct ctu_can_fd_tx_priority_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TX_PRIORITY */
+ uint32_t txt1p : 3;
+ uint32_t reserved_3 : 1;
+ uint32_t txt2p : 3;
+ uint32_t reserved_7 : 1;
+ uint32_t txt3p : 3;
+ uint32_t reserved_11 : 1;
+ uint32_t txt4p : 3;
+ uint32_t reserved_31_15 : 17;
+#else
+ uint32_t reserved_31_15 : 17;
+ uint32_t txt4p : 3;
+ uint32_t reserved_11 : 1;
+ uint32_t txt3p : 3;
+ uint32_t reserved_7 : 1;
+ uint32_t txt2p : 3;
+ uint32_t reserved_3 : 1;
+ uint32_t txt1p : 3;
+#endif
+ } s;
+};
+
+union ctu_can_fd_err_capt_alc {
+ uint32_t u32;
+ struct ctu_can_fd_err_capt_alc_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* ERR_CAPT */
+ uint32_t err_pos : 5;
+ uint32_t err_type : 3;
+ uint32_t reserved_15_8 : 8;
+ /* ALC */
+ uint32_t alc_bit : 5;
+ uint32_t alc_id_field : 3;
+ uint32_t reserved_31_24 : 8;
+#else
+ uint32_t reserved_31_24 : 8;
+ uint32_t alc_id_field : 3;
+ uint32_t alc_bit : 5;
+ uint32_t reserved_15_8 : 8;
+ uint32_t err_type : 3;
+ uint32_t err_pos : 5;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_err_capt_err_pos {
+ ERC_POS_SOF = 0x0,
+ ERC_POS_ARB = 0x1,
+ ERC_POS_CTRL = 0x2,
+ ERC_POS_DATA = 0x3,
+ ERC_POS_CRC = 0x4,
+ ERC_POS_ACK = 0x5,
+ ERC_POS_EOF = 0x6,
+ ERC_POS_ERR = 0x7,
+ ERC_POS_OVRL = 0x8,
+ ERC_POS_OTHER = 0x1f,
+};
+
+enum ctu_can_fd_err_capt_err_type {
+ ERC_BIT_ERR = 0x0,
+ ERC_CRC_ERR = 0x1,
+ ERC_FRM_ERR = 0x2,
+ ERC_ACK_ERR = 0x3,
+ ERC_STUF_ERR = 0x4,
+};
+
+enum ctu_can_fd_alc_alc_id_field {
+ ALC_RSVD = 0x0,
+ ALC_BASE_ID = 0x1,
+ ALC_SRR_RTR = 0x2,
+ ALC_IDE = 0x3,
+ ALC_EXTENSION = 0x4,
+ ALC_RTR = 0x5,
+};
+
+union ctu_can_fd_trv_delay_ssp_cfg {
+ uint32_t u32;
+ struct ctu_can_fd_trv_delay_ssp_cfg_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* TRV_DELAY */
+ uint32_t trv_delay_value : 7;
+ uint32_t reserved_15_7 : 9;
+ /* SSP_CFG */
+ uint32_t ssp_offset : 8;
+ uint32_t ssp_src : 2;
+ uint32_t reserved_31_26 : 6;
+#else
+ uint32_t reserved_31_26 : 6;
+ uint32_t ssp_src : 2;
+ uint32_t ssp_offset : 8;
+ uint32_t reserved_15_7 : 9;
+ uint32_t trv_delay_value : 7;
+#endif
+ } s;
+};
+
+enum ctu_can_fd_ssp_cfg_ssp_src {
+ SSP_SRC_MEAS_N_OFFSET = 0x0,
+ SSP_SRC_NO_SSP = 0x1,
+ SSP_SRC_OFFSET = 0x2,
+};
+
+union ctu_can_fd_rx_fr_ctr {
+ uint32_t u32;
+ struct ctu_can_fd_rx_fr_ctr_s {
+ /* RX_FR_CTR */
+ uint32_t rx_fr_ctr_val : 32;
+ } s;
+};
+
+union ctu_can_fd_tx_fr_ctr {
+ uint32_t u32;
+ struct ctu_can_fd_tx_fr_ctr_s {
+ /* TX_FR_CTR */
+ uint32_t tx_fr_ctr_val : 32;
+ } s;
+};
+
+union ctu_can_fd_debug_register {
+ uint32_t u32;
+ struct ctu_can_fd_debug_register_s {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ /* DEBUG_REGISTER */
+ uint32_t stuff_count : 3;
+ uint32_t destuff_count : 3;
+ uint32_t pc_arb : 1;
+ uint32_t pc_con : 1;
+ uint32_t pc_dat : 1;
+ uint32_t pc_stc : 1;
+ uint32_t pc_crc : 1;
+ uint32_t pc_crcd : 1;
+ uint32_t pc_ack : 1;
+ uint32_t pc_ackd : 1;
+ uint32_t pc_eof : 1;
+ uint32_t pc_int : 1;
+ uint32_t pc_susp : 1;
+ uint32_t pc_ovr : 1;
+ uint32_t pc_sof : 1;
+ uint32_t reserved_31_19 : 13;
+#else
+ uint32_t reserved_31_19 : 13;
+ uint32_t pc_sof : 1;
+ uint32_t pc_ovr : 1;
+ uint32_t pc_susp : 1;
+ uint32_t pc_int : 1;
+ uint32_t pc_eof : 1;
+ uint32_t pc_ackd : 1;
+ uint32_t pc_ack : 1;
+ uint32_t pc_crcd : 1;
+ uint32_t pc_crc : 1;
+ uint32_t pc_stc : 1;
+ uint32_t pc_dat : 1;
+ uint32_t pc_con : 1;
+ uint32_t pc_arb : 1;
+ uint32_t destuff_count : 3;
+ uint32_t stuff_count : 3;
+#endif
+ } s;
+};
+
+union ctu_can_fd_yolo_reg {
+ uint32_t u32;
+ struct ctu_can_fd_yolo_reg_s {
+ /* YOLO_REG */
+ uint32_t yolo_val : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_low {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_low_s {
+ /* TIMESTAMP_LOW */
+ uint32_t timestamp_low : 32;
+ } s;
+};
+
+union ctu_can_fd_timestamp_high {
+ uint32_t u32;
+ struct ctu_can_fd_timestamp_high_s {
+ /* TIMESTAMP_HIGH */
+ uint32_t timestamp_high : 32;
+ } s;
+};
+
+#endif
diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c
new file mode 100644
index 000000000..d171c372e
--- /dev/null
+++ b/hw/net/can/ctucan_core.c
@@ -0,0 +1,687 @@
+/*
+ * CTU CAN FD PCI device emulation
+ * http://canbus.pages.fel.cvut.cz/
+ *
+ * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang 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 "qemu/log.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "ctucan_core.h"
+
+#ifndef DEBUG_CAN
+#define DEBUG_CAN 0
+#endif /*DEBUG_CAN*/
+
+#define DPRINTF(fmt, ...) \
+ do { \
+ if (DEBUG_CAN) { \
+ qemu_log("[ctucan]: " fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+static void ctucan_buff2frame(const uint8_t *buff, qemu_can_frame *frame)
+{
+ frame->can_id = 0;
+ frame->can_dlc = 0;
+ frame->flags = 0;
+
+ if (buff == NULL) {
+ return;
+ }
+ {
+ union ctu_can_fd_frame_form_w frame_form_w;
+ union ctu_can_fd_identifier_w identifier_w;
+ unsigned int ide;
+ uint32_t w;
+
+ w = le32_to_cpu(*(uint32_t *)buff);
+ frame_form_w = (union ctu_can_fd_frame_form_w)w;
+ frame->can_dlc = can_dlc2len(frame_form_w.s.dlc);
+
+ w = le32_to_cpu(*(uint32_t *)(buff + 4));
+ identifier_w = (union ctu_can_fd_identifier_w)w;
+
+ ide = frame_form_w.s.ide;
+ if (ide) {
+ frame->can_id = (identifier_w.s.identifier_base << 18) |
+ identifier_w.s.identifier_ext;
+ frame->can_id |= QEMU_CAN_EFF_FLAG;
+ } else {
+ frame->can_id = identifier_w.s.identifier_base;
+ }
+
+ if (frame_form_w.s.esi_rsv) {
+ frame->flags |= QEMU_CAN_FRMF_ESI;
+ }
+
+ if (frame_form_w.s.rtr) {
+ frame->can_id |= QEMU_CAN_RTR_FLAG;
+ }
+
+ if (frame_form_w.s.fdf) { /*CAN FD*/
+ frame->flags |= QEMU_CAN_FRMF_TYPE_FD;
+ if (frame_form_w.s.brs) {
+ frame->flags |= QEMU_CAN_FRMF_BRS;
+ }
+ }
+ }
+
+ memcpy(frame->data, buff + 0x10, 0x40);
+}
+
+
+static int ctucan_frame2buff(const qemu_can_frame *frame, uint8_t *buff)
+{
+ unsigned int bytes_cnt = -1;
+ memset(buff, 0, CTUCAN_MSG_MAX_LEN * sizeof(*buff));
+
+ if (frame == NULL) {
+ return bytes_cnt;
+ }
+ {
+ union ctu_can_fd_frame_form_w frame_form_w;
+ union ctu_can_fd_identifier_w identifier_w;
+
+ frame_form_w.u32 = 0;
+ identifier_w.u32 = 0;
+
+ bytes_cnt = frame->can_dlc;
+ bytes_cnt = (bytes_cnt + 3) & ~3;
+ bytes_cnt += 16;
+ frame_form_w.s.rwcnt = (bytes_cnt >> 2) - 1;
+
+ frame_form_w.s.dlc = can_len2dlc(frame->can_dlc);
+
+ if (frame->can_id & QEMU_CAN_EFF_FLAG) {
+ frame_form_w.s.ide = 1;
+ identifier_w.s.identifier_base =
+ (frame->can_id & 0x1FFC0000) >> 18;
+ identifier_w.s.identifier_ext = frame->can_id & 0x3FFFF;
+ } else {
+ identifier_w.s.identifier_base = frame->can_id & 0x7FF;
+ }
+
+ if (frame->flags & QEMU_CAN_FRMF_ESI) {
+ frame_form_w.s.esi_rsv = 1;
+ }
+
+ if (frame->can_id & QEMU_CAN_RTR_FLAG) {
+ frame_form_w.s.rtr = 1;
+ }
+
+ if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) { /*CAN FD*/
+ frame_form_w.s.fdf = 1;
+ if (frame->flags & QEMU_CAN_FRMF_BRS) {
+ frame_form_w.s.brs = 1;
+ }
+ }
+ *(uint32_t *)buff = cpu_to_le32(frame_form_w.u32);
+ *(uint32_t *)(buff + 4) = cpu_to_le32(identifier_w.u32);
+ }
+
+ memcpy(buff + 0x10, frame->data, 0x40);
+
+ return bytes_cnt;
+}
+
+static void ctucan_update_irq(CtuCanCoreState *s)
+{
+ union ctu_can_fd_int_stat int_rq;
+
+ int_rq.u32 = 0;
+
+ if (s->rx_status_rx_settings.s.rxfrc) {
+ int_rq.s.rbnei = 1;
+ }
+
+ int_rq.u32 &= ~s->int_mask.u32;
+ s->int_stat.u32 |= int_rq.u32;
+ if (s->int_stat.u32 & s->int_ena.u32) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void ctucan_update_txnf(CtuCanCoreState *s)
+{
+ int i;
+ int txnf;
+ unsigned int buff_st;
+
+ txnf = 0;
+
+ for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
+ buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
+ if (buff_st == TXT_ETY) {
+ txnf = 1;
+ }
+ }
+ s->status.s.txnf = txnf;
+}
+
+void ctucan_hardware_reset(CtuCanCoreState *s)
+{
+ DPRINTF("Hardware reset in progress!!!\n");
+ int i;
+ unsigned int buff_st;
+ uint32_t buff_st_mask;
+
+ s->tx_status.u32 = 0;
+ for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
+ buff_st_mask = 0xf << (i * 4);
+ buff_st = TXT_ETY;
+ s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
+ (buff_st << (i * 4));
+ }
+ s->status.s.idle = 1;
+
+ ctucan_update_txnf(s);
+
+ s->rx_status_rx_settings.u32 = 0;
+ s->rx_tail_pos = 0;
+ s->rx_cnt = 0;
+ s->rx_frame_rem = 0;
+
+ /* Flush RX buffer */
+ s->rx_tail_pos = 0;
+ s->rx_cnt = 0;
+ s->rx_frame_rem = 0;
+
+ /* Set on progdokum reset value */
+ s->mode_settings.u32 = 0;
+ s->mode_settings.s.fde = 1;
+
+ s->int_stat.u32 = 0;
+ s->int_ena.u32 = 0;
+ s->int_mask.u32 = 0;
+
+ s->rx_status_rx_settings.u32 = 0;
+ s->rx_status_rx_settings.s.rxe = 0;
+
+ s->rx_fr_ctr.u32 = 0;
+ s->tx_fr_ctr.u32 = 0;
+
+ s->yolo_reg.s.yolo_val = 3735928559;
+
+ qemu_irq_lower(s->irq);
+}
+
+static void ctucan_send_ready_buffers(CtuCanCoreState *s)
+{
+ qemu_can_frame frame;
+ uint8_t *pf;
+ int buff2tx_idx;
+ uint32_t tx_prio_max;
+
+ if (!s->mode_settings.s.ena) {
+ return;
+ }
+
+ do {
+ union ctu_can_fd_int_stat int_stat;
+ int i;
+ buff2tx_idx = -1;
+ tx_prio_max = 0;
+
+ for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
+ uint32_t prio;
+
+ if (extract32(s->tx_status.u32, i * 4, 4) != TXT_RDY) {
+ continue;
+ }
+ prio = (s->tx_priority.u32 >> (i * 4)) & 0x7;
+ if (tx_prio_max < prio) {
+ tx_prio_max = prio;
+ buff2tx_idx = i;
+ }
+ }
+ if (buff2tx_idx == -1) {
+ break;
+ }
+ int_stat.u32 = 0;
+ pf = s->tx_buffer[buff2tx_idx].data;
+ ctucan_buff2frame(pf, &frame);
+ s->status.s.idle = 0;
+ s->status.s.txs = 1;
+ can_bus_client_send(&s->bus_client, &frame, 1);
+ s->status.s.idle = 1;
+ s->status.s.txs = 0;
+ s->tx_fr_ctr.s.tx_fr_ctr_val++;
+ int_stat.s.txi = 1;
+ int_stat.s.txbhci = 1;
+ s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
+ s->tx_status.u32 = deposit32(s->tx_status.u32,
+ buff2tx_idx * 4, 4, TXT_TOK);
+ } while (1);
+}
+
+#define CTUCAN_CORE_TXBUFF_SPAN \
+ (CTU_CAN_FD_TXTB2_DATA_1 - CTU_CAN_FD_TXTB1_DATA_1)
+
+void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ int i;
+
+ DPRINTF("write 0x%02llx addr 0x%02x\n",
+ (unsigned long long)val, (unsigned int)addr);
+
+ if (addr >= CTUCAN_CORE_MEM_SIZE) {
+ return;
+ }
+
+ if (addr >= CTU_CAN_FD_TXTB1_DATA_1) {
+ int buff_num;
+ addr -= CTU_CAN_FD_TXTB1_DATA_1;
+ buff_num = addr / CTUCAN_CORE_TXBUFF_SPAN;
+ addr %= CTUCAN_CORE_TXBUFF_SPAN;
+ if ((buff_num < CTUCAN_CORE_TXBUF_NUM) &&
+ ((addr + size) <= sizeof(s->tx_buffer[buff_num].data))) {
+ stn_le_p(s->tx_buffer[buff_num].data + addr, size, val);
+ }
+ } else {
+ switch (addr & ~3) {
+ case CTU_CAN_FD_MODE:
+ s->mode_settings.u32 = (uint32_t)val;
+ if (s->mode_settings.s.rst) {
+ ctucan_hardware_reset(s);
+ s->mode_settings.s.rst = 0;
+ }
+ break;
+ case CTU_CAN_FD_COMMAND:
+ {
+ union ctu_can_fd_command command;
+ command.u32 = (uint32_t)val;
+ if (command.s.cdo) {
+ s->status.s.dor = 0;
+ }
+ if (command.s.rrb) {
+ s->rx_tail_pos = 0;
+ s->rx_cnt = 0;
+ s->rx_frame_rem = 0;
+ s->rx_status_rx_settings.s.rxfrc = 0;
+ }
+ if (command.s.txfcrst) {
+ s->tx_fr_ctr.s.tx_fr_ctr_val = 0;
+ }
+ if (command.s.rxfcrst) {
+ s->rx_fr_ctr.s.rx_fr_ctr_val = 0;
+ }
+ break;
+ }
+ case CTU_CAN_FD_INT_STAT:
+ s->int_stat.u32 &= ~(uint32_t)val;
+ break;
+ case CTU_CAN_FD_INT_ENA_SET:
+ s->int_ena.u32 |= (uint32_t)val;
+ break;
+ case CTU_CAN_FD_INT_ENA_CLR:
+ s->int_ena.u32 &= ~(uint32_t)val;
+ break;
+ case CTU_CAN_FD_INT_MASK_SET:
+ s->int_mask.u32 |= (uint32_t)val;
+ break;
+ case CTU_CAN_FD_INT_MASK_CLR:
+ s->int_mask.u32 &= ~(uint32_t)val;
+ break;
+ case CTU_CAN_FD_TX_COMMAND:
+ if (s->mode_settings.s.ena) {
+ union ctu_can_fd_tx_command tx_command;
+ union ctu_can_fd_tx_command mask;
+ unsigned int buff_st;
+ uint32_t buff_st_mask;
+
+ tx_command.u32 = (uint32_t)val;
+ mask.u32 = 0;
+ mask.s.txb1 = 1;
+
+ for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
+ if (!(tx_command.u32 & (mask.u32 << i))) {
+ continue;
+ }
+ buff_st_mask = 0xf << (i * 4);
+ buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
+ if (tx_command.s.txca) {
+ if (buff_st == TXT_RDY) {
+ buff_st = TXT_ABT;
+ }
+ }
+ if (tx_command.s.txcr) {
+ if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
+ (buff_st == TXT_ABT) || (buff_st == TXT_ETY))
+ buff_st = TXT_RDY;
+ }
+ if (tx_command.s.txce) {
+ if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
+ (buff_st == TXT_ABT))
+ buff_st = TXT_ETY;
+ }
+ s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
+ (buff_st << (i * 4));
+ }
+
+ ctucan_send_ready_buffers(s);
+ ctucan_update_txnf(s);
+ }
+ break;
+ case CTU_CAN_FD_TX_PRIORITY:
+ s->tx_priority.u32 = (uint32_t)val;
+ break;
+ }
+
+ ctucan_update_irq(s);
+ }
+
+ return;
+}
+
+uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size)
+{
+ uint32_t val = 0;
+
+ DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
+
+ if (addr > CTUCAN_CORE_MEM_SIZE) {
+ return 0;
+ }
+
+ switch (addr & ~3) {
+ case CTU_CAN_FD_DEVICE_ID:
+ {
+ union ctu_can_fd_device_id_version idver;
+ idver.u32 = 0;
+ idver.s.device_id = CTU_CAN_FD_ID;
+ idver.s.ver_major = 2;
+ idver.s.ver_minor = 2;
+ val = idver.u32;
+ }
+ break;
+ case CTU_CAN_FD_MODE:
+ val = s->mode_settings.u32;
+ break;
+ case CTU_CAN_FD_STATUS:
+ val = s->status.u32;
+ break;
+ case CTU_CAN_FD_INT_STAT:
+ val = s->int_stat.u32;
+ break;
+ case CTU_CAN_FD_INT_ENA_SET:
+ case CTU_CAN_FD_INT_ENA_CLR:
+ val = s->int_ena.u32;
+ break;
+ case CTU_CAN_FD_INT_MASK_SET:
+ case CTU_CAN_FD_INT_MASK_CLR:
+ val = s->int_mask.u32;
+ break;
+ case CTU_CAN_FD_RX_MEM_INFO:
+ s->rx_mem_info.u32 = 0;
+ s->rx_mem_info.s.rx_buff_size = CTUCAN_RCV_BUF_LEN >> 2;
+ s->rx_mem_info.s.rx_mem_free = (CTUCAN_RCV_BUF_LEN -
+ s->rx_cnt) >> 2;
+ val = s->rx_mem_info.u32;
+ break;
+ case CTU_CAN_FD_RX_POINTERS:
+ {
+ uint32_t rx_head_pos = s->rx_tail_pos + s->rx_cnt;
+ rx_head_pos %= CTUCAN_RCV_BUF_LEN;
+ s->rx_pointers.s.rx_wpp = rx_head_pos;
+ s->rx_pointers.s.rx_rpp = s->rx_tail_pos;
+ val = s->rx_pointers.u32;
+ break;
+ }
+ case CTU_CAN_FD_RX_STATUS:
+ case CTU_CAN_FD_RX_SETTINGS:
+ if (!s->rx_status_rx_settings.s.rxfrc) {
+ s->rx_status_rx_settings.s.rxe = 1;
+ } else {
+ s->rx_status_rx_settings.s.rxe = 0;
+ }
+ if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
+ s->rx_status_rx_settings.s.rxf = 1;
+ } else {
+ s->rx_status_rx_settings.s.rxf = 0;
+ }
+ val = s->rx_status_rx_settings.u32;
+ break;
+ case CTU_CAN_FD_RX_DATA:
+ if (s->rx_cnt) {
+ memcpy(&val, s->rx_buff + s->rx_tail_pos, 4);
+ val = le32_to_cpu(val);
+ if (!s->rx_frame_rem) {
+ union ctu_can_fd_frame_form_w frame_form_w;
+ frame_form_w.u32 = val;
+ s->rx_frame_rem = frame_form_w.s.rwcnt * 4 + 4;
+ }
+ s->rx_cnt -= 4;
+ s->rx_frame_rem -= 4;
+ if (!s->rx_frame_rem) {
+ s->rx_status_rx_settings.s.rxfrc--;
+ if (!s->rx_status_rx_settings.s.rxfrc) {
+ s->status.s.rxne = 0;
+ s->status.s.idle = 1;
+ s->status.s.rxs = 0;
+ }
+ }
+ s->rx_tail_pos = (s->rx_tail_pos + 4) % CTUCAN_RCV_BUF_LEN;
+ } else {
+ val = 0;
+ }
+ break;
+ case CTU_CAN_FD_TX_STATUS:
+ val = s->tx_status.u32;
+ break;
+ case CTU_CAN_FD_TX_PRIORITY:
+ val = s->tx_priority.u32;
+ break;
+ case CTU_CAN_FD_RX_FR_CTR:
+ val = s->rx_fr_ctr.s.rx_fr_ctr_val;
+ break;
+ case CTU_CAN_FD_TX_FR_CTR:
+ val = s->tx_fr_ctr.s.tx_fr_ctr_val;
+ break;
+ case CTU_CAN_FD_YOLO_REG:
+ val = s->yolo_reg.s.yolo_val;
+ break;
+ }
+
+ val >>= ((addr & 3) << 3);
+ if (size < 8) {
+ val &= ((uint64_t)1 << (size << 3)) - 1;
+ }
+
+ return val;
+}
+
+bool ctucan_can_receive(CanBusClientState *client)
+{
+ CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
+
+ if (!s->mode_settings.s.ena) {
+ return false;
+ }
+
+ return true; /* always return true, when operation mode */
+}
+
+ssize_t ctucan_receive(CanBusClientState *client, const qemu_can_frame *frames,
+ size_t frames_cnt)
+{
+ CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
+ static uint8_t rcv[CTUCAN_MSG_MAX_LEN];
+ int i;
+ int ret = -1;
+ const qemu_can_frame *frame = frames;
+ union ctu_can_fd_int_stat int_stat;
+ int_stat.u32 = 0;
+
+ if (frames_cnt <= 0) {
+ return 0;
+ }
+
+ ret = ctucan_frame2buff(frame, rcv);
+
+ if (s->rx_cnt + ret > CTUCAN_RCV_BUF_LEN) { /* Data overrun. */
+ s->status.s.dor = 1;
+ int_stat.s.doi = 1;
+ s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
+ ctucan_update_irq(s);
+ DPRINTF("Receive FIFO overrun\n");
+ return ret;
+ }
+ s->status.s.idle = 0;
+ s->status.s.rxs = 1;
+ int_stat.s.rxi = 1;
+ if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
+ int_stat.s.rxfi = 1;
+ }
+ s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
+ s->rx_fr_ctr.s.rx_fr_ctr_val++;
+ s->rx_status_rx_settings.s.rxfrc++;
+ for (i = 0; i < ret; i++) {
+ s->rx_buff[(s->rx_tail_pos + s->rx_cnt) % CTUCAN_RCV_BUF_LEN] = rcv[i];
+ s->rx_cnt++;
+ }
+ s->status.s.rxne = 1;
+
+ ctucan_update_irq(s);
+
+ return 1;
+}
+
+static CanBusClientInfo ctucan_bus_client_info = {
+ .can_receive = ctucan_can_receive,
+ .receive = ctucan_receive,
+};
+
+
+int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus)
+{
+ s->bus_client.info = &ctucan_bus_client_info;
+
+ if (!bus) {
+ return -EINVAL;
+ }
+
+ if (can_bus_insert_client(bus, &s->bus_client) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void ctucan_disconnect(CtuCanCoreState *s)
+{
+ can_bus_remove_client(&s->bus_client);
+}
+
+int ctucan_init(CtuCanCoreState *s, qemu_irq irq)
+{
+ s->irq = irq;
+
+ qemu_irq_lower(s->irq);
+
+ ctucan_hardware_reset(s);
+
+ return 0;
+}
+
+const VMStateDescription vmstate_qemu_ctucan_tx_buffer = {
+ .name = "qemu_ctucan_tx_buffer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ctucan_post_load(void *opaque, int version_id)
+{
+ CtuCanCoreState *s = opaque;
+ ctucan_update_irq(s);
+ return 0;
+}
+
+/* VMState is needed for live migration of QEMU images */
+const VMStateDescription vmstate_ctucan = {
+ .name = "ctucan",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = ctucan_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState),
+ VMSTATE_UINT32(status.u32, CtuCanCoreState),
+ VMSTATE_UINT32(int_stat.u32, CtuCanCoreState),
+ VMSTATE_UINT32(int_ena.u32, CtuCanCoreState),
+ VMSTATE_UINT32(int_mask.u32, CtuCanCoreState),
+ VMSTATE_UINT32(brt.u32, CtuCanCoreState),
+ VMSTATE_UINT32(brt_fd.u32, CtuCanCoreState),
+ VMSTATE_UINT32(ewl_erp_fault_state.u32, CtuCanCoreState),
+ VMSTATE_UINT32(rec_tec.u32, CtuCanCoreState),
+ VMSTATE_UINT32(err_norm_err_fd.u32, CtuCanCoreState),
+ VMSTATE_UINT32(ctr_pres.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_a_mask.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_a_val.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_b_mask.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_b_val.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_c_mask.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_c_val.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_ran_low.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_ran_high.u32, CtuCanCoreState),
+ VMSTATE_UINT32(filter_control_filter_status.u32, CtuCanCoreState),
+ VMSTATE_UINT32(rx_mem_info.u32, CtuCanCoreState),
+ VMSTATE_UINT32(rx_pointers.u32, CtuCanCoreState),
+ VMSTATE_UINT32(rx_status_rx_settings.u32, CtuCanCoreState),
+ VMSTATE_UINT32(tx_status.u32, CtuCanCoreState),
+ VMSTATE_UINT32(tx_priority.u32, CtuCanCoreState),
+ VMSTATE_UINT32(err_capt_alc.u32, CtuCanCoreState),
+ VMSTATE_UINT32(trv_delay_ssp_cfg.u32, CtuCanCoreState),
+ VMSTATE_UINT32(rx_fr_ctr.u32, CtuCanCoreState),
+ VMSTATE_UINT32(tx_fr_ctr.u32, CtuCanCoreState),
+ VMSTATE_UINT32(debug_register.u32, CtuCanCoreState),
+ VMSTATE_UINT32(yolo_reg.u32, CtuCanCoreState),
+ VMSTATE_UINT32(timestamp_low.u32, CtuCanCoreState),
+ VMSTATE_UINT32(timestamp_high.u32, CtuCanCoreState),
+
+ VMSTATE_STRUCT_ARRAY(tx_buffer, CtuCanCoreState,
+ CTUCAN_CORE_TXBUF_NUM, 0, vmstate_qemu_ctucan_tx_buffer,
+ CtuCanCoreMsgBuffer),
+
+ VMSTATE_BUFFER(rx_buff, CtuCanCoreState),
+ VMSTATE_UINT32(rx_tail_pos, CtuCanCoreState),
+ VMSTATE_UINT32(rx_cnt, CtuCanCoreState),
+ VMSTATE_UINT32(rx_frame_rem, CtuCanCoreState),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/net/can/ctucan_core.h b/hw/net/can/ctucan_core.h
new file mode 100644
index 000000000..bbc09ae06
--- /dev/null
+++ b/hw/net/can/ctucan_core.h
@@ -0,0 +1,126 @@
+/*
+ * CTU CAN FD device emulation
+ * http://canbus.pages.fel.cvut.cz/
+ *
+ * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang 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_CTUCAN_CORE_H
+#define HW_CAN_CTUCAN_CORE_H
+
+#include "exec/hwaddr.h"
+#include "net/can_emu.h"
+
+#ifndef HOST_WORDS_BIGENDIAN
+#define __LITTLE_ENDIAN_BITFIELD 1
+#endif
+
+#include "ctu_can_fd_frame.h"
+#include "ctu_can_fd_regs.h"
+
+#define CTUCAN_CORE_MEM_SIZE 0x500
+
+/* The max size for a message in FIFO */
+#define CTUCAN_MSG_MAX_LEN (CTU_CAN_FD_DATA_1_4_W + 64)
+/* The receive buffer size. */
+#define CTUCAN_RCV_BUF_LEN (1024 * 8)
+
+
+/* The max size for a message buffer */
+#define CTUCAN_CORE_MSG_MAX_LEN 0x50
+/* The receive buffer size. */
+#define CTUCAN_CORE_RCV_BUF_LEN 0x1000
+
+#define CTUCAN_CORE_TXBUF_NUM 4
+
+typedef struct CtuCanCoreMsgBuffer {
+ uint8_t data[CTUCAN_CORE_MSG_MAX_LEN];
+} CtuCanCoreMsgBuffer;
+
+typedef struct CtuCanCoreState {
+ union ctu_can_fd_mode_settings mode_settings;
+ union ctu_can_fd_status status;
+ union ctu_can_fd_int_stat int_stat;
+ union ctu_can_fd_int_ena_set int_ena;
+ union ctu_can_fd_int_mask_set int_mask;
+ union ctu_can_fd_btr brt;
+ union ctu_can_fd_btr_fd brt_fd;
+ union ctu_can_fd_ewl_erp_fault_state ewl_erp_fault_state;
+ union ctu_can_fd_rec_tec rec_tec;
+ union ctu_can_fd_err_norm_err_fd err_norm_err_fd;
+ union ctu_can_fd_ctr_pres ctr_pres;
+ union ctu_can_fd_filter_a_mask filter_a_mask;
+ union ctu_can_fd_filter_a_val filter_a_val;
+ union ctu_can_fd_filter_b_mask filter_b_mask;
+ union ctu_can_fd_filter_b_val filter_b_val;
+ union ctu_can_fd_filter_c_mask filter_c_mask;
+ union ctu_can_fd_filter_c_val filter_c_val;
+ union ctu_can_fd_filter_ran_low filter_ran_low;
+ union ctu_can_fd_filter_ran_high filter_ran_high;
+ union ctu_can_fd_filter_control_filter_status filter_control_filter_status;
+ union ctu_can_fd_rx_mem_info rx_mem_info;
+ union ctu_can_fd_rx_pointers rx_pointers;
+ union ctu_can_fd_rx_status_rx_settings rx_status_rx_settings;
+ union ctu_can_fd_tx_status tx_status;
+ union ctu_can_fd_tx_priority tx_priority;
+ union ctu_can_fd_err_capt_alc err_capt_alc;
+ union ctu_can_fd_trv_delay_ssp_cfg trv_delay_ssp_cfg;
+ union ctu_can_fd_rx_fr_ctr rx_fr_ctr;
+ union ctu_can_fd_tx_fr_ctr tx_fr_ctr;
+ union ctu_can_fd_debug_register debug_register;
+ union ctu_can_fd_yolo_reg yolo_reg;
+ union ctu_can_fd_timestamp_low timestamp_low;
+ union ctu_can_fd_timestamp_high timestamp_high;
+
+ CtuCanCoreMsgBuffer tx_buffer[CTUCAN_CORE_TXBUF_NUM];
+
+ uint8_t rx_buff[CTUCAN_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */
+ uint32_t rx_tail_pos; /* Count by bytes. */
+ uint32_t rx_cnt; /* Count by bytes. */
+ uint32_t rx_frame_rem;
+
+ qemu_irq irq;
+ CanBusClientState bus_client;
+} CtuCanCoreState;
+
+void ctucan_hardware_reset(CtuCanCoreState *s);
+
+void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
+ unsigned size);
+
+uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size);
+
+int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus);
+
+void ctucan_disconnect(CtuCanCoreState *s);
+
+int ctucan_init(CtuCanCoreState *s, qemu_irq irq);
+
+bool ctucan_can_receive(CanBusClientState *client);
+
+ssize_t ctucan_receive(CanBusClientState *client,
+ const qemu_can_frame *frames, size_t frames_cnt);
+
+extern const VMStateDescription vmstate_ctucan;
+
+#endif
diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c
new file mode 100644
index 000000000..f1c86cd06
--- /dev/null
+++ b/hw/net/can/ctucan_pci.c
@@ -0,0 +1,281 @@
+/*
+ * CTU CAN FD PCI device emulation
+ * http://canbus.pages.fel.cvut.cz/
+ *
+ * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
+ *
+ * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
+ * Jin Yang 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 "qemu/event_notifier.h"
+#include "qemu/module.h"
+#include "qemu/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/irq.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "net/can_emu.h"
+
+#include "ctucan_core.h"
+
+#define TYPE_CTUCAN_PCI_DEV "ctucan_pci"
+
+typedef struct CtuCanPCIState CtuCanPCIState;
+DECLARE_INSTANCE_CHECKER(CtuCanPCIState, CTUCAN_PCI_DEV,
+ TYPE_CTUCAN_PCI_DEV)
+
+#define CTUCAN_PCI_CORE_COUNT 2
+#define CTUCAN_PCI_CORE_RANGE 0x10000
+
+#define CTUCAN_PCI_BAR_COUNT 2
+
+#define CTUCAN_PCI_BYTES_PER_CORE 0x4000
+
+#ifndef PCI_VENDOR_ID_TEDIA
+#define PCI_VENDOR_ID_TEDIA 0x1760
+#endif
+
+#define PCI_DEVICE_ID_TEDIA_CTUCAN_VER21 0xff00
+
+#define CTUCAN_BAR0_RANGE 0x8000
+#define CTUCAN_BAR0_CTUCAN_ID 0x0000
+#define CTUCAN_BAR0_CRA_BASE 0x4000
+#define CYCLONE_IV_CRA_A2P_IE (0x0050)
+
+#define CTUCAN_WITHOUT_CTUCAN_ID 0
+#define CTUCAN_WITH_CTUCAN_ID 1
+
+struct CtuCanPCIState {
+ /*< private >*/
+ PCIDevice dev;
+ /*< public >*/
+ MemoryRegion ctucan_io[CTUCAN_PCI_BAR_COUNT];
+
+ CtuCanCoreState ctucan_state[CTUCAN_PCI_CORE_COUNT];
+ qemu_irq irq;
+
+ char *model; /* The model that support, only SJA1000 now. */
+ CanBusState *canbus[CTUCAN_PCI_CORE_COUNT];
+};
+
+static void ctucan_pci_reset(DeviceState *dev)
+{
+ CtuCanPCIState *d = CTUCAN_PCI_DEV(dev);
+ int i;
+
+ for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
+ ctucan_hardware_reset(&d->ctucan_state[i]);
+ }
+}
+
+static uint64_t ctucan_pci_id_cra_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ if (addr >= 4) {
+ return 0;
+ }
+
+ uint64_t tmp = 0xC0000000 + CTUCAN_PCI_CORE_COUNT;
+ tmp >>= ((addr & 3) << 3);
+ if (size < 8) {
+ tmp &= ((uint64_t)1 << (size << 3)) - 1;
+ }
+ return tmp;
+}
+
+static void ctucan_pci_id_cra_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+
+}
+
+static uint64_t ctucan_pci_cores_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CtuCanPCIState *d = opaque;
+ CtuCanCoreState *s;
+ hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE;
+
+ if (core_num >= CTUCAN_PCI_CORE_COUNT) {
+ return 0;
+ }
+
+ s = &d->ctucan_state[core_num];
+
+ return ctucan_mem_read(s, addr % CTUCAN_PCI_BYTES_PER_CORE, size);
+}
+
+static void ctucan_pci_cores_io_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ CtuCanPCIState *d = opaque;
+ CtuCanCoreState *s;
+ hwaddr core_num = addr / CTUCAN_PCI_BYTES_PER_CORE;
+
+ if (core_num >= CTUCAN_PCI_CORE_COUNT) {
+ return;
+ }
+
+ s = &d->ctucan_state[core_num];
+
+ return ctucan_mem_write(s, addr % CTUCAN_PCI_BYTES_PER_CORE, data, size);
+}
+
+static const MemoryRegionOps ctucan_pci_id_cra_io_ops = {
+ .read = ctucan_pci_id_cra_io_read,
+ .write = ctucan_pci_id_cra_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+};
+
+static const MemoryRegionOps ctucan_pci_cores_io_ops = {
+ .read = ctucan_pci_cores_io_read,
+ .write = ctucan_pci_cores_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+};
+
+static void ctucan_pci_realize(PCIDevice *pci_dev, Error **errp)
+{
+ CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev);
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
+
+ d->irq = pci_allocate_irq(&d->dev);
+
+ for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
+ ctucan_init(&d->ctucan_state[i], d->irq);
+ }
+
+ for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
+ if (ctucan_connect_to_bus(&d->ctucan_state[i], d->canbus[i]) < 0) {
+ error_setg(errp, "ctucan_connect_to_bus failed");
+ return;
+ }
+ }
+
+ memory_region_init_io(&d->ctucan_io[0], OBJECT(d),
+ &ctucan_pci_id_cra_io_ops, d,
+ "ctucan_pci-core0", CTUCAN_BAR0_RANGE);
+ memory_region_init_io(&d->ctucan_io[1], OBJECT(d),
+ &ctucan_pci_cores_io_ops, d,
+ "ctucan_pci-core1", CTUCAN_PCI_CORE_RANGE);
+
+ for (i = 0 ; i < CTUCAN_PCI_BAR_COUNT; i++) {
+ pci_register_bar(&d->dev, i, PCI_BASE_ADDRESS_MEM_MASK & 0,
+ &d->ctucan_io[i]);
+ }
+}
+
+static void ctucan_pci_exit(PCIDevice *pci_dev)
+{
+ CtuCanPCIState *d = CTUCAN_PCI_DEV(pci_dev);
+ int i;
+
+ for (i = 0 ; i < CTUCAN_PCI_CORE_COUNT; i++) {
+ ctucan_disconnect(&d->ctucan_state[i]);
+ }
+
+ qemu_free_irq(d->irq);
+}
+
+static const VMStateDescription vmstate_ctucan_pci = {
+ .name = "ctucan_pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, CtuCanPCIState),
+ VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan,
+ CtuCanCoreState),
+#if CTUCAN_PCI_CORE_COUNT >= 2
+ VMSTATE_STRUCT(ctucan_state[1], CtuCanPCIState, 0, vmstate_ctucan,
+ CtuCanCoreState),
+#endif
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ctucan_pci_instance_init(Object *obj)
+{
+ CtuCanPCIState *d = CTUCAN_PCI_DEV(obj);
+
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
+ (Object **)&d->canbus[0],
+ qdev_prop_allow_set_link_before_realize, 0);
+#if CTUCAN_PCI_CORE_COUNT >= 2
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+ (Object **)&d->canbus[1],
+ qdev_prop_allow_set_link_before_realize, 0);
+#endif
+}
+
+static void ctucan_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = ctucan_pci_realize;
+ k->exit = ctucan_pci_exit;
+ k->vendor_id = PCI_VENDOR_ID_TEDIA;
+ k->device_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21;
+ k->revision = 0x00;
+ k->class_id = 0x000c09;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_TEDIA;
+ k->subsystem_id = PCI_DEVICE_ID_TEDIA_CTUCAN_VER21;
+ dc->desc = "CTU CAN PCI";
+ dc->vmsd = &vmstate_ctucan_pci;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = ctucan_pci_reset;
+}
+
+static const TypeInfo ctucan_pci_info = {
+ .name = TYPE_CTUCAN_PCI_DEV,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(CtuCanPCIState),
+ .class_init = ctucan_pci_class_init,
+ .instance_init = ctucan_pci_instance_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void ctucan_pci_register_types(void)
+{
+ type_register_static(&ctucan_pci_info);
+}
+
+type_init(ctucan_pci_register_types)
diff --git a/hw/net/can/meson.build b/hw/net/can/meson.build
new file mode 100644
index 000000000..8fabbd9ee
--- /dev/null
+++ b/hw/net/can/meson.build
@@ -0,0 +1,7 @@
+softmmu_ss.add(when: 'CONFIG_CAN_SJA1000', if_true: files('can_sja1000.c'))
+softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_kvaser_pci.c'))
+softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_pcm3680_pci.c'))
+softmmu_ss.add(when: 'CONFIG_CAN_PCI', if_true: files('can_mioe3680_pci.c'))
+softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD', if_true: files('ctucan_core.c'))
+softmmu_ss.add(when: 'CONFIG_CAN_CTUCANFD_PCI', if_true: files('ctucan_pci.c'))
+softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP', if_true: files('xlnx-zynqmp-can.c'))
diff --git a/hw/net/can/trace-events b/hw/net/can/trace-events
new file mode 100644
index 000000000..8346a98ab
--- /dev/null
+++ b/hw/net/can/trace-events
@@ -0,0 +1,9 @@
+# xlnx-zynqmp-can.c
+xlnx_can_update_irq(uint32_t isr, uint32_t ier, uint32_t irq) "ISR: 0x%08x IER: 0x%08x IRQ: 0x%08x"
+xlnx_can_reset(uint32_t val) "Resetting controller with value = 0x%08x"
+xlnx_can_rx_fifo_filter_reject(uint32_t id, uint8_t dlc) "Frame: ID: 0x%08x DLC: 0x%02x"
+xlnx_can_filter_id_pre_write(uint8_t filter_num, uint32_t value) "Filter%d ID: 0x%08x"
+xlnx_can_filter_mask_pre_write(uint8_t filter_num, uint32_t value) "Filter%d MASK: 0x%08x"
+xlnx_can_tx_data(uint32_t id, uint8_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
+xlnx_can_rx_data(uint32_t id, uint32_t dlc, uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) "Frame: ID: 0x%08x DLC: 0x%02x DATA: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"
+xlnx_can_rx_discard(uint32_t status) "Controller is not enabled for bus communication. Status Register: 0x%08x"
diff --git a/hw/net/can/trace.h b/hw/net/can/trace.h
new file mode 100644
index 000000000..d391c6490
--- /dev/null
+++ b/hw/net/can/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_net_can.h"
diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c
new file mode 100644
index 000000000..22bb8910f
--- /dev/null
+++ b/hw/net/can/xlnx-zynqmp-can.c
@@ -0,0 +1,1160 @@
+/*
+ * QEMU model of the Xilinx ZynqMP CAN controller.
+ * This implementation is based on the following datasheet:
+ * https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf
+ *
+ * Copyright (c) 2020 Xilinx Inc.
+ *
+ * Written-by: Vikram Garhwal<fnu.vikram@xilinx.com>
+ *
+ * Based on QEMU CAN Device emulation implemented by Jin Yang, Deniz Eren and
+ * Pavel Pisa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), 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 "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "net/can_emu.h"
+#include "net/can_host.h"
+#include "qemu/event_notifier.h"
+#include "qom/object_interfaces.h"
+#include "hw/net/xlnx-zynqmp-can.h"
+#include "trace.h"
+
+#ifndef XLNX_ZYNQMP_CAN_ERR_DEBUG
+#define XLNX_ZYNQMP_CAN_ERR_DEBUG 0
+#endif
+
+#define MAX_DLC 8
+#undef ERROR
+
+REG32(SOFTWARE_RESET_REGISTER, 0x0)
+ FIELD(SOFTWARE_RESET_REGISTER, CEN, 1, 1)
+ FIELD(SOFTWARE_RESET_REGISTER, SRST, 0, 1)
+REG32(MODE_SELECT_REGISTER, 0x4)
+ FIELD(MODE_SELECT_REGISTER, SNOOP, 2, 1)
+ FIELD(MODE_SELECT_REGISTER, LBACK, 1, 1)
+ FIELD(MODE_SELECT_REGISTER, SLEEP, 0, 1)
+REG32(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, 0x8)
+ FIELD(ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER, BRP, 0, 8)
+REG32(ARBITRATION_PHASE_BIT_TIMING_REGISTER, 0xc)
+ FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, SJW, 7, 2)
+ FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS2, 4, 3)
+ FIELD(ARBITRATION_PHASE_BIT_TIMING_REGISTER, TS1, 0, 4)
+REG32(ERROR_COUNTER_REGISTER, 0x10)
+ FIELD(ERROR_COUNTER_REGISTER, REC, 8, 8)
+ FIELD(ERROR_COUNTER_REGISTER, TEC, 0, 8)
+REG32(ERROR_STATUS_REGISTER, 0x14)
+ FIELD(ERROR_STATUS_REGISTER, ACKER, 4, 1)
+ FIELD(ERROR_STATUS_REGISTER, BERR, 3, 1)
+ FIELD(ERROR_STATUS_REGISTER, STER, 2, 1)
+ FIELD(ERROR_STATUS_REGISTER, FMER, 1, 1)
+ FIELD(ERROR_STATUS_REGISTER, CRCER, 0, 1)
+REG32(STATUS_REGISTER, 0x18)
+ FIELD(STATUS_REGISTER, SNOOP, 12, 1)
+ FIELD(STATUS_REGISTER, ACFBSY, 11, 1)
+ FIELD(STATUS_REGISTER, TXFLL, 10, 1)
+ FIELD(STATUS_REGISTER, TXBFLL, 9, 1)
+ FIELD(STATUS_REGISTER, ESTAT, 7, 2)
+ FIELD(STATUS_REGISTER, ERRWRN, 6, 1)
+ FIELD(STATUS_REGISTER, BBSY, 5, 1)
+ FIELD(STATUS_REGISTER, BIDLE, 4, 1)
+ FIELD(STATUS_REGISTER, NORMAL, 3, 1)
+ FIELD(STATUS_REGISTER, SLEEP, 2, 1)
+ FIELD(STATUS_REGISTER, LBACK, 1, 1)
+ FIELD(STATUS_REGISTER, CONFIG, 0, 1)
+REG32(INTERRUPT_STATUS_REGISTER, 0x1c)
+ FIELD(INTERRUPT_STATUS_REGISTER, TXFEMP, 14, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, TXFWMEMP, 13, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, RXFWMFLL, 12, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, WKUP, 11, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, SLP, 10, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, BSOFF, 9, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, ERROR, 8, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, RXNEMP, 7, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, RXOFLW, 6, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, RXUFLW, 5, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, RXOK, 4, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, TXBFLL, 3, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, TXFLL, 2, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, TXOK, 1, 1)
+ FIELD(INTERRUPT_STATUS_REGISTER, ARBLST, 0, 1)
+REG32(INTERRUPT_ENABLE_REGISTER, 0x20)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ETXFEMP, 14, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ETXFWMEMP, 13, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ERXFWMFLL, 12, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, EWKUP, 11, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ESLP, 10, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, EBSOFF, 9, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, EERROR, 8, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ERXNEMP, 7, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ERXOFLW, 6, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ERXUFLW, 5, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ERXOK, 4, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ETXBFLL, 3, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ETXFLL, 2, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, ETXOK, 1, 1)
+ FIELD(INTERRUPT_ENABLE_REGISTER, EARBLST, 0, 1)
+REG32(INTERRUPT_CLEAR_REGISTER, 0x24)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CTXFEMP, 14, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CTXFWMEMP, 13, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CRXFWMFLL, 12, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CWKUP, 11, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CSLP, 10, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CBSOFF, 9, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CERROR, 8, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CRXNEMP, 7, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CRXOFLW, 6, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CRXUFLW, 5, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CRXOK, 4, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CTXBFLL, 3, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CTXFLL, 2, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CTXOK, 1, 1)
+ FIELD(INTERRUPT_CLEAR_REGISTER, CARBLST, 0, 1)
+REG32(TIMESTAMP_REGISTER, 0x28)
+ FIELD(TIMESTAMP_REGISTER, CTS, 0, 1)
+REG32(WIR, 0x2c)
+ FIELD(WIR, EW, 8, 8)
+ FIELD(WIR, FW, 0, 8)
+REG32(TXFIFO_ID, 0x30)
+ FIELD(TXFIFO_ID, IDH, 21, 11)
+ FIELD(TXFIFO_ID, SRRRTR, 20, 1)
+ FIELD(TXFIFO_ID, IDE, 19, 1)
+ FIELD(TXFIFO_ID, IDL, 1, 18)
+ FIELD(TXFIFO_ID, RTR, 0, 1)
+REG32(TXFIFO_DLC, 0x34)
+ FIELD(TXFIFO_DLC, DLC, 28, 4)
+REG32(TXFIFO_DATA1, 0x38)
+ FIELD(TXFIFO_DATA1, DB0, 24, 8)
+ FIELD(TXFIFO_DATA1, DB1, 16, 8)
+ FIELD(TXFIFO_DATA1, DB2, 8, 8)
+ FIELD(TXFIFO_DATA1, DB3, 0, 8)
+REG32(TXFIFO_DATA2, 0x3c)
+ FIELD(TXFIFO_DATA2, DB4, 24, 8)
+ FIELD(TXFIFO_DATA2, DB5, 16, 8)
+ FIELD(TXFIFO_DATA2, DB6, 8, 8)
+ FIELD(TXFIFO_DATA2, DB7, 0, 8)
+REG32(TXHPB_ID, 0x40)
+ FIELD(TXHPB_ID, IDH, 21, 11)
+ FIELD(TXHPB_ID, SRRRTR, 20, 1)
+ FIELD(TXHPB_ID, IDE, 19, 1)
+ FIELD(TXHPB_ID, IDL, 1, 18)
+ FIELD(TXHPB_ID, RTR, 0, 1)
+REG32(TXHPB_DLC, 0x44)
+ FIELD(TXHPB_DLC, DLC, 28, 4)
+REG32(TXHPB_DATA1, 0x48)
+ FIELD(TXHPB_DATA1, DB0, 24, 8)
+ FIELD(TXHPB_DATA1, DB1, 16, 8)
+ FIELD(TXHPB_DATA1, DB2, 8, 8)
+ FIELD(TXHPB_DATA1, DB3, 0, 8)
+REG32(TXHPB_DATA2, 0x4c)
+ FIELD(TXHPB_DATA2, DB4, 24, 8)
+ FIELD(TXHPB_DATA2, DB5, 16, 8)
+ FIELD(TXHPB_DATA2, DB6, 8, 8)
+ FIELD(TXHPB_DATA2, DB7, 0, 8)
+REG32(RXFIFO_ID, 0x50)
+ FIELD(RXFIFO_ID, IDH, 21, 11)
+ FIELD(RXFIFO_ID, SRRRTR, 20, 1)
+ FIELD(RXFIFO_ID, IDE, 19, 1)
+ FIELD(RXFIFO_ID, IDL, 1, 18)
+ FIELD(RXFIFO_ID, RTR, 0, 1)
+REG32(RXFIFO_DLC, 0x54)
+ FIELD(RXFIFO_DLC, DLC, 28, 4)
+ FIELD(RXFIFO_DLC, RXT, 0, 16)
+REG32(RXFIFO_DATA1, 0x58)
+ FIELD(RXFIFO_DATA1, DB0, 24, 8)
+ FIELD(RXFIFO_DATA1, DB1, 16, 8)
+ FIELD(RXFIFO_DATA1, DB2, 8, 8)
+ FIELD(RXFIFO_DATA1, DB3, 0, 8)
+REG32(RXFIFO_DATA2, 0x5c)
+ FIELD(RXFIFO_DATA2, DB4, 24, 8)
+ FIELD(RXFIFO_DATA2, DB5, 16, 8)
+ FIELD(RXFIFO_DATA2, DB6, 8, 8)
+ FIELD(RXFIFO_DATA2, DB7, 0, 8)
+REG32(AFR, 0x60)
+ FIELD(AFR, UAF4, 3, 1)
+ FIELD(AFR, UAF3, 2, 1)
+ FIELD(AFR, UAF2, 1, 1)
+ FIELD(AFR, UAF1, 0, 1)
+REG32(AFMR1, 0x64)
+ FIELD(AFMR1, AMIDH, 21, 11)
+ FIELD(AFMR1, AMSRR, 20, 1)
+ FIELD(AFMR1, AMIDE, 19, 1)
+ FIELD(AFMR1, AMIDL, 1, 18)
+ FIELD(AFMR1, AMRTR, 0, 1)
+REG32(AFIR1, 0x68)
+ FIELD(AFIR1, AIIDH, 21, 11)
+ FIELD(AFIR1, AISRR, 20, 1)
+ FIELD(AFIR1, AIIDE, 19, 1)
+ FIELD(AFIR1, AIIDL, 1, 18)
+ FIELD(AFIR1, AIRTR, 0, 1)
+REG32(AFMR2, 0x6c)
+ FIELD(AFMR2, AMIDH, 21, 11)
+ FIELD(AFMR2, AMSRR, 20, 1)
+ FIELD(AFMR2, AMIDE, 19, 1)
+ FIELD(AFMR2, AMIDL, 1, 18)
+ FIELD(AFMR2, AMRTR, 0, 1)
+REG32(AFIR2, 0x70)
+ FIELD(AFIR2, AIIDH, 21, 11)
+ FIELD(AFIR2, AISRR, 20, 1)
+ FIELD(AFIR2, AIIDE, 19, 1)
+ FIELD(AFIR2, AIIDL, 1, 18)
+ FIELD(AFIR2, AIRTR, 0, 1)
+REG32(AFMR3, 0x74)
+ FIELD(AFMR3, AMIDH, 21, 11)
+ FIELD(AFMR3, AMSRR, 20, 1)
+ FIELD(AFMR3, AMIDE, 19, 1)
+ FIELD(AFMR3, AMIDL, 1, 18)
+ FIELD(AFMR3, AMRTR, 0, 1)
+REG32(AFIR3, 0x78)
+ FIELD(AFIR3, AIIDH, 21, 11)
+ FIELD(AFIR3, AISRR, 20, 1)
+ FIELD(AFIR3, AIIDE, 19, 1)
+ FIELD(AFIR3, AIIDL, 1, 18)
+ FIELD(AFIR3, AIRTR, 0, 1)
+REG32(AFMR4, 0x7c)
+ FIELD(AFMR4, AMIDH, 21, 11)
+ FIELD(AFMR4, AMSRR, 20, 1)
+ FIELD(AFMR4, AMIDE, 19, 1)
+ FIELD(AFMR4, AMIDL, 1, 18)
+ FIELD(AFMR4, AMRTR, 0, 1)
+REG32(AFIR4, 0x80)
+ FIELD(AFIR4, AIIDH, 21, 11)
+ FIELD(AFIR4, AISRR, 20, 1)
+ FIELD(AFIR4, AIIDE, 19, 1)
+ FIELD(AFIR4, AIIDL, 1, 18)
+ FIELD(AFIR4, AIRTR, 0, 1)
+
+static void can_update_irq(XlnxZynqMPCANState *s)
+{
+ uint32_t irq;
+
+ /* Watermark register interrupts. */
+ if ((fifo32_num_free(&s->tx_fifo) / CAN_FRAME_SIZE) >
+ ARRAY_FIELD_EX32(s->regs, WIR, EW)) {
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXFWMEMP, 1);
+ }
+
+ if ((fifo32_num_used(&s->rx_fifo) / CAN_FRAME_SIZE) >
+ ARRAY_FIELD_EX32(s->regs, WIR, FW)) {
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXFWMFLL, 1);
+ }
+
+ /* RX Interrupts. */
+ if (fifo32_num_used(&s->rx_fifo) >= 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];
+
+ trace_xlnx_can_update_irq(s->regs[R_INTERRUPT_STATUS_REGISTER],
+ s->regs[R_INTERRUPT_ENABLE_REGISTER], irq);
+ qemu_set_irq(s->irq, irq);
+}
+
+static void can_ier_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ can_update_irq(s);
+}
+
+static uint64_t can_icr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ 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 XlnxZynqMPCAN in configuration mode. */
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 1);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, BSOFF, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ERROR, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 0);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, ARBLST, 0);
+
+ can_update_irq(s);
+}
+
+static void update_status_register_mode_bits(XlnxZynqMPCANState *s)
+{
+ bool sleep_status = ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP);
+ bool sleep_mode = ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP);
+ /* Wake up interrupt bit. */
+ bool wakeup_irq_val = sleep_status && (sleep_mode == 0);
+ /* Sleep interrupt bit. */
+ bool sleep_irq_val = sleep_mode && (sleep_status == 0);
+
+ /* Clear previous core mode status bits. */
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 0);
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 0);
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 0);
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 0);
+
+ /* set current mode bit and generate irqs accordingly. */
+ if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, LBACK)) {
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, LBACK, 1);
+ } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SLEEP)) {
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SLEEP, 1);
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, SLP,
+ sleep_irq_val);
+ } else if (ARRAY_FIELD_EX32(s->regs, MODE_SELECT_REGISTER, SNOOP)) {
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, SNOOP, 1);
+ } else {
+ /*
+ * If all bits are zero then XlnxZynqMPCAN is set in normal mode.
+ */
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, NORMAL, 1);
+ /* Set wakeup interrupt bit. */
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, WKUP,
+ wakeup_irq_val);
+ }
+
+ can_update_irq(s);
+}
+
+static void can_exit_sleep_mode(XlnxZynqMPCANState *s)
+{
+ ARRAY_FIELD_DP32(s->regs, MODE_SELECT_REGISTER, SLEEP, 0);
+ update_status_register_mode_bits(s);
+}
+
+static void generate_frame(qemu_can_frame *frame, uint32_t *data)
+{
+ frame->can_id = 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)) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer data while"
+ " data while controller is in reset mode.\n",
+ path);
+ return false;
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
+ " data while controller is in configuration mode. Reset"
+ " the core so operations can start fresh.\n",
+ path);
+ return false;
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to transfer"
+ " data while controller is in SNOOP MODE.\n",
+ path);
+ return false;
+ }
+
+ return true;
+}
+
+static void transfer_fifo(XlnxZynqMPCANState *s, Fifo32 *fifo)
+{
+ qemu_can_frame frame;
+ uint32_t data[CAN_FRAME_SIZE];
+ int i;
+ bool can_tx = tx_ready_check(s);
+
+ if (!can_tx) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is not enabled for data"
+ " transfer.\n", path);
+ can_update_irq(s);
+ return;
+ }
+
+ while (!fifo32_is_empty(fifo)) {
+ for (i = 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 XlnxZynqMPCAN Bus.
+ * Any message transmitted is looped back to the RX line and
+ * acknowledged. The XlnxZynqMPCAN core receives any message
+ * that it transmits.
+ */
+ if (fifo32_is_full(&s->rx_fifo)) {
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, 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);
+
+ trace_xlnx_can_tx_data(frame.can_id, frame.can_dlc,
+ frame.data[0], frame.data[1],
+ frame.data[2], frame.data[3],
+ frame.data[4], frame.data[5],
+ frame.data[6], frame.data[7]);
+ can_bus_client_send(&s->bus_client, &frame, 1);
+ }
+ }
+
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, TXOK, 1);
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, TXBFLL, 0);
+
+ if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) {
+ can_exit_sleep_mode(s);
+ }
+
+ can_update_irq(s);
+}
+
+static uint64_t can_srr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ ARRAY_FIELD_DP32(s->regs, SOFTWARE_RESET_REGISTER, CEN,
+ FIELD_EX32(val, SOFTWARE_RESET_REGISTER, CEN));
+
+ if (FIELD_EX32(val, SOFTWARE_RESET_REGISTER, SRST)) {
+ trace_xlnx_can_reset(val);
+
+ /* First, core will do software reset then will enter in config mode. */
+ can_config_reset(s);
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN) == 0) {
+ can_config_mode(s);
+ } else {
+ /*
+ * Leave config mode. Now XlnxZynqMPCAN core will enter normal,
+ * sleep, snoop or loopback mode depending upon LBACK, SLEEP, SNOOP
+ * register states.
+ */
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, CONFIG, 0);
+
+ ptimer_transaction_begin(s->can_timer);
+ ptimer_set_count(s->can_timer, 0);
+ ptimer_transaction_commit(s->can_timer);
+
+ /* XlnxZynqMPCAN is out of config mode. It will send pending data. */
+ transfer_fifo(s, &s->txhpb_fifo);
+ transfer_fifo(s, &s->tx_fifo);
+ }
+
+ update_status_register_mode_bits(s);
+
+ return s->regs[R_SOFTWARE_RESET_REGISTER];
+}
+
+static uint64_t can_msr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+ uint8_t multi_mode;
+
+ /*
+ * Multiple mode set check. This is done to make sure user doesn't set
+ * multiple modes.
+ */
+ multi_mode = FIELD_EX32(val, MODE_SELECT_REGISTER, LBACK) +
+ FIELD_EX32(val, MODE_SELECT_REGISTER, SLEEP) +
+ FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP);
+
+ if (multi_mode > 1) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to config"
+ " several modes simultaneously. One mode will be selected"
+ " according to their priority: LBACK > SLEEP > SNOOP.\n",
+ path);
+ }
+
+ 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)) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
+ " LBACK mode without setting CEN bit as 0.\n",
+ path);
+ } else if (FIELD_EX32(val, MODE_SELECT_REGISTER, SNOOP)) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Attempting to set"
+ " SNOOP mode without setting CEN bit as 0.\n",
+ path);
+ }
+
+ update_status_register_mode_bits(s);
+ }
+
+ return s->regs[R_MODE_SELECT_REGISTER];
+}
+
+static uint64_t can_brpr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ /* Only allow writes when in config mode. */
+ if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+ return s->regs[R_ARBITRATION_PHASE_BAUD_RATE_PRESCALER_REGISTER];
+ }
+
+ return val;
+}
+
+static uint64_t can_btr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ /* Only allow writes when in config mode. */
+ if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+ return s->regs[R_ARBITRATION_PHASE_BIT_TIMING_REGISTER];
+ }
+
+ return val;
+}
+
+static uint64_t can_tcr_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ if (FIELD_EX32(val, TIMESTAMP_REGISTER, CTS)) {
+ ptimer_transaction_begin(s->can_timer);
+ ptimer_set_count(s->can_timer, 0);
+ ptimer_transaction_commit(s->can_timer);
+ }
+
+ return 0;
+}
+
+static void update_rx_fifo(XlnxZynqMPCANState *s, const qemu_can_frame *frame)
+{
+ bool filter_pass = false;
+ uint16_t timestamp = 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 = true;
+ }
+
+ /*
+ * Messages that pass any of the acceptance filters will be stored in
+ * the RX FIFO.
+ */
+ if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1)) {
+ uint32_t id_masked = 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 = true;
+ }
+ }
+
+ 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 = true;
+ }
+ }
+
+ 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 = true;
+ }
+ }
+
+ 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 = true;
+ }
+ }
+
+ if (!filter_pass) {
+ trace_xlnx_can_rx_fifo_filter_reject(frame->can_id, frame->can_dlc);
+ return;
+ }
+
+ /* Store the message in fifo if it passed through any of the filters. */
+ if (filter_pass && frame->can_dlc <= MAX_DLC) {
+
+ if (fifo32_is_full(&s->rx_fifo)) {
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOFLW, 1);
+ } else {
+ timestamp = CAN_TIMER_MAX - ptimer_get_count(s->can_timer);
+
+ fifo32_push(&s->rx_fifo, frame->can_id);
+
+ fifo32_push(&s->rx_fifo, deposit32(0, R_RXFIFO_DLC_DLC_SHIFT,
+ R_RXFIFO_DLC_DLC_LENGTH,
+ frame->can_dlc) |
+ deposit32(0, R_RXFIFO_DLC_RXT_SHIFT,
+ R_RXFIFO_DLC_RXT_LENGTH,
+ timestamp));
+
+ /* First 32 bit of the data. */
+ fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA1_DB3_SHIFT,
+ R_TXFIFO_DATA1_DB3_LENGTH,
+ frame->data[0]) |
+ deposit32(0, R_TXFIFO_DATA1_DB2_SHIFT,
+ R_TXFIFO_DATA1_DB2_LENGTH,
+ frame->data[1]) |
+ deposit32(0, R_TXFIFO_DATA1_DB1_SHIFT,
+ R_TXFIFO_DATA1_DB1_LENGTH,
+ frame->data[2]) |
+ deposit32(0, R_TXFIFO_DATA1_DB0_SHIFT,
+ R_TXFIFO_DATA1_DB0_LENGTH,
+ frame->data[3]));
+ /* Last 32 bit of the data. */
+ fifo32_push(&s->rx_fifo, deposit32(0, R_TXFIFO_DATA2_DB7_SHIFT,
+ R_TXFIFO_DATA2_DB7_LENGTH,
+ frame->data[4]) |
+ deposit32(0, R_TXFIFO_DATA2_DB6_SHIFT,
+ R_TXFIFO_DATA2_DB6_LENGTH,
+ frame->data[5]) |
+ deposit32(0, R_TXFIFO_DATA2_DB5_SHIFT,
+ R_TXFIFO_DATA2_DB5_LENGTH,
+ frame->data[6]) |
+ deposit32(0, R_TXFIFO_DATA2_DB4_SHIFT,
+ R_TXFIFO_DATA2_DB4_LENGTH,
+ frame->data[7]));
+
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXOK, 1);
+ trace_xlnx_can_rx_data(frame->can_id, frame->can_dlc,
+ frame->data[0], frame->data[1],
+ frame->data[2], frame->data[3],
+ frame->data[4], frame->data[5],
+ frame->data[6], frame->data[7]);
+ }
+
+ can_update_irq(s);
+ }
+}
+
+static uint64_t can_rxfifo_pre_read(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ if (!fifo32_is_empty(&s->rx_fifo)) {
+ val = fifo32_pop(&s->rx_fifo);
+ } else {
+ ARRAY_FIELD_DP32(s->regs, INTERRUPT_STATUS_REGISTER, RXUFLW, 1);
+ }
+
+ can_update_irq(s);
+ return val;
+}
+
+static void can_filter_enable_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ if (ARRAY_FIELD_EX32(s->regs, AFR, UAF1) &&
+ ARRAY_FIELD_EX32(s->regs, AFR, UAF2) &&
+ ARRAY_FIELD_EX32(s->regs, AFR, UAF3) &&
+ ARRAY_FIELD_EX32(s->regs, AFR, UAF4)) {
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 1);
+ } else {
+ ARRAY_FIELD_DP32(s->regs, STATUS_REGISTER, ACFBSY, 0);
+ }
+}
+
+static uint64_t can_filter_mask_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+ uint32_t reg_idx = (reg->access->addr) / 4;
+ 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;
+
+ trace_xlnx_can_filter_mask_pre_write(filter_number, s->regs[reg_idx]);
+ } else {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
+ " mask is not set as corresponding UAF bit is not 0.\n",
+ path, filter_number + 1);
+ }
+
+ return s->regs[reg_idx];
+}
+
+static uint64_t can_filter_id_pre_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+ uint32_t reg_idx = (reg->access->addr) / 4;
+ uint32_t filter_number = (reg_idx - R_AFIR1) / 2;
+
+ if (!(s->regs[R_AFR] & (1 << filter_number))) {
+ s->regs[reg_idx] = val;
+
+ trace_xlnx_can_filter_id_pre_write(filter_number, s->regs[reg_idx]);
+ } else {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Acceptance filter %d"
+ " id is not set as corresponding UAF bit is not 0.\n",
+ path, filter_number + 1);
+ }
+
+ return s->regs[reg_idx];
+}
+
+static void can_tx_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(reg->opaque);
+
+ 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;
+
+ if (!fifo32_is_full(f)) {
+ fifo32_push(f, val);
+ } else {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: TX FIFO is full.\n", path);
+ }
+
+ /* Initiate the message send if TX register is written. */
+ if (initiate_transfer &&
+ ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) {
+ transfer_fifo(s, f);
+ }
+
+ can_update_irq(s);
+}
+
+static const RegisterAccessInfo can_regs_info[] = {
+ { .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 void xlnx_zynqmp_can_ptimer_cb(void *opaque)
+{
+ /* No action required on the timer rollover. */
+}
+
+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_init(Object *obj, ResetType type)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj);
+ unsigned int i;
+
+ for (i = R_RXFIFO_ID; i < ARRAY_SIZE(s->reg_info); ++i) {
+ register_reset(&s->reg_info[i]);
+ }
+
+ ptimer_transaction_begin(s->can_timer);
+ ptimer_set_count(s->can_timer, 0);
+ ptimer_transaction_commit(s->can_timer);
+}
+
+static void xlnx_zynqmp_can_reset_hold(Object *obj)
+{
+ XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj);
+ unsigned int i;
+
+ for (i = 0; i < R_RXFIFO_ID; ++i) {
+ register_reset(&s->reg_info[i]);
+ }
+
+ /*
+ * Reset FIFOs when CAN model is reset. This will clear the fifo writes
+ * done by post_write which gets called from register_reset function,
+ * post_write handle will not be able to trigger tx because CAN will be
+ * disabled when software_reset_register is cleared first.
+ */
+ fifo32_reset(&s->rx_fifo);
+ fifo32_reset(&s->tx_fifo);
+ fifo32_reset(&s->txhpb_fifo);
+}
+
+static bool xlnx_zynqmp_can_can_receive(CanBusClientState *client)
+{
+ XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
+ bus_client);
+
+ if (ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, SRST)) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is in reset state.\n",
+ path);
+ return false;
+ }
+
+ if ((ARRAY_FIELD_EX32(s->regs, SOFTWARE_RESET_REGISTER, CEN)) == 0) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Controller is disabled. Incoming"
+ " messages will be discarded.\n", path);
+ return false;
+ }
+
+ return true;
+}
+
+static ssize_t xlnx_zynqmp_can_receive(CanBusClientState *client,
+ const qemu_can_frame *buf, size_t buf_size) {
+ XlnxZynqMPCANState *s = container_of(client, XlnxZynqMPCANState,
+ bus_client);
+ const qemu_can_frame *frame = buf;
+
+ if (buf_size <= 0) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Error in the data received.\n",
+ path);
+ return 0;
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SNOOP)) {
+ /* Snoop Mode: Just keep the data. no response back. */
+ update_rx_fifo(s, frame);
+ } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP))) {
+ /*
+ * XlnxZynqMPCAN is in sleep mode. Any data on bus will bring it to wake
+ * up state.
+ */
+ can_exit_sleep_mode(s);
+ update_rx_fifo(s, frame);
+ } else if ((ARRAY_FIELD_EX32(s->regs, STATUS_REGISTER, SLEEP)) == 0) {
+ update_rx_fifo(s, frame);
+ } else {
+ /*
+ * XlnxZynqMPCAN will not participate in normal bus communication
+ * and will not receive any messages transmitted by other CAN nodes.
+ */
+ trace_xlnx_can_rx_discard(s->regs[R_STATUS_REGISTER]);
+ }
+
+ 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->canbus) {
+ if (xlnx_zynqmp_can_connect_to_bus(s, s->canbus) < 0) {
+ g_autofree char *path = object_get_canonical_path(OBJECT(s));
+
+ error_setg(errp, "%s: xlnx_zynqmp_can_connect_to_bus"
+ " failed.", path);
+ return;
+ }
+ }
+
+ /* Create RX FIFO, TXFIFO, TXHPB storage. */
+ fifo32_create(&s->rx_fifo, RXFIFO_SIZE);
+ fifo32_create(&s->tx_fifo, RXFIFO_SIZE);
+ fifo32_create(&s->txhpb_fifo, CAN_FRAME_SIZE);
+
+ /* Allocate a new timer. */
+ s->can_timer = ptimer_init(xlnx_zynqmp_can_ptimer_cb, s,
+ PTIMER_POLICY_DEFAULT);
+
+ ptimer_transaction_begin(s->can_timer);
+
+ ptimer_set_freq(s->can_timer, s->cfg.ext_clk_freq);
+ ptimer_set_limit(s->can_timer, CAN_TIMER_MAX, 1);
+ ptimer_run(s->can_timer, 0);
+ ptimer_transaction_commit(s->can_timer);
+}
+
+static void xlnx_zynqmp_can_init(Object *obj)
+{
+ XlnxZynqMPCANState *s = 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);
+}
+
+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_FIFO32(tx_fifo, XlnxZynqMPCANState),
+ VMSTATE_FIFO32(txhpb_fifo, XlnxZynqMPCANState),
+ VMSTATE_UINT32_ARRAY(regs, XlnxZynqMPCANState, XLNX_ZYNQMP_CAN_R_MAX),
+ VMSTATE_PTIMER(can_timer, XlnxZynqMPCANState),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static Property xlnx_zynqmp_can_properties[] = {
+ DEFINE_PROP_UINT32("ext_clk_freq", XlnxZynqMPCANState, cfg.ext_clk_freq,
+ CAN_DEFAULT_CLOCK),
+ DEFINE_PROP_LINK("canbus", XlnxZynqMPCANState, canbus, TYPE_CAN_BUS,
+ CanBusState *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.enter = xlnx_zynqmp_can_reset_init;
+ rc->phases.hold = xlnx_zynqmp_can_reset_hold;
+ 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)