summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/bcm2835_peripherals.c23
-rw-r--r--hw/arm/exynos4210.c14
-rw-r--r--hw/arm/fsl-imx6.c7
-rw-r--r--hw/arm/xilinx_zynq.c53
-rw-r--r--hw/arm/xlnx-zynqmp.c30
-rw-r--r--hw/display/cg3.c1
-rw-r--r--hw/display/exynos4210_fimd.c1
-rw-r--r--hw/display/framebuffer.c1
-rw-r--r--hw/display/g364fb.c11
-rw-r--r--hw/display/sm501.c1
-rw-r--r--hw/display/tcx.c2
-rw-r--r--hw/display/vga.c6
-rw-r--r--hw/net/Makefile.objs2
-rw-r--r--hw/net/can/Makefile.objs4
-rw-r--r--hw/net/can/can_kvaser_pci.c319
-rw-r--r--hw/net/can/can_mioe3680_pci.c262
-rw-r--r--hw/net/can/can_pcm3680_pci.c263
-rw-r--r--hw/net/can/can_sja1000.c953
-rw-r--r--hw/net/can/can_sja1000.h146
-rw-r--r--hw/sd/core.c61
-rw-r--r--hw/sd/sd.c29
-rw-r--r--hw/sd/sdhci-internal.h71
-rw-r--r--hw/sd/sdhci.c397
-rw-r--r--hw/sd/trace-events9
24 files changed, 2481 insertions, 185 deletions
diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c
index 12e0dd11af..13b63970d7 100644
--- a/hw/arm/bcm2835_peripherals.c
+++ b/hw/arm/bcm2835_peripherals.c
@@ -19,7 +19,7 @@
 #define BCM2835_VC_PERI_BASE 0x7e000000
 
 /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
-#define BCM2835_SDHC_CAPAREG 0x52034b4
+#define BCM2835_SDHC_CAPAREG 0x52134b4
 
 static void bcm2835_peripherals_init(Object *obj)
 {
@@ -254,14 +254,19 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
     memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
 
-    /* Extended Mass Media Controller */
-    object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
-                            &err);
-    if (err) {
-        error_propagate(errp, err);
-        return;
-    }
-
+    /* Extended Mass Media Controller
+     *
+     * Compatible with:
+     * - SD Host Controller Specification Version 3.0 Draft 1.0
+     * - SDIO Specification Version 3.0
+     * - MMC Specification Version 4.4
+     *
+     * For the exact details please refer to the Arasan documentation:
+     *   SD3.0_Host_AHB_eMMC4.4_Usersguide_ver5.9_jan11_10.pdf
+     */
+    object_property_set_uint(OBJECT(&s->sdhci), 3, "sd-spec-version", &err);
+    object_property_set_uint(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
+                             &err);
     object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
                              &err);
     if (err) {
diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c
index e8e1d81e62..06f9d1ffa4 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -377,8 +377,20 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem)
         BlockBackend *blk;
         DriveInfo *di;
 
+        /* Compatible with:
+         * - SD Host Controller Specification Version 2.0
+         * - SDIO Specification Version 2.0
+         * - MMC Specification Version 4.3
+         * - SDMA
+         * - ADMA2
+         *
+         * As this part of the Exynos4210 is not publically available,
+         * we used the "HS-MMC Controller S3C2416X RISC Microprocessor"
+         * public datasheet which is very similar (implementing
+         * MMC Specification Version 4.0 being the only difference noted)
+         */
         dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
-        qdev_prop_set_uint32(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
+        qdev_prop_set_uint64(dev, "capareg", EXYNOS4210_SDHCI_CAPABILITIES);
         qdev_init_nofail(dev);
 
         busdev = SYS_BUS_DEVICE(dev);
diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c
index e6559a8b12..b6ac72de27 100644
--- a/hw/arm/fsl-imx6.c
+++ b/hw/arm/fsl-imx6.c
@@ -27,6 +27,8 @@
 #include "chardev/char.h"
 #include "qemu/error-report.h"
 
+#define IMX6_ESDHC_CAPABILITIES     0x057834b4
+
 #define NAME_SIZE 20
 
 static void fsl_imx6_init(Object *obj)
@@ -348,6 +350,11 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
             { FSL_IMX6_uSDHC4_ADDR, FSL_IMX6_uSDHC4_IRQ },
         };
 
+        /* UHS-I SDIO3.0 SDR104 1.8V ADMA */
+        object_property_set_uint(OBJECT(&s->esdhc[i]), 3, "sd-spec-version",
+                                 &err);
+        object_property_set_uint(OBJECT(&s->esdhc[i]), IMX6_ESDHC_CAPABILITIES,
+                                 "capareg", &err);
         object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err);
         if (err) {
             error_propagate(errp, err);
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 1836a4ed45..0f76333770 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -61,6 +61,8 @@ static const int dma_irqs[8] = {
 #define SLCR_XILINX_UNLOCK_KEY  0xdf0d
 #define SLCR_XILINX_LOCK_KEY    0x767b
 
+#define ZYNQ_SDHCI_CAPABILITIES 0x69ec0080  /* Datasheet: UG585 (v1.12.1) */
+
 #define ARMV7_IMM16(x) (extract32((x),  0, 12) | \
                         extract32((x), 12,  4) << 16)
 
@@ -165,10 +167,8 @@ static void zynq_init(MachineState *machine)
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ext_ram = g_new(MemoryRegion, 1);
     MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
-    DeviceState *dev, *carddev;
+    DeviceState *dev;
     SysBusDevice *busdev;
-    DriveInfo *di;
-    BlockBackend *blk;
     qemu_irq pic[64];
     int n;
 
@@ -247,27 +247,32 @@ static void zynq_init(MachineState *machine)
     gem_init(&nd_table[0], 0xE000B000, pic[54-IRQ_OFFSET]);
     gem_init(&nd_table[1], 0xE000C000, pic[77-IRQ_OFFSET]);
 
-    dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]);
-
-    di = drive_get_next(IF_SD);
-    blk = di ? blk_by_legacy_dinfo(di) : NULL;
-    carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
-    qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
-    object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
-
-    dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
-    qdev_init_nofail(dev);
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000);
-    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]);
-
-    di = drive_get_next(IF_SD);
-    blk = di ? blk_by_legacy_dinfo(di) : NULL;
-    carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
-    qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
-    object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
+    for (n = 0; n < 2; n++) {
+        int hci_irq = n ? 79 : 56;
+        hwaddr hci_addr = n ? 0xE0101000 : 0xE0100000;
+        DriveInfo *di;
+        BlockBackend *blk;
+        DeviceState *carddev;
+
+        /* Compatible with:
+         * - SD Host Controller Specification Version 2.0 Part A2
+         * - SDIO Specification Version 2.0
+         * - MMC Specification Version 3.31
+         */
+        dev = qdev_create(NULL, TYPE_SYSBUS_SDHCI);
+        qdev_prop_set_uint8(dev, "sd-spec-version", 2);
+        qdev_prop_set_uint64(dev, "capareg", ZYNQ_SDHCI_CAPABILITIES);
+        qdev_init_nofail(dev);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
+
+        di = drive_get_next(IF_SD);
+        blk = di ? blk_by_legacy_dinfo(di) : NULL;
+        carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD);
+        qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
+        object_property_set_bool(OBJECT(carddev), true, "realized",
+                                 &error_fatal);
+    }
 
     dev = qdev_create(NULL, TYPE_ZYNQ_XADC);
     qdev_init_nofail(dev);
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index ca398c4159..4b93a3abd2 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -53,6 +53,8 @@
 #define IPI_ADDR            0xFF300000
 #define IPI_IRQ             64
 
+#define SDHCI_CAPABILITIES  0x280737ec6481 /* Datasheet: UG1085 (v1.7) */
+
 static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
     0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
 };
@@ -387,22 +389,28 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]);
 
     for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
-        char *bus_name;
-
-        object_property_set_bool(OBJECT(&s->sdhci[i]), true,
-                                 "realized", &err);
+        char *bus_name = g_strdup_printf("sd-bus%d", i);
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->sdhci[i]);
+        Object *sdhci = OBJECT(&s->sdhci[i]);
+
+        /* Compatible with:
+         * - SD Host Controller Specification Version 3.00
+         * - SDIO Specification Version 3.0
+         * - eMMC Specification Version 4.51
+         */
+        object_property_set_uint(sdhci, 3, "sd-spec-version", &err);
+        object_property_set_uint(sdhci, SDHCI_CAPABILITIES, "capareg", &err);
+        object_property_set_uint(sdhci, UHS_I, "uhs", &err);
+        object_property_set_bool(sdhci, true, "realized", &err);
         if (err) {
             error_propagate(errp, err);
             return;
         }
-        sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
-                        sdhci_addr[i]);
-        sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0,
-                           gic_spi[sdhci_intr[i]]);
+        sysbus_mmio_map(sbd, 0, sdhci_addr[i]);
+        sysbus_connect_irq(sbd, 0, gic_spi[sdhci_intr[i]]);
+
         /* Alias controller SD bus to the SoC itself */
-        bus_name = g_strdup_printf("sd-bus%d", i);
-        object_property_add_alias(OBJECT(s), bus_name,
-                                  OBJECT(&s->sdhci[i]), "sd-bus",
+        object_property_add_alias(OBJECT(s), bus_name, sdhci, "sd-bus",
                                   &error_abort);
         g_free(bus_name);
     }
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index cafd9f47ef..6fff4852c5 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -108,7 +108,6 @@ static void cg3_update_display(void *opaque)
     data = (uint32_t *)surface_data(surface);
 
     if (!s->full_update) {
-        memory_region_sync_dirty_bitmap(&s->vram_mem);
         snap = memory_region_snapshot_and_clear_dirty(&s->vram_mem, 0x0,
                                               memory_region_size(&s->vram_mem),
                                               DIRTY_MEMORY_VGA);
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 86e37e93e9..f011ea5b00 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1289,7 +1289,6 @@ static void exynos4210_fimd_update(void *opaque)
             scrn_width = w->virtpage_width;
             /* Total width of virtual screen page in bytes */
             inc_size = scrn_width + w->virtpage_offsize;
-            memory_region_sync_dirty_bitmap(w->mem_section.mr);
             host_fb_addr = w->host_fb_addr;
             fb_line_addr = w->mem_section.offset_within_region;
             snap = memory_region_snapshot_and_clear_dirty(w->mem_section.mr,
diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c
index d7310d25f2..36e3db189a 100644
--- a/hw/display/framebuffer.c
+++ b/hw/display/framebuffer.c
@@ -83,7 +83,6 @@ void framebuffer_update_display(
     if (!mem) {
         return;
     }
-    memory_region_sync_dirty_bitmap(mem);
 
     addr = mem_section->offset_within_region;
     src = memory_region_get_ram_ptr(mem) + addr;
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index 86557d14a9..819f8be05d 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -62,15 +62,15 @@ typedef struct G364State {
 
 #define G364_PAGE_SIZE 4096
 
-static inline int check_dirty(G364State *s, ram_addr_t page)
+static inline int check_dirty(G364State *s, DirtyBitmapSnapshot *snap, ram_addr_t page)
 {
-    return memory_region_test_and_clear_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
-                                              DIRTY_MEMORY_VGA);
+    return memory_region_snapshot_get_dirty(&s->mem_vram, snap, page, G364_PAGE_SIZE);
 }
 
 static void g364fb_draw_graphic8(G364State *s)
 {
     DisplaySurface *surface = qemu_console_surface(s->con);
+    DirtyBitmapSnapshot *snap;
     int i, w;
     uint8_t *vram;
     uint8_t *data_display, *dd;
@@ -122,8 +122,10 @@ static void g364fb_draw_graphic8(G364State *s)
     vram = s->vram + s->top_of_screen;
     /* XXX: out of range in vram? */
     data_display = dd = surface_data(surface);
+    snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0, s->vram_size,
+                                                  DIRTY_MEMORY_VGA);
     while (y < s->height) {
-        if (check_dirty(s, page)) {
+        if (check_dirty(s, snap, page)) {
             if (y < ymin)
                 ymin = ymax = y;
             if (x < xmin)
@@ -244,7 +246,6 @@ static void g364fb_update_display(void *opaque)
         qemu_console_resize(s->con, s->width, s->height);
     }
 
-    memory_region_sync_dirty_bitmap(&s->mem_vram);
     if (s->ctla & CTLA_FORCE_BLANK) {
         g364fb_draw_blank(s);
     } else if (s->depth == 8) {
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 134cbed607..f4bb33c279 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1508,7 +1508,6 @@ static void sm501_update_display(void *opaque)
     }
 
     /* draw each line according to conditions */
-    memory_region_sync_dirty_bitmap(&s->local_mem_region);
     snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region,
               offset, width * height * src_bpp, DIRTY_MEMORY_VGA);
     for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) {
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index daa93e0929..b2786ee8d0 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -236,7 +236,6 @@ static void tcx_update_display(void *opaque)
     dd = surface_stride(surface);
     ds = 1024;
 
-    memory_region_sync_dirty_bitmap(&ts->vram_mem);
     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
                                              memory_region_size(&ts->vram_mem),
                                              DIRTY_MEMORY_VGA);
@@ -292,7 +291,6 @@ static void tcx24_update_display(void *opaque)
     dd = surface_stride(surface);
     ds = 1024;
 
-    memory_region_sync_dirty_bitmap(&ts->vram_mem);
     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
                                              memory_region_size(&ts->vram_mem),
                                              DIRTY_MEMORY_VGA);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 6e78a4e156..28f298b342 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -1444,11 +1444,6 @@ static bool vga_scanline_invalidated(VGACommonState *s, int y)
     return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f));
 }
 
-void vga_sync_dirty_bitmap(VGACommonState *s)
-{
-    memory_region_sync_dirty_bitmap(&s->vram);
-}
-
 void vga_dirty_log_start(VGACommonState *s)
 {
     memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
@@ -1638,7 +1633,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
     y1 = 0;
 
     if (!full_update) {
-        vga_sync_dirty_bitmap(s);
         if (s->line_compare < height) {
             /* split screen mode */
             region_start = 0;
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 4171af0b5d..ab22968641 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -46,3 +46,5 @@ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
                                rocker/rocker_desc.o rocker/rocker_world.o \
                                rocker/rocker_of_dpa.o
 obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
+
+common-obj-$(CONFIG_CAN_BUS) += can/
diff --git a/hw/net/can/Makefile.objs b/hw/net/can/Makefile.objs
new file mode 100644
index 0000000000..9f0c4ee332
--- /dev/null
+++ b/hw/net/can/Makefile.objs
@@ -0,0 +1,4 @@
+common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
+common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
+common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
+common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o
diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c
new file mode 100644
index 0000000000..5f82f4359a
--- /dev/null
+++ b/hw/net/can/can_kvaser_pci.c
@@ -0,0 +1,319 @@
+/*
+ * 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/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "kvaser_pci"
+
+#define KVASER_PCI_DEV(obj) \
+    OBJECT_CHECK(KvaserPCIState, (obj), 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
+
+typedef 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;
+} KvaserPCIState;
+
+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, &error_abort);
+}
+
+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 0000000000..fd20b88955
--- /dev/null
+++ b/hw/net/can/can_mioe3680_pci.c
@@ -0,0 +1,262 @@
+/*
+ * 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/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "mioe3680_pci"
+
+#define MIOe3680_PCI_DEV(obj) \
+    OBJECT_CHECK(Mioe3680PCIState, (obj), 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
+
+typedef 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];
+} Mioe3680PCIState;
+
+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, &error_abort);
+    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+                             (Object **)&d->canbus[1],
+                             qdev_prop_allow_set_link_before_realize,
+                             0, &error_abort);
+}
+
+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 0000000000..23f7ff45a3
--- /dev/null
+++ b/hw/net/can/can_pcm3680_pci.c
@@ -0,0 +1,263 @@
+/*
+ * 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/thread.h"
+#include "qemu/sockets.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/can_emu.h"
+
+#include "can_sja1000.h"
+
+#define TYPE_CAN_PCI_DEV "pcm3680_pci"
+
+#define PCM3680i_PCI_DEV(obj) \
+    OBJECT_CHECK(Pcm3680iPCIState, (obj), 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
+
+typedef 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];
+} Pcm3680iPCIState;
+
+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, &error_abort);
+    object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
+                             (Object **)&d->canbus[1],
+                             qdev_prop_allow_set_link_before_realize,
+                             0, &error_abort);
+}
+
+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 0000000000..629323312c
--- /dev/null
+++ b/hw/net/can/can_sja1000.c
@@ -0,0 +1,953 @@
+/*
+ * 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/hw.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;
+
+    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();
+}
+
+static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame)
+{
+    uint8_t i;
+
+    frame->can_id = 0;
+    if (buff[0] & 0x40) { /* RTR */
+        frame->can_id = QEMU_CAN_RTR_FLAG;
+    }
+    frame->can_dlc = buff[0] & 0x0f;
+
+    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->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;
+
+    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;
+
+    if (frame->can_id & QEMU_CAN_ERR_FLAG) { /* error frame, NOT support now. */
+        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 < frame->can_dlc; i++) {
+            buff[5 + i] = frame->data[i];
+        }
+        return frame->can_dlc + 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 < frame->can_dlc; i++) {
+            buff[3 + i] = frame->data[i];
+        }
+
+        return frame->can_dlc + 3;
+    }
+
+    return -1;
+}
+
+static int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff)
+{
+    int i;
+
+     /*
+      * 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;
+    }
+
+    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 < frame->can_dlc; i++) {
+        buff[2 + i] = frame->data[i];
+    }
+
+    return frame->can_dlc + 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. */
+        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. */
+        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;
+}
+
+int 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 0;
+        }
+    } else { /* BasicCAN mode */
+        if (s->control & 0x01) {
+            return 0;
+        }
+    }
+
+    return 1; /* always return 1, 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 (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 (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 0000000000..4731cbbd2a
--- /dev/null
+++ b/hw/net/can/can_sja1000.h
@@ -0,0 +1,146 @@
+/*
+ * 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 "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);
+
+int 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/sd/core.c b/hw/sd/core.c
index 295dc44ab7..3c6eae6c88 100644
--- a/hw/sd/core.c
+++ b/hw/sd/core.c
@@ -23,6 +23,12 @@
 #include "hw/qdev-core.h"
 #include "sysemu/block-backend.h"
 #include "hw/sd/sd.h"
+#include "trace.h"
+
+static inline const char *sdbus_name(SDBus *sdbus)
+{
+    return sdbus->qbus.name;
+}
 
 static SDState *get_card(SDBus *sdbus)
 {
@@ -35,10 +41,58 @@ static SDState *get_card(SDBus *sdbus)
     return SD_CARD(kid->child);
 }
 
+uint8_t sdbus_get_dat_lines(SDBus *sdbus)
+{
+    SDState *slave = get_card(sdbus);
+    uint8_t dat_lines = 0b1111; /* 4 bit bus width */
+
+    if (slave) {
+        SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+
+        if (sc->get_dat_lines) {
+            dat_lines = sc->get_dat_lines(slave);
+        }
+    }
+    trace_sdbus_get_dat_lines(sdbus_name(sdbus), dat_lines);
+
+    return dat_lines;
+}
+
+bool sdbus_get_cmd_line(SDBus *sdbus)
+{
+    SDState *slave = get_card(sdbus);
+    bool cmd_line = true;
+
+    if (slave) {
+        SDCardClass *sc = SD_CARD_GET_CLASS(slave);
+
+        if (sc->get_cmd_line) {
+            cmd_line = sc->get_cmd_line(slave);
+        }
+    }
+    trace_sdbus_get_cmd_line(sdbus_name(sdbus), cmd_line);
+
+    return cmd_line;
+}
+
+void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts)
+{
+    SDState *card = get_card(sdbus);
+
+    trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts);
+    if (card) {
+        SDCardClass *sc = SD_CARD_GET_CLASS(card);
+
+        assert(sc->set_voltage);
+        sc->set_voltage(card, millivolts);
+    }
+}
+
 int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response)
 {
     SDState *card = get_card(sdbus);
 
+    trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg, req->crc);
     if (card) {
         SDCardClass *sc = SD_CARD_GET_CLASS(card);
 
@@ -52,6 +106,7 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
 {
     SDState *card = get_card(sdbus);
 
+    trace_sdbus_write(sdbus_name(sdbus), value);
     if (card) {
         SDCardClass *sc = SD_CARD_GET_CLASS(card);
 
@@ -62,14 +117,16 @@ void sdbus_write_data(SDBus *sdbus, uint8_t value)
 uint8_t sdbus_read_data(SDBus *sdbus)
 {
     SDState *card = get_card(sdbus);
+    uint8_t value = 0;
 
     if (card) {
         SDCardClass *sc = SD_CARD_GET_CLASS(card);
 
-        return sc->read_data(card);
+        value = sc->read_data(card);
     }
+    trace_sdbus_read(sdbus_name(sdbus), value);
 
-    return 0;
+    return value;
 }
 
 bool sdbus_data_ready(SDBus *sdbus)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 73e405a04f..9ac9b63ff8 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -126,8 +126,32 @@ struct SDState {
     BlockBackend *blk;
 
     bool enable;
+    uint8_t dat_lines;
+    bool cmd_line;
 };
 
+static uint8_t sd_get_dat_lines(SDState *sd)
+{
+    return sd->enable ? sd->dat_lines : 0;
+}
+
+static bool sd_get_cmd_line(SDState *sd)
+{
+    return sd->enable ? sd->cmd_line : false;
+}
+
+static void sd_set_voltage(SDState *sd, uint16_t millivolts)
+{
+    switch (millivolts) {
+    case 3001 ... 3600: /* SD_VOLTAGE_3_3V */
+    case 2001 ... 3000: /* SD_VOLTAGE_3_0V */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "SD card voltage not supported: %.3fV",
+                      millivolts / 1000.f);
+    }
+}
+
 static void sd_set_mode(SDState *sd)
 {
     switch (sd->state) {
@@ -445,6 +469,8 @@ static void sd_reset(DeviceState *dev)
     sd->blk_len = 0x200;
     sd->pwd_len = 0;
     sd->expecting_acmd = false;
+    sd->dat_lines = 0xf;
+    sd->cmd_line = true;
     sd->multi_blk_cnt = 0;
 }
 
@@ -1926,6 +1952,9 @@ static void sd_class_init(ObjectClass *klass, void *data)
     dc->reset = sd_reset;
     dc->bus_type = TYPE_SD_BUS;
 
+    sc->set_voltage = sd_set_voltage;
+    sc->get_dat_lines = sd_get_dat_lines;
+    sc->get_cmd_line = sd_get_cmd_line;
     sc->do_command = sd_do_command;
     sc->write_data = sd_write_data;
     sc->read_data = sd_read_data;
diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 0991acd724..756ef3f3c2 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -24,6 +24,8 @@
 #ifndef SDHCI_INTERNAL_H
 #define SDHCI_INTERNAL_H
 
+#include "hw/registerfields.h"
+
 /* R/W SDMA System Address register 0x0 */
 #define SDHC_SYSAD                     0x00
 
@@ -41,6 +43,7 @@
 #define SDHC_TRNS_DMA                  0x0001
 #define SDHC_TRNS_BLK_CNT_EN           0x0002
 #define SDHC_TRNS_ACMD12               0x0004
+#define SDHC_TRNS_ACMD23               0x0008 /* since v3 */
 #define SDHC_TRNS_READ                 0x0010
 #define SDHC_TRNS_MULTI                0x0020
 #define SDHC_TRNMOD_MASK               0x0037
@@ -79,15 +82,19 @@
 #define SDHC_CARD_PRESENT              0x00010000
 #define SDHC_CARD_DETECT               0x00040000
 #define SDHC_WRITE_PROTECT             0x00080000
+FIELD(SDHC_PRNSTS, DAT_LVL,            20, 4);
+FIELD(SDHC_PRNSTS, CMD_LVL,            24, 1);
 #define TRANSFERRING_DATA(x)           \
     ((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE))
 
 /* R/W Host control Register 0x0 */
 #define SDHC_HOSTCTL                   0x28
 #define SDHC_CTRL_LED                  0x01
+#define SDHC_CTRL_DATATRANSFERWIDTH    0x02 /* SD mode only */
+#define SDHC_CTRL_HIGH_SPEED           0x04
 #define SDHC_CTRL_DMA_CHECK_MASK       0x18
 #define SDHC_CTRL_SDMA                 0x00
-#define SDHC_CTRL_ADMA1_32             0x08
+#define SDHC_CTRL_ADMA1_32             0x08 /* NOT ALLOWED since v2 */
 #define SDHC_CTRL_ADMA2_32             0x10
 #define SDHC_CTRL_ADMA2_64             0x18
 #define SDHC_DMA_TYPE(x)               ((x) & SDHC_CTRL_DMA_CHECK_MASK)
@@ -96,10 +103,10 @@
 #define SDHC_CTRL_CDTEST_INS           0x40
 #define SDHC_CTRL_CDTEST_EN            0x80
 
-
 /* R/W Power Control Register 0x0 */
 #define SDHC_PWRCON                    0x29
 #define SDHC_POWER_ON                  (1 << 0)
+FIELD(SDHC_PWRCON, BUS_VOLTAGE,        1, 3);
 
 /* R/W Block Gap Control Register 0x0 */
 #define SDHC_BLKGAP                    0x2A
@@ -122,6 +129,7 @@
 
 /* R/W Timeout Control Register 0x0 */
 #define SDHC_TIMEOUTCON                0x2E
+FIELD(SDHC_TIMEOUTCON, COUNTER,        0, 4);
 
 /* R/W Software Reset Register 0x0 */
 #define SDHC_SWRST                     0x2F
@@ -178,17 +186,62 @@
 
 /* ROC Auto CMD12 error status register 0x0 */
 #define SDHC_ACMD12ERRSTS              0x3C
+FIELD(SDHC_ACMD12ERRSTS, TIMEOUT_ERR,  1, 1);
+FIELD(SDHC_ACMD12ERRSTS, CRC_ERR,      2, 1);
+FIELD(SDHC_ACMD12ERRSTS, INDEX_ERR,    4, 1);
+
+/* Host Control Register 2 (since v3) */
+#define SDHC_HOSTCTL2                  0x3E
+FIELD(SDHC_HOSTCTL2, UHS_MODE_SEL,     0, 3);
+FIELD(SDHC_HOSTCTL2, V18_ENA,          3, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, DRIVER_STRENGTH,  4, 2); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, EXECUTE_TUNING,   6, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, SAMPLING_CLKSEL,  7, 1); /* UHS-I only */
+FIELD(SDHC_HOSTCTL2, UHS_II_ENA,       8, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, ADMA2_LENGTH,    10, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, CMD23_ENA,       11, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, VERSION4,        12, 1); /* since v4 */
+FIELD(SDHC_HOSTCTL2, ASYNC_INT,       14, 1);
+FIELD(SDHC_HOSTCTL2, PRESET_ENA,      15, 1);
 
 /* HWInit Capabilities Register 0x05E80080 */
 #define SDHC_CAPAB                     0x40
-#define SDHC_CAN_DO_DMA                0x00400000
-#define SDHC_CAN_DO_ADMA2              0x00080000
-#define SDHC_CAN_DO_ADMA1              0x00100000
-#define SDHC_64_BIT_BUS_SUPPORT        (1 << 28)
-#define SDHC_CAPAB_BLOCKSIZE(x)        (((x) >> 16) & 0x3)
+FIELD(SDHC_CAPAB, TOCLKFREQ,           0, 6);
+FIELD(SDHC_CAPAB, TOUNIT,              7, 1);
+FIELD(SDHC_CAPAB, BASECLKFREQ,         8, 8);
+FIELD(SDHC_CAPAB, MAXBLOCKLENGTH,     16, 2);
+FIELD(SDHC_CAPAB, EMBEDDED_8BIT,      18, 1); /* since v3 */
+FIELD(SDHC_CAPAB, ADMA2,              19, 1); /* since v2 */
+FIELD(SDHC_CAPAB, ADMA1,              20, 1); /* v1 only? */
+FIELD(SDHC_CAPAB, HIGHSPEED,          21, 1);
+FIELD(SDHC_CAPAB, SDMA,               22, 1);
+FIELD(SDHC_CAPAB, SUSPRESUME,         23, 1);
+FIELD(SDHC_CAPAB, V33,                24, 1);
+FIELD(SDHC_CAPAB, V30,                25, 1);
+FIELD(SDHC_CAPAB, V18,                26, 1);
+FIELD(SDHC_CAPAB, BUS64BIT_V4,        27, 1); /* since v4.10 */
+FIELD(SDHC_CAPAB, BUS64BIT,           28, 1); /* since v2 */
+FIELD(SDHC_CAPAB, ASYNC_INT,          29, 1); /* since v3 */
+FIELD(SDHC_CAPAB, SLOT_TYPE,          30, 2); /* since v3 */
+FIELD(SDHC_CAPAB, BUS_SPEED,          32, 3); /* since v3 */
+FIELD(SDHC_CAPAB, UHS_II,             35, 8); /* since v4.20 */
+FIELD(SDHC_CAPAB, DRIVER_STRENGTH,    36, 3); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_A,      36, 1); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_C,      37, 1); /* since v3 */
+FIELD(SDHC_CAPAB, DRIVER_TYPE_D,      38, 1); /* since v3 */
+FIELD(SDHC_CAPAB, TIMER_RETUNING,     40, 4); /* since v3 */
+FIELD(SDHC_CAPAB, SDR50_TUNING,       45, 1); /* since v3 */
+FIELD(SDHC_CAPAB, RETUNING_MODE,      46, 2); /* since v3 */
+FIELD(SDHC_CAPAB, CLOCK_MULT,         48, 8); /* since v3 */
+FIELD(SDHC_CAPAB, ADMA3,              59, 1); /* since v4.20 */
+FIELD(SDHC_CAPAB, V18_VDD2,           60, 1); /* since v4.20 */
 
 /* HWInit Maximum Current Capabilities Register 0x0 */
 #define SDHC_MAXCURR                   0x48
+FIELD(SDHC_MAXCURR, V33_VDD1,          0, 8);
+FIELD(SDHC_MAXCURR, V30_VDD1,          8, 8);
+FIELD(SDHC_MAXCURR, V18_VDD1,         16, 8);
+FIELD(SDHC_MAXCURR, V18_VDD2,         32, 8); /* since v4.20 */
 
 /* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */
 #define SDHC_FEAER                     0x50
@@ -216,9 +269,9 @@
 /* Slot interrupt status */
 #define SDHC_SLOT_INT_STATUS            0xFC
 
-/* HWInit Host Controller Version Register 0x0401 */
+/* HWInit Host Controller Version Register */
 #define SDHC_HCVER                      0xFE
-#define SD_HOST_SPECv2_VERS             0x2401
+#define SDHC_HCVER_VENDOR               0x24
 
 #define SDHC_REGISTERS_MAP_SIZE         0x100
 #define SDHC_INSERTION_DELAY            (NANOSECONDS_PER_SECOND)
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index ee95e78aeb..97b4a473c8 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -23,6 +23,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "hw/hw.h"
 #include "sysemu/block-backend.h"
@@ -33,72 +34,194 @@
 #include "hw/sd/sdhci.h"
 #include "sdhci-internal.h"
 #include "qemu/log.h"
+#include "qemu/cutils.h"
 #include "trace.h"
 
 #define TYPE_SDHCI_BUS "sdhci-bus"
 #define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS)
 
+#define MASKED_WRITE(reg, mask, val)  (reg = (reg & (mask)) | (val))
+
 /* Default SD/MMC host controller features information, which will be
  * presented in CAPABILITIES register of generic SD host controller at reset.
- * If not stated otherwise:
- * 0 - not supported, 1 - supported, other - prohibited.
+ *
+ * support:
+ * - 3.3v and 1.8v voltages
+ * - SDMA/ADMA1/ADMA2
+ * - high-speed
+ * max host controller R/W buffers size: 512B
+ * max clock frequency for SDclock: 52 MHz
+ * timeout clock frequency: 52 MHz
+ *
+ * does not support:
+ * - 3.0v voltage
+ * - 64-bit system bus
+ * - suspend/resume
  */
-#define SDHC_CAPAB_64BITBUS       0ul        /* 64-bit System Bus Support */
-#define SDHC_CAPAB_18V            1ul        /* Voltage support 1.8v */
-#define SDHC_CAPAB_30V            0ul        /* Voltage support 3.0v */
-#define SDHC_CAPAB_33V            1ul        /* Voltage support 3.3v */
-#define SDHC_CAPAB_SUSPRESUME     0ul        /* Suspend/resume support */
-#define SDHC_CAPAB_SDMA           1ul        /* SDMA support */
-#define SDHC_CAPAB_HIGHSPEED      1ul        /* High speed support */
-#define SDHC_CAPAB_ADMA1          1ul        /* ADMA1 support */
-#define SDHC_CAPAB_ADMA2          1ul        /* ADMA2 support */
-/* Maximum host controller R/W buffers size
- * Possible values: 512, 1024, 2048 bytes */
-#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
-/* Maximum clock frequency for SDclock in MHz
- * value in range 10-63 MHz, 0 - not defined */
-#define SDHC_CAPAB_BASECLKFREQ    52ul
-#define SDHC_CAPAB_TOUNIT         1ul  /* Timeout clock unit 0 - kHz, 1 - MHz */
-/* Timeout clock frequency 1-63, 0 - not defined */
-#define SDHC_CAPAB_TOCLKFREQ      52ul
-
-/* Now check all parameters and calculate CAPABILITIES REGISTER value */
-#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 ||     \
-    SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 ||  \
-    SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
-    SDHC_CAPAB_TOUNIT > 1
-#error Capabilities features can have value 0 or 1 only!
-#endif
-
-#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
-#define MAX_BLOCK_LENGTH 0ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
-#define MAX_BLOCK_LENGTH 1ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
-#define MAX_BLOCK_LENGTH 2ul
-#else
-#error Max host controller block size can have value 512, 1024 or 2048 only!
-#endif
-
-#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
-    SDHC_CAPAB_BASECLKFREQ > 63
-#error SDclock frequency can have value in range 0, 10-63 only!
-#endif
-
-#if SDHC_CAPAB_TOCLKFREQ > 63
-#error Timeout clock frequency can have value in range 0-63 only!
-#endif
-
-#define SDHC_CAPAB_REG_DEFAULT                                 \
-   ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) |     \
-    (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) |          \
-    (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) |  \
-    (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) |  \
-    (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) |      \
-    (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
-    (SDHC_CAPAB_TOCLKFREQ))
+#define SDHC_CAPAB_REG_DEFAULT 0x057834b4
 
-#define MASKED_WRITE(reg, mask, val)  (reg = (reg & (mask)) | (val))
+static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+{
+    return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
+}
+
+/* return true on error */
+static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc,
+                                         uint8_t freq, Error **errp)
+{
+    if (s->sd_spec_version >= 3) {
+        return false;
+    }
+    switch (freq) {
+    case 0:
+    case 10 ... 63:
+        break;
+    default:
+        error_setg(errp, "SD %s clock frequency can have value"
+                   "in range 0-63 only", desc);
+        return true;
+    }
+    return false;
+}
+
+static void sdhci_check_capareg(SDHCIState *s, Error **errp)
+{
+    uint64_t msk = s->capareg;
+    uint32_t val;
+    bool y;
+
+    switch (s->sd_spec_version) {
+    case 4:
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT_V4);
+        trace_sdhci_capareg("64-bit system bus (v4)", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT_V4, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, UHS_II);
+        trace_sdhci_capareg("UHS-II", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, UHS_II, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA3);
+        trace_sdhci_capareg("ADMA3", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA3, 0);
+
+    /* fallthrough */
+    case 3:
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, ASYNC_INT);
+        trace_sdhci_capareg("async interrupt", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, ASYNC_INT, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, SLOT_TYPE);
+        if (val) {
+            error_setg(errp, "slot-type not supported");
+            return;
+        }
+        trace_sdhci_capareg("slot type", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, SLOT_TYPE, 0);
+
+        if (val != 2) {
+            val = FIELD_EX64(s->capareg, SDHC_CAPAB, EMBEDDED_8BIT);
+            trace_sdhci_capareg("8-bit bus", val);
+        }
+        msk = FIELD_DP64(msk, SDHC_CAPAB, EMBEDDED_8BIT, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS_SPEED);
+        trace_sdhci_capareg("bus speed mask", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, BUS_SPEED, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, DRIVER_STRENGTH);
+        trace_sdhci_capareg("driver strength mask", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, DRIVER_STRENGTH, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, TIMER_RETUNING);
+        trace_sdhci_capareg("timer re-tuning", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, TIMER_RETUNING, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDR50_TUNING);
+        trace_sdhci_capareg("use SDR50 tuning", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, SDR50_TUNING, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, RETUNING_MODE);
+        trace_sdhci_capareg("re-tuning mode", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, RETUNING_MODE, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, CLOCK_MULT);
+        trace_sdhci_capareg("clock multiplier", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, CLOCK_MULT, 0);
+
+    /* fallthrough */
+    case 2: /* default version */
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA2);
+        trace_sdhci_capareg("ADMA2", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA2, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, ADMA1);
+        trace_sdhci_capareg("ADMA1", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, ADMA1, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, BUS64BIT);
+        trace_sdhci_capareg("64-bit system bus (v3)", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, BUS64BIT, 0);
+
+    /* fallthrough */
+    case 1:
+        y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ);
+        trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val);
+        if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) {
+            return;
+        }
+        msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ);
+        trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val);
+        if (sdhci_check_capab_freq_range(s, "base", val, errp)) {
+            return;
+        }
+        msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH);
+        if (val >= 3) {
+            error_setg(errp, "block size can be 512, 1024 or 2048 only");
+            return;
+        }
+        trace_sdhci_capareg("max block length", sdhci_get_fifolen(s));
+        msk = FIELD_DP64(msk, SDHC_CAPAB, MAXBLOCKLENGTH, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, HIGHSPEED);
+        trace_sdhci_capareg("high speed", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, HIGHSPEED, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, SDMA);
+        trace_sdhci_capareg("SDMA", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, SDMA, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, SUSPRESUME);
+        trace_sdhci_capareg("suspend/resume", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, SUSPRESUME, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, V33);
+        trace_sdhci_capareg("3.3v", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, V33, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, V30);
+        trace_sdhci_capareg("3.0v", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, V30, 0);
+
+        val = FIELD_EX64(s->capareg, SDHC_CAPAB, V18);
+        trace_sdhci_capareg("1.8v", val);
+        msk = FIELD_DP64(msk, SDHC_CAPAB, V18, 0);
+        break;
+
+    default:
+        error_setg(errp, "Unsupported spec version: %u", s->sd_spec_version);
+    }
+    if (msk) {
+        qemu_log_mask(LOG_UNIMP,
+                      "SDHCI: unknown CAPAB mask: 0x%016" PRIx64 "\n", msk);
+    }
+}
 
 static uint8_t sdhci_slotint(SDHCIState *s)
 {
@@ -173,7 +296,8 @@ static void sdhci_reset(SDHCIState *s)
 
     timer_del(s->insert_timer);
     timer_del(s->transfer_timer);
-    /* Set all registers to 0. Capabilities registers are not cleared
+
+    /* Set all registers to 0. Capabilities/Version registers are not cleared
      * and assumed to always preserve their value, given to them during
      * initialization */
     memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
@@ -292,19 +416,35 @@ static void sdhci_end_transfer(SDHCIState *s)
 /*
  * Programmed i/o data transfer
  */
+#define BLOCK_SIZE_MASK (4 * K_BYTE - 1)
 
 /* Fill host controller's read buffer with BLKSIZE bytes of data from card */
 static void sdhci_read_block_from_card(SDHCIState *s)
 {
     int index = 0;
+    uint8_t data;
+    const uint16_t blk_size = s->blksize & BLOCK_SIZE_MASK;
 
     if ((s->trnmod & SDHC_TRNS_MULTI) &&
             (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) {
         return;
     }
 
-    for (index = 0; index < (s->blksize & 0x0fff); index++) {
-        s->fifo_buffer[index] = sdbus_read_data(&s->sdbus);
+    for (index = 0; index < blk_size; index++) {
+        data = sdbus_read_data(&s->sdbus);
+        if (!FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
+            /* Device is not in tunning */
+            s->fifo_buffer[index] = data;
+        }
+    }
+
+    if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, EXECUTE_TUNING)) {
+        /* Device is in tunning */
+        s->hostctl2 &= ~R_SDHC_HOSTCTL2_EXECUTE_TUNING_MASK;
+        s->hostctl2 |= R_SDHC_HOSTCTL2_SAMPLING_CLKSEL_MASK;
+        s->prnsts &= ~(SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ |
+                       SDHC_DATA_INHIBIT);
+        goto read_done;
     }
 
     /* New data now available for READ through Buffer Port Register */
@@ -329,6 +469,7 @@ static void sdhci_read_block_from_card(SDHCIState *s)
         }
     }
 
+read_done:
     sdhci_update_irq(s);
 }
 
@@ -348,7 +489,7 @@ static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
         value |= s->fifo_buffer[s->data_count] << i * 8;
         s->data_count++;
         /* check if we've read all valid data (blksize bytes) from buffer */
-        if ((s->data_count) >= (s->blksize & 0x0fff)) {
+        if ((s->data_count) >= (s->blksize & BLOCK_SIZE_MASK)) {
             trace_sdhci_read_dataport(s->data_count);
             s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
             s->data_count = 0;  /* next buff read must start at position [0] */
@@ -395,7 +536,7 @@ static void sdhci_write_block_to_card(SDHCIState *s)
         }
     }
 
-    for (index = 0; index < (s->blksize & 0x0fff); index++) {
+    for (index = 0; index < (s->blksize & BLOCK_SIZE_MASK); index++) {
         sdbus_write_data(&s->sdbus, s->fifo_buffer[index]);
     }
 
@@ -440,7 +581,7 @@ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
         s->fifo_buffer[s->data_count] = value & 0xFF;
         s->data_count++;
         value >>= 8;
-        if (s->data_count >= (s->blksize & 0x0fff)) {
+        if (s->data_count >= (s->blksize & BLOCK_SIZE_MASK)) {
             trace_sdhci_write_dataport(s->data_count);
             s->data_count = 0;
             s->prnsts &= ~SDHC_SPACE_AVAILABLE;
@@ -460,8 +601,8 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
 {
     bool page_aligned = false;
     unsigned int n, begin;
-    const uint16_t block_size = s->blksize & 0x0fff;
-    uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
+    const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
+    uint32_t boundary_chk = 1 << (((s->blksize & ~BLOCK_SIZE_MASK) >> 12) + 12);
     uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
 
     if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
@@ -550,7 +691,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
 static void sdhci_sdma_transfer_single_block(SDHCIState *s)
 {
     int n;
-    uint32_t datacnt = s->blksize & 0x0fff;
+    uint32_t datacnt = s->blksize & BLOCK_SIZE_MASK;
 
     if (s->trnmod & SDHC_TRNS_READ) {
         for (n = 0; n < datacnt; n++) {
@@ -580,7 +721,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
     uint32_t adma1 = 0;
     uint64_t adma2 = 0;
     hwaddr entry_addr = (hwaddr)s->admasysaddr;
-    switch (SDHC_DMA_TYPE(s->hostctl)) {
+    switch (SDHC_DMA_TYPE(s->hostctl1)) {
     case SDHC_CTRL_ADMA2_32:
         dma_memory_read(s->dma_as, entry_addr, (uint8_t *)&adma2,
                         sizeof(adma2));
@@ -614,8 +755,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
         dscr->length = le16_to_cpu(dscr->length);
         dma_memory_read(s->dma_as, entry_addr + 4,
                         (uint8_t *)(&dscr->addr), 8);
-        dscr->attr = le64_to_cpu(dscr->attr);
-        dscr->attr &= 0xfffffff8;
+        dscr->addr = le64_to_cpu(dscr->addr);
+        dscr->attr &= (uint8_t) ~0xC0;
         dscr->incr = 12;
         break;
     }
@@ -626,7 +767,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
 static void sdhci_do_adma(SDHCIState *s)
 {
     unsigned int n, begin, length;
-    const uint16_t block_size = s->blksize & 0x0fff;
+    const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK;
     ADMADescr dscr = {};
     int i;
 
@@ -769,7 +910,7 @@ static void sdhci_data_transfer(void *opaque)
     SDHCIState *s = (SDHCIState *)opaque;
 
     if (s->trnmod & SDHC_TRNS_DMA) {
-        switch (SDHC_DMA_TYPE(s->hostctl)) {
+        switch (SDHC_DMA_TYPE(s->hostctl1)) {
         case SDHC_CTRL_SDMA:
             if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
                 sdhci_sdma_transfer_single_block(s);
@@ -779,7 +920,7 @@ static void sdhci_data_transfer(void *opaque)
 
             break;
         case SDHC_CTRL_ADMA1_32:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
+            if (!(s->capareg & R_SDHC_CAPAB_ADMA1_MASK)) {
                 trace_sdhci_error("ADMA1 not supported");
                 break;
             }
@@ -787,7 +928,7 @@ static void sdhci_data_transfer(void *opaque)
             sdhci_do_adma(s);
             break;
         case SDHC_CTRL_ADMA2_32:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
+            if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK)) {
                 trace_sdhci_error("ADMA2 not supported");
                 break;
             }
@@ -795,8 +936,8 @@ static void sdhci_data_transfer(void *opaque)
             sdhci_do_adma(s);
             break;
         case SDHC_CTRL_ADMA2_64:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
-                    !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
+            if (!(s->capareg & R_SDHC_CAPAB_ADMA2_MASK) ||
+                    !(s->capareg & R_SDHC_CAPAB_BUS64BIT_MASK)) {
                 trace_sdhci_error("64 bit ADMA not supported");
                 break;
             }
@@ -876,9 +1017,13 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
         break;
     case SDHC_PRNSTS:
         ret = s->prnsts;
+        ret = FIELD_DP32(ret, SDHC_PRNSTS, DAT_LVL,
+                         sdbus_get_dat_lines(&s->sdbus));
+        ret = FIELD_DP32(ret, SDHC_PRNSTS, CMD_LVL,
+                         sdbus_get_cmd_line(&s->sdbus));
         break;
     case SDHC_HOSTCTL:
-        ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) |
+        ret = s->hostctl1 | (s->pwrcon << 8) | (s->blkgap << 16) |
               (s->wakcon << 24);
         break;
     case SDHC_CLKCON:
@@ -894,7 +1039,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
         ret = s->norintsigen | (s->errintsigen << 16);
         break;
     case SDHC_ACMD12ERRSTS:
-        ret = s->acmd12errsts;
+        ret = s->acmd12errsts | (s->hostctl2 << 16);
         break;
     case SDHC_CAPAB:
         ret = (uint32_t)s->capareg;
@@ -918,7 +1063,7 @@ static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size)
         ret = (uint32_t)(s->admasysaddr >> 32);
         break;
     case SDHC_SLOT_INT_STATUS:
-        ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
+        ret = (s->version << 16) | sdhci_slotint(s);
         break;
     default:
         qemu_log_mask(LOG_UNIMP, "SDHC rd_%ub @0x%02" HWADDR_PRIx " "
@@ -996,7 +1141,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
         MASKED_WRITE(s->sdmasysad, mask, value);
         /* Writing to last byte of sdmasysad might trigger transfer */
         if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
-                s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
+                s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
             if (s->trnmod & SDHC_TRNS_MULTI) {
                 sdhci_sdma_transfer_multi_blocks(s);
             } else {
@@ -1026,7 +1171,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
     case SDHC_TRNMOD:
         /* DMA can be enabled only if it is supported as indicated by
          * capabilities register */
-        if (!(s->capareg & SDHC_CAN_DO_DMA)) {
+        if (!(s->capareg & R_SDHC_CAPAB_SDMA_MASK)) {
             value &= ~SDHC_TRNS_DMA;
         }
         MASKED_WRITE(s->trnmod, mask, value & SDHC_TRNMOD_MASK);
@@ -1048,7 +1193,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
         if (!(mask & 0xFF0000)) {
             sdhci_blkgap_write(s, value >> 16);
         }
-        MASKED_WRITE(s->hostctl, mask, value);
+        MASKED_WRITE(s->hostctl1, mask, value);
         MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8);
         MASKED_WRITE(s->wakcon, mask >> 24, value >> 24);
         if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 ||
@@ -1128,7 +1273,16 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
         sdhci_update_irq(s);
         break;
     case SDHC_ACMD12ERRSTS:
-        MASKED_WRITE(s->acmd12errsts, mask, value);
+        MASKED_WRITE(s->acmd12errsts, mask, value & UINT16_MAX);
+        if (s->uhs_mode >= UHS_I) {
+            MASKED_WRITE(s->hostctl2, mask >> 16, value >> 16);
+
+            if (FIELD_EX32(s->hostctl2, SDHC_HOSTCTL2, V18_ENA)) {
+                sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_1_8V);
+            } else {
+                sdbus_set_voltage(&s->sdbus, SD_VOLTAGE_3_3V);
+            }
+        }
         break;
 
     case SDHC_CAPAB:
@@ -1159,26 +1313,34 @@ static const MemoryRegionOps sdhci_mmio_ops = {
     .endianness = DEVICE_LITTLE_ENDIAN,
 };
 
-static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp)
 {
-    switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
-    case 0:
-        return 512;
-    case 1:
-        return 1024;
-    case 2:
-        return 2048;
+    Error *local_err = NULL;
+
+    switch (s->sd_spec_version) {
+    case 2 ... 3:
+        break;
     default:
-        hw_error("SDHC: unsupported value for maximum block size\n");
-        return 0;
+        error_setg(errp, "Only Spec v2/v3 are supported");
+        return;
+    }
+    s->version = (SDHC_HCVER_VENDOR << 8) | (s->sd_spec_version - 1);
+
+    sdhci_check_capareg(s, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
     }
 }
 
 /* --- qdev common --- */
 
 #define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \
-    /* Capabilities registers provide information on supported features
-     * of this specific host controller implementation */ \
+    DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \
+    DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \
+    \
+    /* Capabilities registers provide information on supported
+     * features of this specific host controller implementation */ \
     DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \
     DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0)
 
@@ -1206,6 +1368,13 @@ static void sdhci_uninitfn(SDHCIState *s)
 
 static void sdhci_common_realize(SDHCIState *s, Error **errp)
 {
+    Error *local_err = NULL;
+
+    sdhci_init_readonly_registers(s, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
     s->buf_maxsz = sdhci_get_fifolen(s);
     s->fifo_buffer = g_malloc0(s->buf_maxsz);
 
@@ -1255,7 +1424,7 @@ const VMStateDescription sdhci_vmstate = {
         VMSTATE_UINT16(cmdreg, SDHCIState),
         VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4),
         VMSTATE_UINT32(prnsts, SDHCIState),
-        VMSTATE_UINT8(hostctl, SDHCIState),
+        VMSTATE_UINT8(hostctl1, SDHCIState),
         VMSTATE_UINT8(pwrcon, SDHCIState),
         VMSTATE_UINT8(blkgap, SDHCIState),
         VMSTATE_UINT8(wakcon, SDHCIState),
@@ -1302,10 +1471,12 @@ static Property sdhci_pci_properties[] = {
 static void sdhci_pci_realize(PCIDevice *dev, Error **errp)
 {
     SDHCIState *s = PCI_SDHCI(dev);
+    Error *local_err = NULL;
 
     sdhci_initfn(s);
     sdhci_common_realize(s, errp);
-    if (errp && *errp) {
+    if (local_err) {
+        error_propagate(errp, local_err);
         return;
     }
 
@@ -1383,9 +1554,11 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp)
 {
     SDHCIState *s = SYSBUS_SDHCI(dev);
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    Error *local_err = NULL;
 
     sdhci_common_realize(s, errp);
-    if (errp && *errp) {
+    if (local_err) {
+        error_propagate(errp, local_err);
         return;
     }
 
@@ -1457,7 +1630,7 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
 {
     SDHCIState *s = SYSBUS_SDHCI(opaque);
     uint32_t ret;
-    uint16_t hostctl;
+    uint16_t hostctl1;
 
     switch (offset) {
     default:
@@ -1469,17 +1642,17 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
          * manipulation code see comments in a similar part of
          * usdhc_write()
          */
-        hostctl = SDHC_DMA_TYPE(s->hostctl) << (8 - 3);
+        hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
 
-        if (s->hostctl & SDHC_CTRL_8BITBUS) {
-            hostctl |= ESDHC_CTRL_8BITBUS;
+        if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
+            hostctl1 |= ESDHC_CTRL_8BITBUS;
         }
 
-        if (s->hostctl & SDHC_CTRL_4BITBUS) {
-            hostctl |= ESDHC_CTRL_4BITBUS;
+        if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
+            hostctl1 |= ESDHC_CTRL_4BITBUS;
         }
 
-        ret  = hostctl;
+        ret  = hostctl1;
         ret |= (uint32_t)s->blkgap << 16;
         ret |= (uint32_t)s->wakcon << 24;
 
@@ -1503,7 +1676,7 @@ static void
 usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
 {
     SDHCIState *s = SYSBUS_SDHCI(opaque);
-    uint8_t hostctl;
+    uint8_t hostctl1;
     uint32_t value = (uint32_t)val;
 
     switch (offset) {
@@ -1566,25 +1739,25 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
         /*
          * First, save bits 7 6 and 0 since they are identical
          */
-        hostctl = value & (SDHC_CTRL_LED |
-                           SDHC_CTRL_CDTEST_INS |
-                           SDHC_CTRL_CDTEST_EN);
+        hostctl1 = value & (SDHC_CTRL_LED |
+                            SDHC_CTRL_CDTEST_INS |
+                            SDHC_CTRL_CDTEST_EN);
         /*
          * Second, split "Data Transfer Width" from bits 2 and 1 in to
          * bits 5 and 1
          */
         if (value & ESDHC_CTRL_8BITBUS) {
-            hostctl |= SDHC_CTRL_8BITBUS;
+            hostctl1 |= SDHC_CTRL_8BITBUS;
         }
 
         if (value & ESDHC_CTRL_4BITBUS) {
-            hostctl |= ESDHC_CTRL_4BITBUS;
+            hostctl1 |= ESDHC_CTRL_4BITBUS;
         }
 
         /*
          * Third, move DMA select from bits 9 and 8 to bits 4 and 3
          */
-        hostctl |= SDHC_DMA_TYPE(value >> (8 - 3));
+        hostctl1 |= SDHC_DMA_TYPE(value >> (8 - 3));
 
         /*
          * Now place the corrected value into low 16-bit of the value
@@ -1595,7 +1768,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
          * kernel
          */
         value &= ~UINT16_MAX;
-        value |= hostctl;
+        value |= hostctl1;
         value |= (uint16_t)s->pwrcon << 8;
 
         sdhci_write(opaque, offset, value, size);
diff --git a/hw/sd/trace-events b/hw/sd/trace-events
index 0a121156a3..0f8536db32 100644
--- a/hw/sd/trace-events
+++ b/hw/sd/trace-events
@@ -1,5 +1,13 @@
 # See docs/devel/tracing.txt for syntax documentation.
 
+# hw/sd/core.c
+sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg, uint8_t crc) "@%s CMD%02d arg 0x%08x crc 0x%02x"
+sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x"
+sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x"
+sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)"
+sdbus_get_dat_lines(const char *bus_name, uint8_t dat_lines) "@%s dat_lines: %u"
+sdbus_get_cmd_line(const char *bus_name, bool cmd_line) "@%s cmd_line: %u"
+
 # hw/sd/sdhci.c
 sdhci_set_inserted(const char *level) "card state changed: %s"
 sdhci_send_command(uint8_t cmd, uint32_t arg) "CMD%02u ARG[0x%08x]"
@@ -13,6 +21,7 @@ sdhci_adma_transfer_completed(void) ""
 sdhci_access(const char *access, unsigned int size, uint64_t offset, const char *dir, uint64_t val, uint64_t val2) "%s%u: addr[0x%04" PRIx64 "] %s 0x%08" PRIx64 " (%" PRIu64 ")"
 sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read from input buffer"
 sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data"
+sdhci_capareg(const char *desc, uint16_t val) "%s: %u"
 
 # hw/sd/milkymist-memcard.c
 milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x"