summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/npcm7xx.c45
-rw-r--r--hw/arm/npcm7xx_boards.c99
-rw-r--r--hw/arm/smmu-common.c30
-rw-r--r--hw/arm/smmu-internal.h5
-rw-r--r--hw/arm/smmuv3.c58
-rw-r--r--hw/arm/trace-events24
-rw-r--r--hw/arm/virt.c23
-rw-r--r--hw/arm/xlnx-versal.c36
-rw-r--r--hw/display/pl110.c123
-rw-r--r--hw/display/pl110_template.h120
-rw-r--r--hw/display/pxa2xx_lcd.c520
-rw-r--r--hw/display/pxa2xx_template.h447
-rw-r--r--hw/i386/intel_iommu.c32
-rw-r--r--hw/mips/gt64xxx_pci.c59
-rw-r--r--hw/mips/trace-events6
-rw-r--r--hw/misc/meson.build2
-rw-r--r--hw/misc/npcm7xx_mft.c540
-rw-r--r--hw/misc/npcm7xx_pwm.c4
-rw-r--r--hw/misc/trace-events8
-rw-r--r--hw/misc/xlnx-versal-xramc.c253
-rw-r--r--hw/net/allwinner-sun8i-emac.c58
-rw-r--r--hw/timer/sse-timer.c1
-rw-r--r--hw/virtio/virtio-iommu.c19
23 files changed, 1709 insertions, 803 deletions
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 9bd1e83f02..495b0f8e91 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -122,6 +122,14 @@ enum NPCM7xxInterrupt {
     NPCM7XX_SMBUS15_IRQ,
     NPCM7XX_PWM0_IRQ            = 93,   /* PWM module 0 */
     NPCM7XX_PWM1_IRQ,                   /* PWM module 1 */
+    NPCM7XX_MFT0_IRQ            = 96,   /* MFT module 0 */
+    NPCM7XX_MFT1_IRQ,                   /* MFT module 1 */
+    NPCM7XX_MFT2_IRQ,                   /* MFT module 2 */
+    NPCM7XX_MFT3_IRQ,                   /* MFT module 3 */
+    NPCM7XX_MFT4_IRQ,                   /* MFT module 4 */
+    NPCM7XX_MFT5_IRQ,                   /* MFT module 5 */
+    NPCM7XX_MFT6_IRQ,                   /* MFT module 6 */
+    NPCM7XX_MFT7_IRQ,                   /* MFT module 7 */
     NPCM7XX_EMC2RX_IRQ          = 114,
     NPCM7XX_EMC2TX_IRQ,
     NPCM7XX_GPIO0_IRQ           = 116,
@@ -172,6 +180,18 @@ static const hwaddr npcm7xx_pwm_addr[] = {
     0xf0104000,
 };
 
+/* Register base address for each MFT Module */
+static const hwaddr npcm7xx_mft_addr[] = {
+    0xf0180000,
+    0xf0181000,
+    0xf0182000,
+    0xf0183000,
+    0xf0184000,
+    0xf0185000,
+    0xf0186000,
+    0xf0187000,
+};
+
 /* Direct memory-mapped access to each SMBus Module. */
 static const hwaddr npcm7xx_smbus_addr[] = {
     0xf0080000,
@@ -417,6 +437,10 @@ static void npcm7xx_init(Object *obj)
         object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM);
     }
 
+    for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+        object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT);
+    }
+
     for (i = 0; i < ARRAY_SIZE(s->emc); i++) {
         object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC);
     }
@@ -603,6 +627,19 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
         sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
     }
 
+    /* MFT Modules. Cannot fail. */
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_mft_addr) != ARRAY_SIZE(s->mft));
+    for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->mft[i]);
+
+        qdev_connect_clock_in(DEVICE(&s->mft[i]), "clock-in",
+                              qdev_get_clock_out(DEVICE(&s->clk),
+                                                 "apb4-clock"));
+        sysbus_realize(sbd, &error_abort);
+        sysbus_mmio_map(sbd, 0, npcm7xx_mft_addr[i]);
+        sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, NPCM7XX_MFT0_IRQ + i));
+    }
+
     /*
      * EMC Modules. Cannot fail.
      * The mapping of the device to its netdev backend works as follows:
@@ -680,14 +717,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
     create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
     create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
     create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
-    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
     create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
     create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
     create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index fbf6ce8e02..e22fe4bf8f 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -21,6 +21,7 @@
 #include "hw/core/cpu.h"
 #include "hw/i2c/smbus_eeprom.h"
 #include "hw/loader.h"
+#include "hw/qdev-core.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
@@ -116,6 +117,64 @@ static void at24c_eeprom_init(NPCM7xxState *soc, int bus, uint8_t addr,
     i2c_slave_realize_and_unref(i2c_dev, i2c_bus, &error_abort);
 }
 
+static void npcm7xx_init_pwm_splitter(NPCM7xxMachine *machine,
+                                      NPCM7xxState *soc, const int *fan_counts)
+{
+    SplitIRQ *splitters = machine->fan_splitter;
+
+    /*
+     * PWM 0~3 belong to module 0 output 0~3.
+     * PWM 4~7 belong to module 1 output 0~3.
+     */
+    for (int i = 0; i < NPCM7XX_NR_PWM_MODULES; ++i) {
+        for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) {
+            int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j;
+            DeviceState *splitter;
+
+            if (fan_counts[splitter_no] < 1) {
+                continue;
+            }
+            object_initialize_child(OBJECT(machine), "fan-splitter[*]",
+                                    &splitters[splitter_no], TYPE_SPLIT_IRQ);
+            splitter = DEVICE(&splitters[splitter_no]);
+            qdev_prop_set_uint16(splitter, "num-lines",
+                                 fan_counts[splitter_no]);
+            qdev_realize(splitter, NULL, &error_abort);
+            qdev_connect_gpio_out_named(DEVICE(&soc->pwm[i]), "duty-gpio-out",
+                                        j, qdev_get_gpio_in(splitter, 0));
+        }
+    }
+}
+
+static void npcm7xx_connect_pwm_fan(NPCM7xxState *soc, SplitIRQ *splitter,
+                                    int fan_no, int output_no)
+{
+    DeviceState *fan;
+    int fan_input;
+    qemu_irq fan_duty_gpio;
+
+    g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT);
+    /*
+     * Fan 0~1 belong to module 0 input 0~1.
+     * Fan 2~3 belong to module 1 input 0~1.
+     * ...
+     * Fan 14~15 belong to module 7 input 0~1.
+     * Fan 16~17 belong to module 0 input 2~3.
+     * Fan 18~19 belong to module 1 input 2~3.
+     */
+    if (fan_no < 16) {
+        fan = DEVICE(&soc->mft[fan_no / 2]);
+        fan_input = fan_no % 2;
+    } else {
+        fan = DEVICE(&soc->mft[(fan_no - 16) / 2]);
+        fan_input = fan_no % 2 + 2;
+    }
+
+    /* Connect the Fan to PWM module */
+    fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input);
+    qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio);
+}
+
 static void npcm750_evb_i2c_init(NPCM7xxState *soc)
 {
     /* lm75 temperature sensor on SVB, tmp105 is compatible */
@@ -128,6 +187,30 @@ static void npcm750_evb_i2c_init(NPCM7xxState *soc)
     i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
 }
 
+static void npcm750_evb_fan_init(NPCM7xxMachine *machine, NPCM7xxState *soc)
+{
+    SplitIRQ *splitter = machine->fan_splitter;
+    static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2};
+
+    npcm7xx_init_pwm_splitter(machine, soc, fan_counts);
+    npcm7xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[3], 0x06, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[3], 0x07, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[4], 0x08, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[4], 0x09, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[5], 0x0a, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[5], 0x0b, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[6], 0x0c, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[6], 0x0d, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[7], 0x0e, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[7], 0x0f, 1);
+}
+
 static void quanta_gsj_i2c_init(NPCM7xxState *soc)
 {
     /* GSJ machine have 4 max31725 temperature sensors, tmp105 is compatible. */
@@ -142,6 +225,20 @@ static void quanta_gsj_i2c_init(NPCM7xxState *soc)
     /* TODO: Add additional i2c devices. */
 }
 
+static void quanta_gsj_fan_init(NPCM7xxMachine *machine, NPCM7xxState *soc)
+{
+    SplitIRQ *splitter = machine->fan_splitter;
+    static const int fan_counts[] = {2, 2, 2, 0, 0, 0, 0, 0};
+
+    npcm7xx_init_pwm_splitter(machine, soc, fan_counts);
+    npcm7xx_connect_pwm_fan(soc, &splitter[0], 0x00, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[0], 0x01, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[1], 0x02, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[1], 0x03, 1);
+    npcm7xx_connect_pwm_fan(soc, &splitter[2], 0x04, 0);
+    npcm7xx_connect_pwm_fan(soc, &splitter[2], 0x05, 1);
+}
+
 static void npcm750_evb_init(MachineState *machine)
 {
     NPCM7xxState *soc;
@@ -153,6 +250,7 @@ static void npcm750_evb_init(MachineState *machine)
     npcm7xx_load_bootrom(machine, soc);
     npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
     npcm750_evb_i2c_init(soc);
+    npcm750_evb_fan_init(NPCM7XX_MACHINE(machine), soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
@@ -168,6 +266,7 @@ static void quanta_gsj_init(MachineState *machine)
     npcm7xx_connect_flash(&soc->fiu[0], 0, "mx25l25635e",
                           drive_get(IF_MTD, 0, 0));
     quanta_gsj_i2c_init(soc);
+    quanta_gsj_fan_init(NPCM7XX_MACHINE(machine), soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
index 405d5c5325..84d2c62c26 100644
--- a/hw/arm/smmu-common.c
+++ b/hw/arm/smmu-common.c
@@ -151,22 +151,28 @@ inline void
 smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
                     uint8_t tg, uint64_t num_pages, uint8_t ttl)
 {
-    if (ttl && (num_pages == 1)) {
+    /* if tg is not set we use 4KB range invalidation */
+    uint8_t granule = tg ? tg * 2 + 10 : 12;
+
+    if (ttl && (num_pages == 1) && (asid >= 0)) {
         SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova, tg, ttl);
 
-        g_hash_table_remove(s->iotlb, &key);
-    } else {
-        /* if tg is not set we use 4KB range invalidation */
-        uint8_t granule = tg ? tg * 2 + 10 : 12;
+        if (g_hash_table_remove(s->iotlb, &key)) {
+            return;
+        }
+        /*
+         * if the entry is not found, let's see if it does not
+         * belong to a larger IOTLB entry
+         */
+    }
 
-        SMMUIOTLBPageInvInfo info = {
-            .asid = asid, .iova = iova,
-            .mask = (num_pages * 1 << granule) - 1};
+    SMMUIOTLBPageInvInfo info = {
+        .asid = asid, .iova = iova,
+        .mask = (num_pages * 1 << granule) - 1};
 
-        g_hash_table_foreach_remove(s->iotlb,
-                                    smmu_hash_remove_by_asid_iova,
-                                    &info);
-    }
+    g_hash_table_foreach_remove(s->iotlb,
+                                smmu_hash_remove_by_asid_iova,
+                                &info);
 }
 
 inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h
index 55147f29be..2d75b31953 100644
--- a/hw/arm/smmu-internal.h
+++ b/hw/arm/smmu-internal.h
@@ -104,4 +104,9 @@ typedef struct SMMUIOTLBPageInvInfo {
     uint64_t mask;
 } SMMUIOTLBPageInvInfo;
 
+typedef struct SMMUSIDRange {
+    uint32_t start;
+    uint32_t end;
+} SMMUSIDRange;
+
 #endif
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index bd1f97000d..3b87324ce2 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -32,6 +32,7 @@
 
 #include "hw/arm/smmuv3.h"
 #include "smmuv3-internal.h"
+#include "smmu-internal.h"
 
 /**
  * smmuv3_trigger_irq - pulse @irq if enabled and update
@@ -861,7 +862,8 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
     uint16_t vmid = CMD_VMID(cmd);
     bool leaf = CMD_LEAF(cmd);
     uint8_t tg = CMD_TG(cmd);
-    hwaddr num_pages = 1;
+    uint64_t first_page = 0, last_page;
+    uint64_t num_pages = 1;
     int asid = -1;
 
     if (tg) {
@@ -874,9 +876,38 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
     if (type == SMMU_CMD_TLBI_NH_VA) {
         asid = CMD_ASID(cmd);
     }
-    trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf);
-    smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages);
-    smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl);
+
+    /* Split invalidations into ^2 range invalidations */
+    last_page = num_pages - 1;
+    while (num_pages) {
+        uint8_t granule = tg * 2 + 10;
+        uint64_t mask, count;
+
+        mask = dma_aligned_pow2_mask(first_page, last_page, 64 - granule);
+        count = mask + 1;
+
+        trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, count, ttl, leaf);
+        smmuv3_inv_notifiers_iova(s, asid, addr, tg, count);
+        smmu_iotlb_inv_iova(s, asid, addr, tg, count, ttl);
+
+        num_pages -= count;
+        first_page += count;
+        addr += count * BIT_ULL(granule);
+    }
+}
+
+static gboolean
+smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data)
+{
+    SMMUDevice *sdev = (SMMUDevice *)key;
+    uint32_t sid = smmu_get_sid(sdev);
+    SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data;
+
+    if (sid < sid_range->start || sid > sid_range->end) {
+        return false;
+    }
+    trace_smmuv3_config_cache_inv(sid);
+    return true;
 }
 
 static int smmuv3_cmdq_consume(SMMUv3State *s)
@@ -949,27 +980,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
         }
         case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */
         {
-            uint32_t start = CMD_SID(&cmd), end, i;
+            uint32_t start = CMD_SID(&cmd);
             uint8_t range = CMD_STE_RANGE(&cmd);
+            uint64_t end = start + (1ULL << (range + 1)) - 1;
+            SMMUSIDRange sid_range = {start, end};
 
             if (CMD_SSEC(&cmd)) {
                 cmd_error = SMMU_CERROR_ILL;
                 break;
             }
-
-            end = start + (1 << (range + 1)) - 1;
             trace_smmuv3_cmdq_cfgi_ste_range(start, end);
-
-            for (i = start; i <= end; i++) {
-                IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i);
-                SMMUDevice *sdev;
-
-                if (!mr) {
-                    continue;
-                }
-                sdev = container_of(mr, SMMUDevice, iommu);
-                smmuv3_flush_config(sdev);
-            }
+            g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste,
+                                        &sid_range);
             break;
         }
         case SMMU_CMD_CFGI_CD:
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index a335ee891d..b79a91af5f 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -29,26 +29,26 @@ smmuv3_cmdq_opcode(const char *opcode) "<--- %s"
 smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d "
 smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d"
 smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
-smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d"
-smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x"
+smmuv3_record_event(const char *type, uint32_t sid) "%s sid=0x%x"
+smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "sid=0x%x features:0x%x, sid_split:0x%x"
 smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d"
 smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64
-smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d"
-smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d STE bypass iova:0x%"PRIx64" is_write=%d"
-smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d abort on iova:0x%"PRIx64" is_write=%d"
-smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x"
+smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d"
+smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x STE bypass iova:0x%"PRIx64" is_write=%d"
+smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x abort on iova:0x%"PRIx64" is_write=%d"
+smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=0x%x iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x"
 smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64
 smmuv3_decode_cd(uint32_t oas) "oas=%d"
 smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d"
-smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d"
+smmuv3_cmdq_cfgi_ste(int streamid) "streamid= 0x%x"
 smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x"
-smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d"
-smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)"
-smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)"
-smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d"
+smmuv3_cmdq_cfgi_cd(uint32_t sid) "sid=0x%x"
+smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
+smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid=0x%x (hits=%d, misses=%d, hit rate=%d)"
+smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d"
 smmuv3_cmdq_tlbi_nh(void) ""
 smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d"
-smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d"
+smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid=0x%x"
 smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
 smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
 smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index c08bf11297..aa2bbd14e0 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2548,27 +2548,36 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
 static int virt_kvm_type(MachineState *ms, const char *type_str)
 {
     VirtMachineState *vms = VIRT_MACHINE(ms);
-    int max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms);
-    int requested_pa_size;
+    int max_vm_pa_size, requested_pa_size;
+    bool fixed_ipa;
+
+    max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
 
     /* we freeze the memory map to compute the highest gpa */
     virt_set_memmap(vms);
 
     requested_pa_size = 64 - clz64(vms->highest_gpa);
 
+    /*
+     * KVM requires the IPA size to be at least 32 bits.
+     */
+    if (requested_pa_size < 32) {
+        requested_pa_size = 32;
+    }
+
     if (requested_pa_size > max_vm_pa_size) {
         error_report("-m and ,maxmem option values "
                      "require an IPA range (%d bits) larger than "
                      "the one supported by the host (%d bits)",
                      requested_pa_size, max_vm_pa_size);
-       exit(1);
+        exit(1);
     }
     /*
-     * By default we return 0 which corresponds to an implicit legacy
-     * 40b IPA setting. Otherwise we return the actual requested PA
-     * logsize
+     * We return the requested PA log size, unless KVM only supports
+     * the implicit legacy 40b IPA setting, in which case the kvm_type
+     * must be 0.
      */
-    return requested_pa_size > 40 ? requested_pa_size : 0;
+    return fixed_ipa ? 0 : requested_pa_size;
 }
 
 static void virt_machine_class_init(ObjectClass *oc, void *data)
diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c
index 628e77ef66..79609692e4 100644
--- a/hw/arm/xlnx-versal.c
+++ b/hw/arm/xlnx-versal.c
@@ -10,6 +10,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/units.h"
 #include "qapi/error.h"
 #include "qemu/log.h"
 #include "qemu/module.h"
@@ -278,6 +279,40 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic)
     sysbus_connect_irq(sbd, 1, pic[VERSAL_RTC_APB_ERR_IRQ]);
 }
 
+static void versal_create_xrams(Versal *s, qemu_irq *pic)
+{
+    int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl);
+    DeviceState *orgate;
+    int i;
+
+    /* XRAM IRQs get ORed into a single line.  */
+    object_initialize_child(OBJECT(s), "xram-irq-orgate",
+                            &s->lpd.xram.irq_orgate, TYPE_OR_IRQ);
+    orgate = DEVICE(&s->lpd.xram.irq_orgate);
+    object_property_set_int(OBJECT(orgate),
+                            "num-lines", nr_xrams, &error_fatal);
+    qdev_realize(orgate, NULL, &error_fatal);
+    qdev_connect_gpio_out(orgate, 0, pic[VERSAL_XRAM_IRQ_0]);
+
+    for (i = 0; i < ARRAY_SIZE(s->lpd.xram.ctrl); i++) {
+        SysBusDevice *sbd;
+        MemoryRegion *mr;
+
+        object_initialize_child(OBJECT(s), "xram[*]", &s->lpd.xram.ctrl[i],
+                                TYPE_XLNX_XRAM_CTRL);
+        sbd = SYS_BUS_DEVICE(&s->lpd.xram.ctrl[i]);
+        sysbus_realize(sbd, &error_fatal);
+
+        mr = sysbus_mmio_get_region(sbd, 0);
+        memory_region_add_subregion(&s->mr_ps,
+                                    MM_XRAMC + i * MM_XRAMC_SIZE, mr);
+        mr = sysbus_mmio_get_region(sbd, 1);
+        memory_region_add_subregion(&s->mr_ps, MM_XRAM + i * MiB, mr);
+
+        sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(orgate, i));
+    }
+}
+
 /* This takes the board allocated linear DDR memory and creates aliases
  * for each split DDR range/aperture on the Versal address map.
  */
@@ -363,6 +398,7 @@ static void versal_realize(DeviceState *dev, Error **errp)
     versal_create_admas(s, pic);
     versal_create_sds(s, pic);
     versal_create_rtc(s, pic);
+    versal_create_xrams(s, pic);
     versal_map_ddr(s);
     versal_unimp(s);
 
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 02b0d45f06..4bf15c1da5 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -123,16 +123,84 @@ static const unsigned char *idregs[] = {
     pl111_id
 };
 
-#define BITS 8
+#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
+
+#undef RGB
+#define BORDER bgr
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
 #include "pl110_template.h"
-#define BITS 15
+#define ORDER 2
 #include "pl110_template.h"
-#define BITS 16
+#undef BORDER
+#define RGB
+#define BORDER rgb
+#define ORDER 0
 #include "pl110_template.h"
-#define BITS 24
+#define ORDER 1
 #include "pl110_template.h"
-#define BITS 32
+#define ORDER 2
 #include "pl110_template.h"
+#undef BORDER
+
+#undef COPY_PIXEL
+
+static drawfn pl110_draw_fn_32[48] = {
+    pl110_draw_line1_lblp_bgr,
+    pl110_draw_line2_lblp_bgr,
+    pl110_draw_line4_lblp_bgr,
+    pl110_draw_line8_lblp_bgr,
+    pl110_draw_line16_555_lblp_bgr,
+    pl110_draw_line32_lblp_bgr,
+    pl110_draw_line16_lblp_bgr,
+    pl110_draw_line12_lblp_bgr,
+
+    pl110_draw_line1_bbbp_bgr,
+    pl110_draw_line2_bbbp_bgr,
+    pl110_draw_line4_bbbp_bgr,
+    pl110_draw_line8_bbbp_bgr,
+    pl110_draw_line16_555_bbbp_bgr,
+    pl110_draw_line32_bbbp_bgr,
+    pl110_draw_line16_bbbp_bgr,
+    pl110_draw_line12_bbbp_bgr,
+
+    pl110_draw_line1_lbbp_bgr,
+    pl110_draw_line2_lbbp_bgr,
+    pl110_draw_line4_lbbp_bgr,
+    pl110_draw_line8_lbbp_bgr,
+    pl110_draw_line16_555_lbbp_bgr,
+    pl110_draw_line32_lbbp_bgr,
+    pl110_draw_line16_lbbp_bgr,
+    pl110_draw_line12_lbbp_bgr,
+
+    pl110_draw_line1_lblp_rgb,
+    pl110_draw_line2_lblp_rgb,
+    pl110_draw_line4_lblp_rgb,
+    pl110_draw_line8_lblp_rgb,
+    pl110_draw_line16_555_lblp_rgb,
+    pl110_draw_line32_lblp_rgb,
+    pl110_draw_line16_lblp_rgb,
+    pl110_draw_line12_lblp_rgb,
+
+    pl110_draw_line1_bbbp_rgb,
+    pl110_draw_line2_bbbp_rgb,
+    pl110_draw_line4_bbbp_rgb,
+    pl110_draw_line8_bbbp_rgb,
+    pl110_draw_line16_555_bbbp_rgb,
+    pl110_draw_line32_bbbp_rgb,
+    pl110_draw_line16_bbbp_rgb,
+    pl110_draw_line12_bbbp_rgb,
+
+    pl110_draw_line1_lbbp_rgb,
+    pl110_draw_line2_lbbp_rgb,
+    pl110_draw_line4_lbbp_rgb,
+    pl110_draw_line8_lbbp_rgb,
+    pl110_draw_line16_555_lbbp_rgb,
+    pl110_draw_line32_lbbp_rgb,
+    pl110_draw_line16_lbbp_rgb,
+    pl110_draw_line12_lbbp_rgb,
+};
 
 static int pl110_enabled(PL110State *s)
 {
@@ -144,9 +212,7 @@ static void pl110_update_display(void *opaque)
     PL110State *s = (PL110State *)opaque;
     SysBusDevice *sbd;
     DisplaySurface *surface = qemu_console_surface(s->con);
-    drawfn* fntable;
     drawfn fn;
-    int dest_width;
     int src_width;
     int bpp_offset;
     int first;
@@ -158,33 +224,6 @@ static void pl110_update_display(void *opaque)
 
     sbd = SYS_BUS_DEVICE(s);
 
-    switch (surface_bits_per_pixel(surface)) {
-    case 0:
-        return;
-    case 8:
-        fntable = pl110_draw_fn_8;
-        dest_width = 1;
-        break;
-    case 15:
-        fntable = pl110_draw_fn_15;
-        dest_width = 2;
-        break;
-    case 16:
-        fntable = pl110_draw_fn_16;
-        dest_width = 2;
-        break;
-    case 24:
-        fntable = pl110_draw_fn_24;
-        dest_width = 3;
-        break;
-    case 32:
-        fntable = pl110_draw_fn_32;
-        dest_width = 4;
-        break;
-    default:
-        fprintf(stderr, "pl110: Bad color depth\n");
-        exit(1);
-    }
     if (s->cr & PL110_CR_BGR)
         bpp_offset = 0;
     else
@@ -218,12 +257,13 @@ static void pl110_update_display(void *opaque)
         }
     }
 
-    if (s->cr & PL110_CR_BEBO)
-        fn = fntable[s->bpp + 8 + bpp_offset];
-    else if (s->cr & PL110_CR_BEPO)
-        fn = fntable[s->bpp + 16 + bpp_offset];
-    else
-        fn = fntable[s->bpp + bpp_offset];
+    if (s->cr & PL110_CR_BEBO) {
+        fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
+    } else if (s->cr & PL110_CR_BEPO) {
+        fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
+    } else {
+        fn = pl110_draw_fn_32[s->bpp + bpp_offset];
+    }
 
     src_width = s->cols;
     switch (s->bpp) {
@@ -247,7 +287,6 @@ static void pl110_update_display(void *opaque)
         src_width <<= 2;
         break;
     }
-    dest_width *= s->cols;
     first = 0;
     if (s->invalidate) {
         framebuffer_update_memory_section(&s->fbsection,
@@ -258,7 +297,7 @@ static void pl110_update_display(void *opaque)
 
     framebuffer_update_display(surface, &s->fbsection,
                                s->cols, s->rows,
-                               src_width, dest_width, 0,
+                               src_width, s->cols * 4, 0,
                                s->invalidate,
                                fn, s->palette,
                                &first, &last);
diff --git a/hw/display/pl110_template.h b/hw/display/pl110_template.h
index 36ba791c6f..877419aa81 100644
--- a/hw/display/pl110_template.h
+++ b/hw/display/pl110_template.h
@@ -10,118 +10,22 @@
  */
 
 #ifndef ORDER
-
-#if BITS == 8
-#define COPY_PIXEL(to, from) *(to++) = from
-#elif BITS == 15 || BITS == 16
-#define COPY_PIXEL(to, from) do { *(uint16_t *)to = from; to += 2; } while (0)
-#elif BITS == 24
-#define COPY_PIXEL(to, from)    \
-    do {                        \
-        *(to++) = from;         \
-        *(to++) = (from) >> 8;  \
-        *(to++) = (from) >> 16; \
-    } while (0)
-#elif BITS == 32
-#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
-#else
-#error unknown bit depth
+#error "pl110_template.h is only for inclusion by pl110.c"
 #endif
 
-#undef RGB
-#define BORDER bgr
-#define ORDER 0
-#include "pl110_template.h"
-#define ORDER 1
-#include "pl110_template.h"
-#define ORDER 2
-#include "pl110_template.h"
-#undef BORDER
-#define RGB
-#define BORDER rgb
-#define ORDER 0
-#include "pl110_template.h"
-#define ORDER 1
-#include "pl110_template.h"
-#define ORDER 2
-#include "pl110_template.h"
-#undef BORDER
-
-static drawfn glue(pl110_draw_fn_,BITS)[48] =
-{
-    glue(pl110_draw_line1_lblp_bgr,BITS),
-    glue(pl110_draw_line2_lblp_bgr,BITS),
-    glue(pl110_draw_line4_lblp_bgr,BITS),
-    glue(pl110_draw_line8_lblp_bgr,BITS),
-    glue(pl110_draw_line16_555_lblp_bgr,BITS),
-    glue(pl110_draw_line32_lblp_bgr,BITS),
-    glue(pl110_draw_line16_lblp_bgr,BITS),
-    glue(pl110_draw_line12_lblp_bgr,BITS),
-
-    glue(pl110_draw_line1_bbbp_bgr,BITS),
-    glue(pl110_draw_line2_bbbp_bgr,BITS),
-    glue(pl110_draw_line4_bbbp_bgr,BITS),
-    glue(pl110_draw_line8_bbbp_bgr,BITS),
-    glue(pl110_draw_line16_555_bbbp_bgr,BITS),
-    glue(pl110_draw_line32_bbbp_bgr,BITS),
-    glue(pl110_draw_line16_bbbp_bgr,BITS),
-    glue(pl110_draw_line12_bbbp_bgr,BITS),
-
-    glue(pl110_draw_line1_lbbp_bgr,BITS),
-    glue(pl110_draw_line2_lbbp_bgr,BITS),
-    glue(pl110_draw_line4_lbbp_bgr,BITS),
-    glue(pl110_draw_line8_lbbp_bgr,BITS),
-    glue(pl110_draw_line16_555_lbbp_bgr,BITS),
-    glue(pl110_draw_line32_lbbp_bgr,BITS),
-    glue(pl110_draw_line16_lbbp_bgr,BITS),
-    glue(pl110_draw_line12_lbbp_bgr,BITS),
-
-    glue(pl110_draw_line1_lblp_rgb,BITS),
-    glue(pl110_draw_line2_lblp_rgb,BITS),
-    glue(pl110_draw_line4_lblp_rgb,BITS),
-    glue(pl110_draw_line8_lblp_rgb,BITS),
-    glue(pl110_draw_line16_555_lblp_rgb,BITS),
-    glue(pl110_draw_line32_lblp_rgb,BITS),
-    glue(pl110_draw_line16_lblp_rgb,BITS),
-    glue(pl110_draw_line12_lblp_rgb,BITS),
-
-    glue(pl110_draw_line1_bbbp_rgb,BITS),
-    glue(pl110_draw_line2_bbbp_rgb,BITS),
-    glue(pl110_draw_line4_bbbp_rgb,BITS),
-    glue(pl110_draw_line8_bbbp_rgb,BITS),
-    glue(pl110_draw_line16_555_bbbp_rgb,BITS),
-    glue(pl110_draw_line32_bbbp_rgb,BITS),
-    glue(pl110_draw_line16_bbbp_rgb,BITS),
-    glue(pl110_draw_line12_bbbp_rgb,BITS),
-
-    glue(pl110_draw_line1_lbbp_rgb,BITS),
-    glue(pl110_draw_line2_lbbp_rgb,BITS),
-    glue(pl110_draw_line4_lbbp_rgb,BITS),
-    glue(pl110_draw_line8_lbbp_rgb,BITS),
-    glue(pl110_draw_line16_555_lbbp_rgb,BITS),
-    glue(pl110_draw_line32_lbbp_rgb,BITS),
-    glue(pl110_draw_line16_lbbp_rgb,BITS),
-    glue(pl110_draw_line12_lbbp_rgb,BITS),
-};
-
-#undef BITS
-#undef COPY_PIXEL
-
-#else
-
 #if ORDER == 0
-#define NAME glue(glue(lblp_, BORDER), BITS)
+#define NAME glue(lblp_, BORDER)
 #ifdef HOST_WORDS_BIGENDIAN
 #define SWAP_WORDS 1
 #endif
 #elif ORDER == 1
-#define NAME glue(glue(bbbp_, BORDER), BITS)
+#define NAME glue(bbbp_, BORDER)
 #ifndef HOST_WORDS_BIGENDIAN
 #define SWAP_WORDS 1
 #endif
 #else
 #define SWAP_PIXELS 1
-#define NAME glue(glue(lbbp_, BORDER), BITS)
+#define NAME glue(lbbp_, BORDER)
 #ifdef HOST_WORDS_BIGENDIAN
 #define SWAP_WORDS 1
 #endif
@@ -270,14 +174,14 @@ static void glue(pl110_draw_line16_,NAME)(void *opaque, uint8_t *d, const uint8_
         MSB = (data & 0x1f) << 3;
         data >>= 5;
 #endif
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
         LSB = (data & 0x1f) << 3;
         data >>= 5;
         g = (data & 0x3f) << 2;
         data >>= 6;
         MSB = (data & 0x1f) << 3;
         data >>= 5;
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
 #undef MSB
 #undef LSB
         width -= 2;
@@ -307,7 +211,7 @@ static void glue(pl110_draw_line32_,NAME)(void *opaque, uint8_t *d, const uint8_
         g = (data >> 16) & 0xff;
         MSB = (data >> 8) & 0xff;
 #endif
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
 #undef MSB
 #undef LSB
         width--;
@@ -338,14 +242,14 @@ static void glue(pl110_draw_line16_555_,NAME)(void *opaque, uint8_t *d, const ui
         data >>= 5;
         MSB = (data & 0x1f) << 3;
         data >>= 5;
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
         LSB = (data & 0x1f) << 3;
         data >>= 5;
         g = (data & 0x1f) << 3;
         data >>= 5;
         MSB = (data & 0x1f) << 3;
         data >>= 6;
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
 #undef MSB
 #undef LSB
         width -= 2;
@@ -376,14 +280,14 @@ static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_
         data >>= 4;
         MSB = (data & 0xf) << 4;
         data >>= 8;
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
         LSB = (data & 0xf) << 4;
         data >>= 4;
         g = (data & 0xf) << 4;
         data >>= 4;
         MSB = (data & 0xf) << 4;
         data >>= 8;
-        COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+        COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
 #undef MSB
 #undef LSB
         width -= 2;
@@ -395,5 +299,3 @@ static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_
 #undef NAME
 #undef SWAP_WORDS
 #undef ORDER
-
-#endif
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
index dfff994962..2887ce496b 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -45,7 +45,6 @@ struct PXA2xxLCDState {
 
     int invalidated;
     QemuConsole *con;
-    drawfn *line_fn[2];
     int dest_width;
     int xres, yres;
     int pal_for;
@@ -188,6 +187,435 @@ typedef struct QEMU_PACKED {
 #define LDCMD_SOFINT	(1 << 22)
 #define LDCMD_PAL	(1 << 26)
 
+/* Size of a pixel in the QEMU UI output surface, in bytes */
+#define DEST_PIXEL_WIDTH 4
+
+/* Line drawing code to handle the various possible guest pixel formats */
+
+# define SKIP_PIXEL(to) do { to += deststep; } while (0)
+# define COPY_PIXEL(to, from)    \
+    do {                         \
+        *(uint32_t *) to = from; \
+        SKIP_PIXEL(to);          \
+    } while (0)
+
+#ifdef HOST_WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+#define FN_2(x) FN(x + 1) FN(x)
+#define FN_4(x) FN_2(x + 2) FN_2(x)
+
+static void pxa2xx_draw_line2(void *opaque, uint8_t *dest, const uint8_t *src,
+                              int width, int deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]);
+#ifdef SWAP_WORDS
+        FN_4(12)
+        FN_4(8)
+        FN_4(4)
+        FN_4(0)
+#else
+        FN_4(0)
+        FN_4(4)
+        FN_4(8)
+        FN_4(12)
+#endif
+#undef FN
+        width -= 16;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line4(void *opaque, uint8_t *dest, const uint8_t *src,
+                              int width, int deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]);
+#ifdef SWAP_WORDS
+        FN_2(6)
+        FN_2(4)
+        FN_2(2)
+        FN_2(0)
+#else
+        FN_2(0)
+        FN_2(2)
+        FN_2(4)
+        FN_2(6)
+#endif
+#undef FN
+        width -= 8;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line8(void *opaque, uint8_t *dest, const uint8_t *src,
+                              int width, int deststep)
+{
+    uint32_t *palette = opaque;
+    uint32_t data;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+        FN(24)
+        FN(16)
+        FN(8)
+        FN(0)
+#else
+        FN(0)
+        FN(8)
+        FN(16)
+        FN(24)
+#endif
+#undef FN
+        width -= 4;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line16(void *opaque, uint8_t *dest, const uint8_t *src,
+                               int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        r = (data & 0x1f) << 3;
+        data >>= 5;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        b = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        r = (data & 0x1f) << 3;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        width -= 2;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line16t(void *opaque, uint8_t *dest, const uint8_t *src,
+                                int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x1f) << 3;
+        data >>= 5;
+        r = (data & 0x1f) << 3;
+        data >>= 5;
+        if (data & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        data >>= 1;
+        b = (data & 0x1f) << 3;
+        data >>= 5;
+        g = (data & 0x1f) << 3;
+        data >>= 5;
+        r = (data & 0x1f) << 3;
+        data >>= 5;
+        if (data & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        width -= 2;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line18(void *opaque, uint8_t *dest, const uint8_t *src,
+                               int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = (data & 0x3f) << 2;
+        data >>= 6;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        r = (data & 0x3f) << 2;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        width -= 1;
+        src += 4;
+    }
+}
+
+/* The wicked packed format */
+static void pxa2xx_draw_line18p(void *opaque, uint8_t *dest, const uint8_t *src,
+                                int width, int deststep)
+{
+    uint32_t data[3];
+    unsigned int r, g, b;
+    while (width > 0) {
+        data[0] = *(uint32_t *) src;
+        src += 4;
+        data[1] = *(uint32_t *) src;
+        src += 4;
+        data[2] = *(uint32_t *) src;
+        src += 4;
+#ifdef SWAP_WORDS
+        data[0] = bswap32(data[0]);
+        data[1] = bswap32(data[1]);
+        data[2] = bswap32(data[2]);
+#endif
+        b = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        g = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        r = (data[0] & 0x3f) << 2;
+        data[0] >>= 12;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        b = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+        data[1] >>= 4;
+        r = (data[1] & 0x3f) << 2;
+        data[1] >>= 12;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        b = (data[1] & 0x3f) << 2;
+        data[1] >>= 6;
+        g = (data[1] & 0x3f) << 2;
+        data[1] >>= 6;
+        r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+        data[2] >>= 8;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        b = (data[2] & 0x3f) << 2;
+        data[2] >>= 6;
+        g = (data[2] & 0x3f) << 2;
+        data[2] >>= 6;
+        r = data[2] << 2;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        width -= 4;
+    }
+}
+
+static void pxa2xx_draw_line19(void *opaque, uint8_t *dest, const uint8_t *src,
+                               int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = (data & 0x3f) << 2;
+        data >>= 6;
+        g = (data & 0x3f) << 2;
+        data >>= 6;
+        r = (data & 0x3f) << 2;
+        data >>= 6;
+        if (data & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        width -= 1;
+        src += 4;
+    }
+}
+
+/* The wicked packed format */
+static void pxa2xx_draw_line19p(void *opaque, uint8_t *dest, const uint8_t *src,
+                                int width, int deststep)
+{
+    uint32_t data[3];
+    unsigned int r, g, b;
+    while (width > 0) {
+        data[0] = *(uint32_t *) src;
+        src += 4;
+        data[1] = *(uint32_t *) src;
+        src += 4;
+        data[2] = *(uint32_t *) src;
+        src += 4;
+# ifdef SWAP_WORDS
+        data[0] = bswap32(data[0]);
+        data[1] = bswap32(data[1]);
+        data[2] = bswap32(data[2]);
+# endif
+        b = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        g = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        r = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        if (data[0] & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        data[0] >>= 6;
+        b = (data[0] & 0x3f) << 2;
+        data[0] >>= 6;
+        g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+        data[1] >>= 4;
+        r = (data[1] & 0x3f) << 2;
+        data[1] >>= 6;
+        if (data[1] & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        data[1] >>= 6;
+        b = (data[1] & 0x3f) << 2;
+        data[1] >>= 6;
+        g = (data[1] & 0x3f) << 2;
+        data[1] >>= 6;
+        r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+        data[2] >>= 2;
+        if (data[2] & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        data[2] >>= 6;
+        b = (data[2] & 0x3f) << 2;
+        data[2] >>= 6;
+        g = (data[2] & 0x3f) << 2;
+        data[2] >>= 6;
+        r = data[2] << 2;
+        data[2] >>= 6;
+        if (data[2] & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        width -= 4;
+    }
+}
+
+static void pxa2xx_draw_line24(void *opaque, uint8_t *dest, const uint8_t *src,
+                               int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = data & 0xff;
+        data >>= 8;
+        g = data & 0xff;
+        data >>= 8;
+        r = data & 0xff;
+        COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        width -= 1;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line24t(void *opaque, uint8_t *dest, const uint8_t *src,
+                                int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = (data & 0x7f) << 1;
+        data >>= 7;
+        g = data & 0xff;
+        data >>= 8;
+        r = data & 0xff;
+        data >>= 8;
+        if (data & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        width -= 1;
+        src += 4;
+    }
+}
+
+static void pxa2xx_draw_line25(void *opaque, uint8_t *dest, const uint8_t *src,
+                               int width, int deststep)
+{
+    uint32_t data;
+    unsigned int r, g, b;
+    while (width > 0) {
+        data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+        data = bswap32(data);
+#endif
+        b = data & 0xff;
+        data >>= 8;
+        g = data & 0xff;
+        data >>= 8;
+        r = data & 0xff;
+        data >>= 8;
+        if (data & 1) {
+            SKIP_PIXEL(dest);
+        } else {
+            COPY_PIXEL(dest, rgb_to_pixel32(r, g, b));
+        }
+        width -= 1;
+        src += 4;
+    }
+}
+
+/* Overlay planes disabled, no transparency */
+static drawfn pxa2xx_draw_fn_32[16] = {
+    [0 ... 0xf]       = NULL,
+    [pxa_lcdc_2bpp]   = pxa2xx_draw_line2,
+    [pxa_lcdc_4bpp]   = pxa2xx_draw_line4,
+    [pxa_lcdc_8bpp]   = pxa2xx_draw_line8,
+    [pxa_lcdc_16bpp]  = pxa2xx_draw_line16,
+    [pxa_lcdc_18bpp]  = pxa2xx_draw_line18,
+    [pxa_lcdc_18pbpp] = pxa2xx_draw_line18p,
+    [pxa_lcdc_24bpp]  = pxa2xx_draw_line24,
+};
+
+/* Overlay planes enabled, transparency used */
+static drawfn pxa2xx_draw_fn_32t[16] = {
+    [0 ... 0xf]       = NULL,
+    [pxa_lcdc_4bpp]   = pxa2xx_draw_line4,
+    [pxa_lcdc_8bpp]   = pxa2xx_draw_line8,
+    [pxa_lcdc_16bpp]  = pxa2xx_draw_line16t,
+    [pxa_lcdc_19bpp]  = pxa2xx_draw_line19,
+    [pxa_lcdc_19pbpp] = pxa2xx_draw_line19p,
+    [pxa_lcdc_24bpp]  = pxa2xx_draw_line24t,
+    [pxa_lcdc_25bpp]  = pxa2xx_draw_line25,
+};
+
+#undef COPY_PIXEL
+#undef SKIP_PIXEL
+
+#ifdef SWAP_WORDS
+# undef SWAP_WORDS
+#endif
+
 /* Route internal interrupt lines to the global IC */
 static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s)
 {
@@ -674,14 +1102,21 @@ static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp)
     }
 }
 
+static inline drawfn pxa2xx_drawfn(PXA2xxLCDState *s)
+{
+    if (s->transp) {
+        return pxa2xx_draw_fn_32t[s->bpp];
+    } else {
+        return pxa2xx_draw_fn_32[s->bpp];
+    }
+}
+
 static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
                 hwaddr addr, int *miny, int *maxy)
 {
     DisplaySurface *surface = qemu_console_surface(s->con);
     int src_width, dest_width;
-    drawfn fn = NULL;
-    if (s->dest_width)
-        fn = s->line_fn[s->transp][s->bpp];
+    drawfn fn = pxa2xx_drawfn(s);
     if (!fn)
         return;
 
@@ -693,14 +1128,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
     else if (s->bpp > pxa_lcdc_8bpp)
         src_width *= 2;
 
-    dest_width = s->xres * s->dest_width;
+    dest_width = s->xres * DEST_PIXEL_WIDTH;
     *miny = 0;
     if (s->invalidated) {
         framebuffer_update_memory_section(&s->fbsection, s->sysmem,
                                           addr, s->yres, src_width);
     }
     framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
-                               src_width, dest_width, s->dest_width,
+                               src_width, dest_width, DEST_PIXEL_WIDTH,
                                s->invalidated,
                                fn, s->dma_ch[0].palette, miny, maxy);
 }
@@ -710,9 +1145,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s,
 {
     DisplaySurface *surface = qemu_console_surface(s->con);
     int src_width, dest_width;
-    drawfn fn = NULL;
-    if (s->dest_width)
-        fn = s->line_fn[s->transp][s->bpp];
+    drawfn fn = pxa2xx_drawfn(s);
     if (!fn)
         return;
 
@@ -724,14 +1157,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s,
     else if (s->bpp > pxa_lcdc_8bpp)
         src_width *= 2;
 
-    dest_width = s->yres * s->dest_width;
+    dest_width = s->yres * DEST_PIXEL_WIDTH;
     *miny = 0;
     if (s->invalidated) {
         framebuffer_update_memory_section(&s->fbsection, s->sysmem,
                                           addr, s->yres, src_width);
     }
     framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
-                               src_width, s->dest_width, -dest_width,
+                               src_width, DEST_PIXEL_WIDTH, -dest_width,
                                s->invalidated,
                                fn, s->dma_ch[0].palette,
                                miny, maxy);
@@ -742,10 +1175,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
 {
     DisplaySurface *surface = qemu_console_surface(s->con);
     int src_width, dest_width;
-    drawfn fn = NULL;
-    if (s->dest_width) {
-        fn = s->line_fn[s->transp][s->bpp];
-    }
+    drawfn fn = pxa2xx_drawfn(s);
     if (!fn) {
         return;
     }
@@ -759,14 +1189,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
         src_width *= 2;
     }
 
-    dest_width = s->xres * s->dest_width;
+    dest_width = s->xres * DEST_PIXEL_WIDTH;
     *miny = 0;
     if (s->invalidated) {
         framebuffer_update_memory_section(&s->fbsection, s->sysmem,
                                           addr, s->yres, src_width);
     }
     framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
-                               src_width, -dest_width, -s->dest_width,
+                               src_width, -dest_width, -DEST_PIXEL_WIDTH,
                                s->invalidated,
                                fn, s->dma_ch[0].palette, miny, maxy);
 }
@@ -776,10 +1206,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s,
 {
     DisplaySurface *surface = qemu_console_surface(s->con);
     int src_width, dest_width;
-    drawfn fn = NULL;
-    if (s->dest_width) {
-        fn = s->line_fn[s->transp][s->bpp];
-    }
+    drawfn fn = pxa2xx_drawfn(s);
     if (!fn) {
         return;
     }
@@ -793,14 +1220,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s,
         src_width *= 2;
     }
 
-    dest_width = s->yres * s->dest_width;
+    dest_width = s->yres * DEST_PIXEL_WIDTH;
     *miny = 0;
     if (s->invalidated) {
         framebuffer_update_memory_section(&s->fbsection, s->sysmem,
                                           addr, s->yres, src_width);
     }
     framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres,
-                               src_width, -s->dest_width, dest_width,
+                               src_width, -DEST_PIXEL_WIDTH, dest_width,
                                s->invalidated,
                                fn, s->dma_ch[0].palette,
                                miny, maxy);
@@ -990,17 +1417,6 @@ static const VMStateDescription vmstate_pxa2xx_lcdc = {
     }
 };
 
-#define BITS 8
-#include "pxa2xx_template.h"
-#define BITS 15
-#include "pxa2xx_template.h"
-#define BITS 16
-#include "pxa2xx_template.h"
-#define BITS 24
-#include "pxa2xx_template.h"
-#define BITS 32
-#include "pxa2xx_template.h"
-
 static const GraphicHwOps pxa2xx_ops = {
     .invalidate  = pxa2xx_invalidate_display,
     .gfx_update  = pxa2xx_update_display,
@@ -1010,7 +1426,6 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
                                  hwaddr base, qemu_irq irq)
 {
     PXA2xxLCDState *s;
-    DisplaySurface *surface;
 
     s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState));
     s->invalidated = 1;
@@ -1024,41 +1439,6 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
     s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s);
-    surface = qemu_console_surface(s->con);
-
-    switch (surface_bits_per_pixel(surface)) {
-    case 0:
-        s->dest_width = 0;
-        break;
-    case 8:
-        s->line_fn[0] = pxa2xx_draw_fn_8;
-        s->line_fn[1] = pxa2xx_draw_fn_8t;
-        s->dest_width = 1;
-        break;
-    case 15:
-        s->line_fn[0] = pxa2xx_draw_fn_15;
-        s->line_fn[1] = pxa2xx_draw_fn_15t;
-        s->dest_width = 2;
-        break;
-    case 16:
-        s->line_fn[0] = pxa2xx_draw_fn_16;
-        s->line_fn[1] = pxa2xx_draw_fn_16t;
-        s->dest_width = 2;
-        break;
-    case 24:
-        s->line_fn[0] = pxa2xx_draw_fn_24;
-        s->line_fn[1] = pxa2xx_draw_fn_24t;
-        s->dest_width = 3;
-        break;
-    case 32:
-        s->line_fn[0] = pxa2xx_draw_fn_32;
-        s->line_fn[1] = pxa2xx_draw_fn_32t;
-        s->dest_width = 4;
-        break;
-    default:
-        fprintf(stderr, "%s: Bad color depth\n", __func__);
-        exit(1);
-    }
 
     vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s);
 
diff --git a/hw/display/pxa2xx_template.h b/hw/display/pxa2xx_template.h
deleted file mode 100644
index c64eebc4b6..0000000000
--- a/hw/display/pxa2xx_template.h
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Intel XScale PXA255/270 LCDC emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GPLv2.
- *
- * Framebuffer format conversion routines.
- */
-
-# define SKIP_PIXEL(to)		to += deststep
-#if BITS == 8
-# define COPY_PIXEL(to, from)  do { *to = from; SKIP_PIXEL(to); } while (0)
-#elif BITS == 15 || BITS == 16
-# define COPY_PIXEL(to, from)    \
-    do {                         \
-        *(uint16_t *) to = from; \
-        SKIP_PIXEL(to);          \
-    } while (0)
-#elif BITS == 24
-# define COPY_PIXEL(to, from)     \
-    do {                          \
-        *(uint16_t *) to = from;  \
-        *(to + 2) = (from) >> 16; \
-        SKIP_PIXEL(to);           \
-    } while (0)
-#elif BITS == 32
-# define COPY_PIXEL(to, from)    \
-    do {                         \
-        *(uint32_t *) to = from; \
-        SKIP_PIXEL(to);          \
-    } while (0)
-#else
-# error unknown bit depth
-#endif
-
-#ifdef HOST_WORDS_BIGENDIAN
-# define SWAP_WORDS	1
-#endif
-
-#define FN_2(x)		FN(x + 1) FN(x)
-#define FN_4(x)		FN_2(x + 2) FN_2(x)
-
-static void glue(pxa2xx_draw_line2_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t *palette = opaque;
-    uint32_t data;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#define FN(x)		COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]);
-#ifdef SWAP_WORDS
-        FN_4(12)
-        FN_4(8)
-        FN_4(4)
-        FN_4(0)
-#else
-        FN_4(0)
-        FN_4(4)
-        FN_4(8)
-        FN_4(12)
-#endif
-#undef FN
-        width -= 16;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line4_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t *palette = opaque;
-    uint32_t data;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#define FN(x)		COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]);
-#ifdef SWAP_WORDS
-        FN_2(6)
-        FN_2(4)
-        FN_2(2)
-        FN_2(0)
-#else
-        FN_2(0)
-        FN_2(2)
-        FN_2(4)
-        FN_2(6)
-#endif
-#undef FN
-        width -= 8;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line8_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t *palette = opaque;
-    uint32_t data;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#define FN(x)		COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]);
-#ifdef SWAP_WORDS
-        FN(24)
-        FN(16)
-        FN(8)
-        FN(0)
-#else
-        FN(0)
-        FN(8)
-        FN(16)
-        FN(24)
-#endif
-#undef FN
-        width -= 4;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line16_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = (data & 0x1f) << 3;
-        data >>= 5;
-        g = (data & 0x3f) << 2;
-        data >>= 6;
-        r = (data & 0x1f) << 3;
-        data >>= 5;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        b = (data & 0x1f) << 3;
-        data >>= 5;
-        g = (data & 0x3f) << 2;
-        data >>= 6;
-        r = (data & 0x1f) << 3;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 2;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line16t_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = (data & 0x1f) << 3;
-        data >>= 5;
-        g = (data & 0x1f) << 3;
-        data >>= 5;
-        r = (data & 0x1f) << 3;
-        data >>= 5;
-        if (data & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        data >>= 1;
-        b = (data & 0x1f) << 3;
-        data >>= 5;
-        g = (data & 0x1f) << 3;
-        data >>= 5;
-        r = (data & 0x1f) << 3;
-        data >>= 5;
-        if (data & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 2;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line18_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = (data & 0x3f) << 2;
-        data >>= 6;
-        g = (data & 0x3f) << 2;
-        data >>= 6;
-        r = (data & 0x3f) << 2;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 1;
-        src += 4;
-    }
-}
-
-/* The wicked packed format */
-static void glue(pxa2xx_draw_line18p_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data[3];
-    unsigned int r, g, b;
-    while (width > 0) {
-        data[0] = *(uint32_t *) src;
-        src += 4;
-        data[1] = *(uint32_t *) src;
-        src += 4;
-        data[2] = *(uint32_t *) src;
-        src += 4;
-#ifdef SWAP_WORDS
-        data[0] = bswap32(data[0]);
-        data[1] = bswap32(data[1]);
-        data[2] = bswap32(data[2]);
-#endif
-        b = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        g = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        r = (data[0] & 0x3f) << 2;
-        data[0] >>= 12;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        b = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        g = ((data[1] & 0xf) << 4) | (data[0] << 2);
-        data[1] >>= 4;
-        r = (data[1] & 0x3f) << 2;
-        data[1] >>= 12;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        b = (data[1] & 0x3f) << 2;
-        data[1] >>= 6;
-        g = (data[1] & 0x3f) << 2;
-        data[1] >>= 6;
-        r = ((data[2] & 0x3) << 6) | (data[1] << 2);
-        data[2] >>= 8;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        b = (data[2] & 0x3f) << 2;
-        data[2] >>= 6;
-        g = (data[2] & 0x3f) << 2;
-        data[2] >>= 6;
-        r = data[2] << 2;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line19_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = (data & 0x3f) << 2;
-        data >>= 6;
-        g = (data & 0x3f) << 2;
-        data >>= 6;
-        r = (data & 0x3f) << 2;
-        data >>= 6;
-        if (data & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 1;
-        src += 4;
-    }
-}
-
-/* The wicked packed format */
-static void glue(pxa2xx_draw_line19p_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data[3];
-    unsigned int r, g, b;
-    while (width > 0) {
-        data[0] = *(uint32_t *) src;
-        src += 4;
-        data[1] = *(uint32_t *) src;
-        src += 4;
-        data[2] = *(uint32_t *) src;
-        src += 4;
-# ifdef SWAP_WORDS
-        data[0] = bswap32(data[0]);
-        data[1] = bswap32(data[1]);
-        data[2] = bswap32(data[2]);
-# endif
-        b = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        g = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        r = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        if (data[0] & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        data[0] >>= 6;
-        b = (data[0] & 0x3f) << 2;
-        data[0] >>= 6;
-        g = ((data[1] & 0xf) << 4) | (data[0] << 2);
-        data[1] >>= 4;
-        r = (data[1] & 0x3f) << 2;
-        data[1] >>= 6;
-        if (data[1] & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        data[1] >>= 6;
-        b = (data[1] & 0x3f) << 2;
-        data[1] >>= 6;
-        g = (data[1] & 0x3f) << 2;
-        data[1] >>= 6;
-        r = ((data[2] & 0x3) << 6) | (data[1] << 2);
-        data[2] >>= 2;
-        if (data[2] & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        data[2] >>= 6;
-        b = (data[2] & 0x3f) << 2;
-        data[2] >>= 6;
-        g = (data[2] & 0x3f) << 2;
-        data[2] >>= 6;
-        r = data[2] << 2;
-        data[2] >>= 6;
-        if (data[2] & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line24_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = data & 0xff;
-        data >>= 8;
-        g = data & 0xff;
-        data >>= 8;
-        r = data & 0xff;
-        COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 1;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line24t_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = (data & 0x7f) << 1;
-        data >>= 7;
-        g = data & 0xff;
-        data >>= 8;
-        r = data & 0xff;
-        data >>= 8;
-        if (data & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 1;
-        src += 4;
-    }
-}
-
-static void glue(pxa2xx_draw_line25_, BITS)(void *opaque,
-                uint8_t *dest, const uint8_t *src, int width, int deststep)
-{
-    uint32_t data;
-    unsigned int r, g, b;
-    while (width > 0) {
-        data = *(uint32_t *) src;
-#ifdef SWAP_WORDS
-        data = bswap32(data);
-#endif
-        b = data & 0xff;
-        data >>= 8;
-        g = data & 0xff;
-        data >>= 8;
-        r = data & 0xff;
-        data >>= 8;
-        if (data & 1)
-            SKIP_PIXEL(dest);
-        else
-            COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
-        width -= 1;
-        src += 4;
-    }
-}
-
-/* Overlay planes disabled, no transparency */
-static drawfn glue(pxa2xx_draw_fn_, BITS)[16] =
-{
-    [0 ... 0xf]       = NULL,
-    [pxa_lcdc_2bpp]   = glue(pxa2xx_draw_line2_, BITS),
-    [pxa_lcdc_4bpp]   = glue(pxa2xx_draw_line4_, BITS),
-    [pxa_lcdc_8bpp]   = glue(pxa2xx_draw_line8_, BITS),
-    [pxa_lcdc_16bpp]  = glue(pxa2xx_draw_line16_, BITS),
-    [pxa_lcdc_18bpp]  = glue(pxa2xx_draw_line18_, BITS),
-    [pxa_lcdc_18pbpp] = glue(pxa2xx_draw_line18p_, BITS),
-    [pxa_lcdc_24bpp]  = glue(pxa2xx_draw_line24_, BITS),
-};
-
-/* Overlay planes enabled, transparency used */
-static drawfn glue(glue(pxa2xx_draw_fn_, BITS), t)[16] =
-{
-    [0 ... 0xf]       = NULL,
-    [pxa_lcdc_4bpp]   = glue(pxa2xx_draw_line4_, BITS),
-    [pxa_lcdc_8bpp]   = glue(pxa2xx_draw_line8_, BITS),
-    [pxa_lcdc_16bpp]  = glue(pxa2xx_draw_line16t_, BITS),
-    [pxa_lcdc_19bpp]  = glue(pxa2xx_draw_line19_, BITS),
-    [pxa_lcdc_19pbpp] = glue(pxa2xx_draw_line19p_, BITS),
-    [pxa_lcdc_24bpp]  = glue(pxa2xx_draw_line24t_, BITS),
-    [pxa_lcdc_25bpp]  = glue(pxa2xx_draw_line25_, BITS),
-};
-
-#undef BITS
-#undef COPY_PIXEL
-#undef SKIP_PIXEL
-
-#ifdef SWAP_WORDS
-# undef SWAP_WORDS
-#endif
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index b4f5094259..6be8f32918 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -35,6 +35,7 @@
 #include "hw/i386/x86-iommu.h"
 #include "hw/pci-host/q35.h"
 #include "sysemu/kvm.h"
+#include "sysemu/dma.h"
 #include "sysemu/sysemu.h"
 #include "hw/i386/apic_internal.h"
 #include "kvm/kvm_i386.h"
@@ -1884,6 +1885,8 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
     case 3:
         mask = 7;   /* Mask bit 2:0 in the SID field */
         break;
+    default:
+        g_assert_not_reached();
     }
     mask = ~mask;
 
@@ -3453,24 +3456,6 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
     return vtd_dev_as;
 }
 
-static uint64_t get_naturally_aligned_size(uint64_t start,
-                                           uint64_t size, int gaw)
-{
-    uint64_t max_mask = 1ULL << gaw;
-    uint64_t alignment = start ? start & -start : max_mask;
-
-    alignment = MIN(alignment, max_mask);
-    size = MIN(size, max_mask);
-
-    if (alignment <= size) {
-        /* Increase the alignment of start */
-        return alignment;
-    } else {
-        /* Find the largest page mask from size */
-        return 1ULL << (63 - clz64(size));
-    }
-}
-
 /* Unmap the whole range in the notifier's scope. */
 static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
 {
@@ -3499,13 +3484,14 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
 
     while (remain >= VTD_PAGE_SIZE) {
         IOMMUTLBEvent event;
-        uint64_t mask = get_naturally_aligned_size(start, remain, s->aw_bits);
+        uint64_t mask = dma_aligned_pow2_mask(start, end, s->aw_bits);
+        uint64_t size = mask + 1;
 
-        assert(mask);
+        assert(size);
 
         event.type = IOMMU_NOTIFIER_UNMAP;
         event.entry.iova = start;
-        event.entry.addr_mask = mask - 1;
+        event.entry.addr_mask = mask;
         event.entry.target_as = &address_space_memory;
         event.entry.perm = IOMMU_NONE;
         /* This field is meaningless for unmap */
@@ -3513,8 +3499,8 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
 
         memory_region_notify_iommu_one(n, &event);
 
-        start += mask;
-        remain -= mask;
+        start += size;
+        remain -= size;
     }
 
     assert(!remain);
diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c
index 588e6f9930..43349d6837 100644
--- a/hw/mips/gt64xxx_pci.c
+++ b/hw/mips/gt64xxx_pci.c
@@ -385,13 +385,13 @@ static void gt64120_writel(void *opaque, hwaddr addr,
 {
     GT64120State *s = opaque;
     PCIHostState *phb = PCI_HOST_BRIDGE(s);
-    uint32_t saddr;
+    uint32_t saddr = addr >> 2;
 
+    trace_gt64120_write(addr, val);
     if (!(s->regs[GT_CPU] & 0x00001000)) {
         val = bswap32(val);
     }
 
-    saddr = (addr & 0xfff) >> 2;
     switch (saddr) {
 
     /* CPU Configuration */
@@ -464,7 +464,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Read-only registers, do nothing */
         qemu_log_mask(LOG_GUEST_ERROR,
                       "gt64120: Read-only register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -474,7 +474,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Read-only registers, do nothing */
         qemu_log_mask(LOG_GUEST_ERROR,
                       "gt64120: Read-only register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -516,7 +516,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Not implemented */
         qemu_log_mask(LOG_UNIMP,
                       "gt64120: Unimplemented device register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -529,7 +529,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Read-only registers, do nothing */
         qemu_log_mask(LOG_GUEST_ERROR,
                       "gt64120: Read-only register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -566,7 +566,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Not implemented */
         qemu_log_mask(LOG_UNIMP,
                       "gt64120: Unimplemented DMA register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -579,7 +579,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* Not implemented */
         qemu_log_mask(LOG_UNIMP,
                       "gt64120: Unimplemented timer register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
 
@@ -622,8 +622,8 @@ static void gt64120_writel(void *opaque, hwaddr addr,
     case GT_PCI1_CFGDATA:
         /* not implemented */
         qemu_log_mask(LOG_UNIMP,
-                      "gt64120: Unimplemented timer register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "gt64120: Unimplemented PCI register write "
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
     case GT_PCI0_CFGADDR:
@@ -643,19 +643,19 @@ static void gt64120_writel(void *opaque, hwaddr addr,
         /* not really implemented */
         s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe));
         s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe);
-        trace_gt64120_write("INTRCAUSE", size, val);
+        trace_gt64120_write_intreg("INTRCAUSE", size, val);
         break;
     case GT_INTRMASK:
         s->regs[saddr] = val & 0x3c3ffffe;
-        trace_gt64120_write("INTRMASK", size, val);
+        trace_gt64120_write_intreg("INTRMASK", size, val);
         break;
     case GT_PCI0_ICMASK:
         s->regs[saddr] = val & 0x03fffffe;
-        trace_gt64120_write("ICMASK", size, val);
+        trace_gt64120_write_intreg("ICMASK", size, val);
         break;
     case GT_PCI0_SERR0MASK:
         s->regs[saddr] = val & 0x0000003f;
-        trace_gt64120_write("SERR0MASK", size, val);
+        trace_gt64120_write_intreg("SERR0MASK", size, val);
         break;
 
     /* Reserved when only PCI_0 is configured. */
@@ -683,7 +683,7 @@ static void gt64120_writel(void *opaque, hwaddr addr,
     default:
         qemu_log_mask(LOG_GUEST_ERROR,
                       "gt64120: Illegal register write "
-                      "reg:0x03%x size:%u value:0x%0*" PRIx64 "\n",
+                      "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n",
                       saddr << 2, size, size << 1, val);
         break;
     }
@@ -695,9 +695,8 @@ static uint64_t gt64120_readl(void *opaque,
     GT64120State *s = opaque;
     PCIHostState *phb = PCI_HOST_BRIDGE(s);
     uint32_t val;
-    uint32_t saddr;
+    uint32_t saddr = addr >> 2;
 
-    saddr = (addr & 0xfff) >> 2;
     switch (saddr) {
 
     /* CPU Configuration */
@@ -931,19 +930,19 @@ static uint64_t gt64120_readl(void *opaque,
     /* Interrupts */
     case GT_INTRCAUSE:
         val = s->regs[saddr];
-        trace_gt64120_read("INTRCAUSE", size, val);
+        trace_gt64120_read_intreg("INTRCAUSE", size, val);
         break;
     case GT_INTRMASK:
         val = s->regs[saddr];
-        trace_gt64120_read("INTRMASK", size, val);
+        trace_gt64120_read_intreg("INTRMASK", size, val);
         break;
     case GT_PCI0_ICMASK:
         val = s->regs[saddr];
-        trace_gt64120_read("ICMASK", size, val);
+        trace_gt64120_read_intreg("ICMASK", size, val);
         break;
     case GT_PCI0_SERR0MASK:
         val = s->regs[saddr];
-        trace_gt64120_read("SERR0MASK", size, val);
+        trace_gt64120_read_intreg("SERR0MASK", size, val);
         break;
 
     /* Reserved when only PCI_0 is configured. */
@@ -960,7 +959,7 @@ static uint64_t gt64120_readl(void *opaque,
         val = s->regs[saddr];
         qemu_log_mask(LOG_GUEST_ERROR,
                       "gt64120: Illegal register read "
-                      "reg:0x03%x size:%u value:0x%0*x\n",
+                      "reg:0x%03x size:%u value:0x%0*x\n",
                       saddr << 2, size, size << 1, val);
         break;
     }
@@ -968,6 +967,7 @@ static uint64_t gt64120_readl(void *opaque,
     if (!(s->regs[GT_CPU] & 0x00001000)) {
         val = bswap32(val);
     }
+    trace_gt64120_read(addr, val);
 
     return val;
 }
@@ -976,6 +976,10 @@ static const MemoryRegionOps isd_mem_ops = {
     .read = gt64120_readl,
     .write = gt64120_writel,
     .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
 };
 
 static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
@@ -1196,6 +1200,14 @@ static void gt64120_reset(DeviceState *dev)
     gt64120_pci_mapping(s);
 }
 
+static void gt64120_realize(DeviceState *dev, Error **errp)
+{
+    GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s,
+                          "gt64120-isd", 0x1000);
+}
+
 PCIBus *gt64120_register(qemu_irq *pic)
 {
     GT64120State *d;
@@ -1214,8 +1226,6 @@ PCIBus *gt64120_register(qemu_irq *pic)
                                      get_system_io(),
                                      PCI_DEVFN(18, 0), 4, TYPE_PCI_BUS);
     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
-    memory_region_init_io(&d->ISD_mem, OBJECT(dev), &isd_mem_ops, d,
-                          "isd-mem", 0x1000);
 
     pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci");
     return phb->bus;
@@ -1270,6 +1280,7 @@ static void gt64120_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
+    dc->realize = gt64120_realize;
     dc->reset = gt64120_reset;
     dc->vmsd = &vmstate_gt64120;
 }
diff --git a/hw/mips/trace-events b/hw/mips/trace-events
index 915139d981..13ee731a48 100644
--- a/hw/mips/trace-events
+++ b/hw/mips/trace-events
@@ -1,4 +1,6 @@
 # gt64xxx_pci.c
-gt64120_read(const char *regname, unsigned size, uint64_t value) "gt64120 read %s size:%u value:0x%08" PRIx64
-gt64120_write(const char *regname, unsigned size, uint64_t value) "gt64120 write %s size:%u value:0x%08" PRIx64
+gt64120_read(uint64_t addr, uint64_t value) "gt64120 read 0x%03"PRIx64" value:0x%08" PRIx64
+gt64120_write(uint64_t addr, uint64_t value) "gt64120 write 0x%03"PRIx64" value:0x%08" PRIx64
+gt64120_read_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 read %s size:%u value:0x%08" PRIx64
+gt64120_write_intreg(const char *regname, unsigned size, uint64_t value) "gt64120 write %s size:%u value:0x%08" PRIx64
 gt64120_isd_remap(uint64_t from_length, uint64_t from_addr, uint64_t to_length, uint64_t to_addr) "ISD: 0x%08" PRIx64 "@0x%08" PRIx64 " -> 0x%08" PRIx64 "@0x%08" PRIx64
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 00356cf12e..7a2b0d031a 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -65,6 +65,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_mft.c',
   'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
@@ -85,6 +86,7 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files(
 ))
 softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
 softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c'))
+softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-xramc.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
 softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c
new file mode 100644
index 0000000000..a30583a1b0
--- /dev/null
+++ b/hw/misc/npcm7xx_mft.c
@@ -0,0 +1,540 @@
+/*
+ * Nuvoton NPCM7xx MFT Module
+ *
+ * Copyright 2021 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/npcm7xx_mft.h"
+#include "hw/misc/npcm7xx_pwm.h"
+#include "hw/registerfields.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+/*
+ * Some of the registers can only accessed via 16-bit ops and some can only
+ * be accessed via 8-bit ops. However we mark all of them using REG16 to
+ * simplify implementation. npcm7xx_mft_check_mem_op checks the access length
+ * of memory operations.
+ */
+REG16(NPCM7XX_MFT_CNT1, 0x00);
+REG16(NPCM7XX_MFT_CRA, 0x02);
+REG16(NPCM7XX_MFT_CRB, 0x04);
+REG16(NPCM7XX_MFT_CNT2, 0x06);
+REG16(NPCM7XX_MFT_PRSC, 0x08);
+REG16(NPCM7XX_MFT_CKC, 0x0a);
+REG16(NPCM7XX_MFT_MCTRL, 0x0c);
+REG16(NPCM7XX_MFT_ICTRL, 0x0e);
+REG16(NPCM7XX_MFT_ICLR, 0x10);
+REG16(NPCM7XX_MFT_IEN, 0x12);
+REG16(NPCM7XX_MFT_CPA, 0x14);
+REG16(NPCM7XX_MFT_CPB, 0x16);
+REG16(NPCM7XX_MFT_CPCFG, 0x18);
+REG16(NPCM7XX_MFT_INASEL, 0x1a);
+REG16(NPCM7XX_MFT_INBSEL, 0x1c);
+
+/* Register Fields */
+#define NPCM7XX_MFT_CKC_C2CSEL          BIT(3)
+#define NPCM7XX_MFT_CKC_C1CSEL          BIT(0)
+
+#define NPCM7XX_MFT_MCTRL_TBEN          BIT(6)
+#define NPCM7XX_MFT_MCTRL_TAEN          BIT(5)
+#define NPCM7XX_MFT_MCTRL_TBEDG         BIT(4)
+#define NPCM7XX_MFT_MCTRL_TAEDG         BIT(3)
+#define NPCM7XX_MFT_MCTRL_MODE5         BIT(2)
+
+#define NPCM7XX_MFT_ICTRL_TFPND         BIT(5)
+#define NPCM7XX_MFT_ICTRL_TEPND         BIT(4)
+#define NPCM7XX_MFT_ICTRL_TDPND         BIT(3)
+#define NPCM7XX_MFT_ICTRL_TCPND         BIT(2)
+#define NPCM7XX_MFT_ICTRL_TBPND         BIT(1)
+#define NPCM7XX_MFT_ICTRL_TAPND         BIT(0)
+
+#define NPCM7XX_MFT_ICLR_TFCLR          BIT(5)
+#define NPCM7XX_MFT_ICLR_TECLR          BIT(4)
+#define NPCM7XX_MFT_ICLR_TDCLR          BIT(3)
+#define NPCM7XX_MFT_ICLR_TCCLR          BIT(2)
+#define NPCM7XX_MFT_ICLR_TBCLR          BIT(1)
+#define NPCM7XX_MFT_ICLR_TACLR          BIT(0)
+
+#define NPCM7XX_MFT_IEN_TFIEN           BIT(5)
+#define NPCM7XX_MFT_IEN_TEIEN           BIT(4)
+#define NPCM7XX_MFT_IEN_TDIEN           BIT(3)
+#define NPCM7XX_MFT_IEN_TCIEN           BIT(2)
+#define NPCM7XX_MFT_IEN_TBIEN           BIT(1)
+#define NPCM7XX_MFT_IEN_TAIEN           BIT(0)
+
+#define NPCM7XX_MFT_CPCFG_GET_B(rv)     extract8((rv), 4, 4)
+#define NPCM7XX_MFT_CPCFG_GET_A(rv)     extract8((rv), 0, 4)
+#define NPCM7XX_MFT_CPCFG_HIEN          BIT(3)
+#define NPCM7XX_MFT_CPCFG_EQEN          BIT(2)
+#define NPCM7XX_MFT_CPCFG_LOEN          BIT(1)
+#define NPCM7XX_MFT_CPCFG_CPSEL         BIT(0)
+
+#define NPCM7XX_MFT_INASEL_SELA         BIT(0)
+#define NPCM7XX_MFT_INBSEL_SELB         BIT(0)
+
+/* Max CNT values of the module. The CNT value is a countdown from it. */
+#define NPCM7XX_MFT_MAX_CNT             0xFFFF
+
+/* Each fan revolution should generated 2 pulses */
+#define NPCM7XX_MFT_PULSE_PER_REVOLUTION 2
+
+typedef enum NPCM7xxMFTCaptureState {
+    /* capture succeeded with a valid CNT value. */
+    NPCM7XX_CAPTURE_SUCCEED,
+    /* capture stopped prematurely due to reaching CPCFG condition. */
+    NPCM7XX_CAPTURE_COMPARE_HIT,
+    /* capture fails since it reaches underflow condition for CNT. */
+    NPCM7XX_CAPTURE_UNDERFLOW,
+} NPCM7xxMFTCaptureState;
+
+static void npcm7xx_mft_reset(NPCM7xxMFTState *s)
+{
+    int i;
+
+    /* Only registers PRSC ~ INBSEL need to be reset. */
+    for (i = R_NPCM7XX_MFT_PRSC; i <= R_NPCM7XX_MFT_INBSEL; ++i) {
+        s->regs[i] = 0;
+    }
+}
+
+static void npcm7xx_mft_clear_interrupt(NPCM7xxMFTState *s, uint8_t iclr)
+{
+    /*
+     * Clear bits in ICTRL where corresponding bits in iclr is 1.
+     * Both iclr and ictrl are 8-bit regs. (See npcm7xx_mft_check_mem_op)
+     */
+    s->regs[R_NPCM7XX_MFT_ICTRL] &= ~iclr;
+}
+
+/*
+ * If the CPCFG's condition should be triggered during count down from
+ * NPCM7XX_MFT_MAX_CNT to src if compared to tgt, return the count when
+ * the condition is triggered.
+ * Otherwise return -1.
+ * Since tgt is uint16_t it must always <= NPCM7XX_MFT_MAX_CNT.
+ */
+static int npcm7xx_mft_compare(int32_t src, uint16_t tgt, uint8_t cpcfg)
+{
+    if (cpcfg & NPCM7XX_MFT_CPCFG_HIEN) {
+        return NPCM7XX_MFT_MAX_CNT;
+    }
+    if ((cpcfg & NPCM7XX_MFT_CPCFG_EQEN) && (src <= tgt)) {
+        return tgt;
+    }
+    if ((cpcfg & NPCM7XX_MFT_CPCFG_LOEN) && (tgt > 0) && (src < tgt)) {
+        return tgt - 1;
+    }
+
+    return -1;
+}
+
+/* Compute CNT according to corresponding fan's RPM. */
+static NPCM7xxMFTCaptureState npcm7xx_mft_compute_cnt(
+    Clock *clock, uint32_t max_rpm, uint32_t duty, uint16_t tgt,
+    uint8_t cpcfg, uint16_t *cnt)
+{
+    uint32_t rpm = (uint64_t)max_rpm * (uint64_t)duty / NPCM7XX_PWM_MAX_DUTY;
+    int32_t count;
+    int stopped;
+    NPCM7xxMFTCaptureState state;
+
+    if (rpm == 0) {
+        /*
+         * If RPM = 0, capture won't happen. CNT will continue count down.
+         * So it's effective equivalent to have a cnt > NPCM7XX_MFT_MAX_CNT
+         */
+        count = NPCM7XX_MFT_MAX_CNT + 1;
+    } else {
+        /*
+         * RPM = revolution/min. The time for one revlution (in ns) is
+         * MINUTE_TO_NANOSECOND / RPM.
+         */
+        count = clock_ns_to_ticks(clock, (60 * NANOSECONDS_PER_SECOND) /
+            (rpm * NPCM7XX_MFT_PULSE_PER_REVOLUTION));
+    }
+
+    if (count > NPCM7XX_MFT_MAX_CNT) {
+        count = -1;
+    } else {
+        /* The CNT is a countdown value from NPCM7XX_MFT_MAX_CNT. */
+        count = NPCM7XX_MFT_MAX_CNT - count;
+    }
+    stopped = npcm7xx_mft_compare(count, tgt, cpcfg);
+    if (stopped == -1) {
+        if (count == -1) {
+            /* Underflow */
+            state = NPCM7XX_CAPTURE_UNDERFLOW;
+        } else {
+            state = NPCM7XX_CAPTURE_SUCCEED;
+        }
+    } else {
+        count = stopped;
+        state = NPCM7XX_CAPTURE_COMPARE_HIT;
+    }
+
+    if (count != -1) {
+        *cnt = count;
+    }
+    trace_npcm7xx_mft_rpm(clock->canonical_path, clock_get_hz(clock),
+                          state, count, rpm, duty);
+    return state;
+}
+
+/*
+ * Capture Fan RPM and update CNT and CR registers accordingly.
+ * Raise IRQ if certain contidions are met in IEN.
+ */
+static void npcm7xx_mft_capture(NPCM7xxMFTState *s)
+{
+    int irq_level = 0;
+    NPCM7xxMFTCaptureState state;
+    int sel;
+    uint8_t cpcfg;
+
+    /*
+     * If not mode 5, the behavior is undefined. We just do nothing in this
+     * case.
+     */
+    if (!(s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_MODE5)) {
+        return;
+    }
+
+    /* Capture input A. */
+    if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TAEN &&
+        s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
+        sel = s->regs[R_NPCM7XX_MFT_INASEL] & NPCM7XX_MFT_INASEL_SELA;
+        cpcfg = NPCM7XX_MFT_CPCFG_GET_A(s->regs[R_NPCM7XX_MFT_CPCFG]);
+        state = npcm7xx_mft_compute_cnt(s->clock_1,
+                                        sel ? s->max_rpm[2] : s->max_rpm[0],
+                                        sel ? s->duty[2] : s->duty[0],
+                                        s->regs[R_NPCM7XX_MFT_CPA],
+                                        cpcfg,
+                                        &s->regs[R_NPCM7XX_MFT_CNT1]);
+        switch (state) {
+        case NPCM7XX_CAPTURE_SUCCEED:
+            /* Interrupt on input capture on TAn transition - TAPND */
+            s->regs[R_NPCM7XX_MFT_CRA] = s->regs[R_NPCM7XX_MFT_CNT1];
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TAPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TAIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        case NPCM7XX_CAPTURE_COMPARE_HIT:
+            /* Compare Hit - TEPND */
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TEPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TEIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        case NPCM7XX_CAPTURE_UNDERFLOW:
+            /* Underflow - TCPND */
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TCPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TCIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        default:
+            g_assert_not_reached();
+        }
+    }
+
+    /* Capture input B. */
+    if (s->regs[R_NPCM7XX_MFT_MCTRL] & NPCM7XX_MFT_MCTRL_TBEN &&
+        s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
+        sel = s->regs[R_NPCM7XX_MFT_INBSEL] & NPCM7XX_MFT_INBSEL_SELB;
+        cpcfg = NPCM7XX_MFT_CPCFG_GET_B(s->regs[R_NPCM7XX_MFT_CPCFG]);
+        state = npcm7xx_mft_compute_cnt(s->clock_2,
+                                        sel ? s->max_rpm[3] : s->max_rpm[1],
+                                        sel ? s->duty[3] : s->duty[1],
+                                        s->regs[R_NPCM7XX_MFT_CPB],
+                                        cpcfg,
+                                        &s->regs[R_NPCM7XX_MFT_CNT2]);
+        switch (state) {
+        case NPCM7XX_CAPTURE_SUCCEED:
+            /* Interrupt on input capture on TBn transition - TBPND */
+            s->regs[R_NPCM7XX_MFT_CRB] = s->regs[R_NPCM7XX_MFT_CNT2];
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TBPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TBIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        case NPCM7XX_CAPTURE_COMPARE_HIT:
+            /* Compare Hit - TFPND */
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TFPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TFIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        case NPCM7XX_CAPTURE_UNDERFLOW:
+            /* Underflow - TDPND */
+            s->regs[R_NPCM7XX_MFT_ICTRL] |= NPCM7XX_MFT_ICTRL_TDPND;
+            if (s->regs[R_NPCM7XX_MFT_IEN] & NPCM7XX_MFT_IEN_TDIEN) {
+                irq_level = 1;
+            }
+            break;
+
+        default:
+            g_assert_not_reached();
+        }
+    }
+
+    trace_npcm7xx_mft_capture(DEVICE(s)->canonical_path, irq_level);
+    qemu_set_irq(s->irq, irq_level);
+}
+
+/* Update clock for counters. */
+static void npcm7xx_mft_update_clock(void *opaque, ClockEvent event)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
+    uint64_t prescaled_clock_period;
+
+    prescaled_clock_period = clock_get(s->clock_in) *
+        (s->regs[R_NPCM7XX_MFT_PRSC] + 1ULL);
+    trace_npcm7xx_mft_update_clock(s->clock_in->canonical_path,
+                                   s->regs[R_NPCM7XX_MFT_CKC],
+                                   clock_get(s->clock_in),
+                                   prescaled_clock_period);
+    /* Update clock 1 */
+    if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C1CSEL) {
+        /* Clock is prescaled. */
+        clock_update(s->clock_1, prescaled_clock_period);
+    } else {
+        /* Clock stopped. */
+        clock_update(s->clock_1, 0);
+    }
+    /* Update clock 2 */
+    if (s->regs[R_NPCM7XX_MFT_CKC] & NPCM7XX_MFT_CKC_C2CSEL) {
+        /* Clock is prescaled. */
+        clock_update(s->clock_2, prescaled_clock_period);
+    } else {
+        /* Clock stopped. */
+        clock_update(s->clock_2, 0);
+    }
+
+    npcm7xx_mft_capture(s);
+}
+
+static uint64_t npcm7xx_mft_read(void *opaque, hwaddr offset, unsigned size)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
+    uint16_t value = 0;
+
+    switch (offset) {
+    case A_NPCM7XX_MFT_ICLR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: register @ 0x%04" HWADDR_PRIx " is write-only\n",
+                      __func__, offset);
+        break;
+
+    default:
+        value = s->regs[offset / 2];
+    }
+
+    trace_npcm7xx_mft_read(DEVICE(s)->canonical_path, offset, value);
+    return value;
+}
+
+static void npcm7xx_mft_write(void *opaque, hwaddr offset,
+                              uint64_t v, unsigned size)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
+
+    trace_npcm7xx_mft_write(DEVICE(s)->canonical_path, offset, v);
+    switch (offset) {
+    case A_NPCM7XX_MFT_ICLR:
+        npcm7xx_mft_clear_interrupt(s, v);
+        break;
+
+    case A_NPCM7XX_MFT_CKC:
+    case A_NPCM7XX_MFT_PRSC:
+        s->regs[offset / 2] = v;
+        npcm7xx_mft_update_clock(s, ClockUpdate);
+        break;
+
+    default:
+        s->regs[offset / 2] = v;
+        npcm7xx_mft_capture(s);
+        break;
+    }
+}
+
+static bool npcm7xx_mft_check_mem_op(void *opaque, hwaddr offset,
+                                     unsigned size, bool is_write,
+                                     MemTxAttrs attrs)
+{
+    switch (offset) {
+    /* 16-bit registers. Must be accessed with 16-bit read/write.*/
+    case A_NPCM7XX_MFT_CNT1:
+    case A_NPCM7XX_MFT_CRA:
+    case A_NPCM7XX_MFT_CRB:
+    case A_NPCM7XX_MFT_CNT2:
+    case A_NPCM7XX_MFT_CPA:
+    case A_NPCM7XX_MFT_CPB:
+        return size == 2;
+
+    /* 8-bit registers. Must be accessed with 8-bit read/write.*/
+    case A_NPCM7XX_MFT_PRSC:
+    case A_NPCM7XX_MFT_CKC:
+    case A_NPCM7XX_MFT_MCTRL:
+    case A_NPCM7XX_MFT_ICTRL:
+    case A_NPCM7XX_MFT_ICLR:
+    case A_NPCM7XX_MFT_IEN:
+    case A_NPCM7XX_MFT_CPCFG:
+    case A_NPCM7XX_MFT_INASEL:
+    case A_NPCM7XX_MFT_INBSEL:
+        return size == 1;
+
+    default:
+        /* Invalid registers. */
+        return false;
+    }
+}
+
+static void npcm7xx_mft_get_max_rpm(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
+{
+    visit_type_uint32(v, name, (uint32_t *)opaque, errp);
+}
+
+static void npcm7xx_mft_set_max_rpm(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
+    uint32_t *max_rpm = opaque;
+    uint32_t value;
+
+    if (!visit_type_uint32(v, name, &value, errp)) {
+        return;
+    }
+
+    *max_rpm = value;
+    npcm7xx_mft_capture(s);
+}
+
+static void npcm7xx_mft_duty_handler(void *opaque, int n, int value)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(opaque);
+
+    trace_npcm7xx_mft_set_duty(DEVICE(s)->canonical_path, n, value);
+    s->duty[n] = value;
+    npcm7xx_mft_capture(s);
+}
+
+static const struct MemoryRegionOps npcm7xx_mft_ops = {
+    .read       = npcm7xx_mft_read,
+    .write      = npcm7xx_mft_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 1,
+        .max_access_size        = 2,
+        .unaligned              = false,
+        .accepts                = npcm7xx_mft_check_mem_op,
+    },
+};
+
+static void npcm7xx_mft_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
+
+    npcm7xx_mft_reset(s);
+}
+
+static void npcm7xx_mft_hold_reset(Object *obj)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
+
+    qemu_irq_lower(s->irq);
+}
+
+static void npcm7xx_mft_init(Object *obj)
+{
+    NPCM7xxMFTState *s = NPCM7XX_MFT(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    DeviceState *dev = DEVICE(obj);
+
+    memory_region_init_io(&s->iomem, obj, &npcm7xx_mft_ops, s,
+                          TYPE_NPCM7XX_MFT, 4 * KiB);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+    s->clock_in = qdev_init_clock_in(dev, "clock-in", npcm7xx_mft_update_clock,
+                                     s, ClockUpdate);
+    s->clock_1 = qdev_init_clock_out(dev, "clock1");
+    s->clock_2 = qdev_init_clock_out(dev, "clock2");
+
+    for (int i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
+        object_property_add(obj, "max_rpm[*]", "uint32",
+                            npcm7xx_mft_get_max_rpm,
+                            npcm7xx_mft_set_max_rpm,
+                            NULL, &s->max_rpm[i]);
+    }
+    qdev_init_gpio_in_named(dev, npcm7xx_mft_duty_handler, "duty",
+                            NPCM7XX_MFT_FANIN_COUNT);
+}
+
+static const VMStateDescription vmstate_npcm7xx_mft = {
+    .name = "npcm7xx-mft-module",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_CLOCK(clock_in, NPCM7xxMFTState),
+        VMSTATE_CLOCK(clock_1, NPCM7xxMFTState),
+        VMSTATE_CLOCK(clock_2, NPCM7xxMFTState),
+        VMSTATE_UINT16_ARRAY(regs, NPCM7xxMFTState, NPCM7XX_MFT_NR_REGS),
+        VMSTATE_UINT32_ARRAY(max_rpm, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
+        VMSTATE_UINT32_ARRAY(duty, NPCM7xxMFTState, NPCM7XX_MFT_FANIN_COUNT),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_mft_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx MFT Controller";
+    dc->vmsd = &vmstate_npcm7xx_mft;
+    rc->phases.enter = npcm7xx_mft_enter_reset;
+    rc->phases.hold = npcm7xx_mft_hold_reset;
+}
+
+static const TypeInfo npcm7xx_mft_info = {
+    .name               = TYPE_NPCM7XX_MFT,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxMFTState),
+    .class_init         = npcm7xx_mft_class_init,
+    .instance_init      = npcm7xx_mft_init,
+};
+
+static void npcm7xx_mft_register_type(void)
+{
+    type_register_static(&npcm7xx_mft_info);
+}
+type_init(npcm7xx_mft_register_type);
diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
index ce192bb274..2be5bd25c6 100644
--- a/hw/misc/npcm7xx_pwm.c
+++ b/hw/misc/npcm7xx_pwm.c
@@ -139,6 +139,7 @@ static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p)
         trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path,
                                       p->index, p->duty, duty);
         p->duty = duty;
+        qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
     }
 }
 
@@ -483,6 +484,7 @@ static void npcm7xx_pwm_init(Object *obj)
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
     int i;
 
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->pwm) != NPCM7XX_PWM_PER_MODULE);
     for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
         NPCM7xxPWM *p = &s->pwm[i];
         p->module = s;
@@ -501,6 +503,8 @@ static void npcm7xx_pwm_init(Object *obj)
         object_property_add_uint32_ptr(obj, "duty[*]",
                 &s->pwm[i].duty, OBJ_PROP_FLAG_READ);
     }
+    qdev_init_gpio_out_named(DEVICE(s), s->duty_gpio_out,
+                             "duty-gpio-out", NPCM7XX_PWM_PER_MODULE);
 }
 
 static const VMStateDescription vmstate_npcm7xx_pwm = {
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index cae005549e..b87d0b4c90 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -116,6 +116,14 @@ npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " valu
 npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
 npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
 
+# npcm7xx_mft.c
+npcm7xx_mft_read(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
+npcm7xx_mft_write(const char *name, uint64_t offset, uint16_t value) "%s: offset: 0x%04" PRIx64 " value: 0x%04" PRIx16
+npcm7xx_mft_rpm(const char *clock, uint32_t clock_hz, int state, int32_t cnt, uint32_t rpm, uint32_t duty) " fan clk: %s clock_hz: %" PRIu32 ", state: %d, cnt: %" PRIi32 ", rpm: %" PRIu32 ", duty: %" PRIu32
+npcm7xx_mft_capture(const char *name, int irq_level) "%s: level: %d"
+npcm7xx_mft_update_clock(const char *name, uint16_t sel, uint64_t clock_period, uint64_t prescaled_clock_period) "%s: sel: 0x%02" PRIx16 ", period: %" PRIu64 ", prescaled: %" PRIu64
+npcm7xx_mft_set_duty(const char *name, int n, int value) "%s[%d]: %d"
+
 # npcm7xx_rng.c
 npcm7xx_rng_read(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u"
 npcm7xx_rng_write(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u"
diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c
new file mode 100644
index 0000000000..e5b719a0ed
--- /dev/null
+++ b/hw/misc/xlnx-versal-xramc.c
@@ -0,0 +1,253 @@
+/*
+ * QEMU model of the Xilinx XRAM Controller.
+ *
+ * Copyright (c) 2021 Xilinx Inc.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "hw/qdev-properties.h"
+#include "hw/irq.h"
+#include "hw/misc/xlnx-versal-xramc.h"
+
+#ifndef XLNX_XRAM_CTRL_ERR_DEBUG
+#define XLNX_XRAM_CTRL_ERR_DEBUG 0
+#endif
+
+static void xram_update_irq(XlnxXramCtrl *s)
+{
+    bool pending = s->regs[R_XRAM_ISR] & ~s->regs[R_XRAM_IMR];
+    qemu_set_irq(s->irq, pending);
+}
+
+static void xram_isr_postw(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(reg->opaque);
+    xram_update_irq(s);
+}
+
+static uint64_t xram_ien_prew(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(reg->opaque);
+    uint32_t val = val64;
+
+    s->regs[R_XRAM_IMR] &= ~val;
+    xram_update_irq(s);
+    return 0;
+}
+
+static uint64_t xram_ids_prew(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(reg->opaque);
+    uint32_t val = val64;
+
+    s->regs[R_XRAM_IMR] |= val;
+    xram_update_irq(s);
+    return 0;
+}
+
+static const RegisterAccessInfo xram_ctrl_regs_info[] = {
+    {   .name = "XRAM_ERR_CTRL",  .addr = A_XRAM_ERR_CTRL,
+        .reset = 0xf,
+        .rsvd = 0xfffffff0,
+    },{ .name = "XRAM_ISR",  .addr = A_XRAM_ISR,
+        .rsvd = 0xfffff800,
+        .w1c = 0x7ff,
+        .post_write = xram_isr_postw,
+    },{ .name = "XRAM_IMR",  .addr = A_XRAM_IMR,
+        .reset = 0x7ff,
+        .rsvd = 0xfffff800,
+        .ro = 0x7ff,
+    },{ .name = "XRAM_IEN",  .addr = A_XRAM_IEN,
+        .rsvd = 0xfffff800,
+        .pre_write = xram_ien_prew,
+    },{ .name = "XRAM_IDS",  .addr = A_XRAM_IDS,
+        .rsvd = 0xfffff800,
+        .pre_write = xram_ids_prew,
+    },{ .name = "XRAM_ECC_CNTL",  .addr = A_XRAM_ECC_CNTL,
+        .rsvd = 0xfffffff8,
+    },{ .name = "XRAM_CLR_EXE",  .addr = A_XRAM_CLR_EXE,
+        .rsvd = 0xffffff00,
+    },{ .name = "XRAM_CE_FFA",  .addr = A_XRAM_CE_FFA,
+        .rsvd = 0xfff00000,
+        .ro = 0xfffff,
+    },{ .name = "XRAM_CE_FFD0",  .addr = A_XRAM_CE_FFD0,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_CE_FFD1",  .addr = A_XRAM_CE_FFD1,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_CE_FFD2",  .addr = A_XRAM_CE_FFD2,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_CE_FFD3",  .addr = A_XRAM_CE_FFD3,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_CE_FFE",  .addr = A_XRAM_CE_FFE,
+        .rsvd = 0xffff0000,
+        .ro = 0xffff,
+    },{ .name = "XRAM_UE_FFA",  .addr = A_XRAM_UE_FFA,
+        .rsvd = 0xfff00000,
+        .ro = 0xfffff,
+    },{ .name = "XRAM_UE_FFD0",  .addr = A_XRAM_UE_FFD0,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_UE_FFD1",  .addr = A_XRAM_UE_FFD1,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_UE_FFD2",  .addr = A_XRAM_UE_FFD2,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_UE_FFD3",  .addr = A_XRAM_UE_FFD3,
+        .ro = 0xffffffff,
+    },{ .name = "XRAM_UE_FFE",  .addr = A_XRAM_UE_FFE,
+        .rsvd = 0xffff0000,
+        .ro = 0xffff,
+    },{ .name = "XRAM_FI_D0",  .addr = A_XRAM_FI_D0,
+    },{ .name = "XRAM_FI_D1",  .addr = A_XRAM_FI_D1,
+    },{ .name = "XRAM_FI_D2",  .addr = A_XRAM_FI_D2,
+    },{ .name = "XRAM_FI_D3",  .addr = A_XRAM_FI_D3,
+    },{ .name = "XRAM_FI_SY",  .addr = A_XRAM_FI_SY,
+        .rsvd = 0xffff0000,
+    },{ .name = "XRAM_RMW_UE_FFA",  .addr = A_XRAM_RMW_UE_FFA,
+        .rsvd = 0xfff00000,
+        .ro = 0xfffff,
+    },{ .name = "XRAM_FI_CNTR",  .addr = A_XRAM_FI_CNTR,
+        .rsvd = 0xff000000,
+    },{ .name = "XRAM_IMP",  .addr = A_XRAM_IMP,
+        .reset = 0x4,
+        .rsvd = 0xfffffff0,
+        .ro = 0xf,
+    },{ .name = "XRAM_PRDY_DBG",  .addr = A_XRAM_PRDY_DBG,
+        .reset = 0xffff,
+        .rsvd = 0xffff0000,
+        .ro = 0xffff,
+    },{ .name = "XRAM_SAFETY_CHK",  .addr = A_XRAM_SAFETY_CHK,
+    }
+};
+
+static void xram_ctrl_reset_enter(Object *obj, ResetType type)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj);
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
+        register_reset(&s->regs_info[i]);
+    }
+
+    ARRAY_FIELD_DP32(s->regs, XRAM_IMP, SIZE, s->cfg.encoded_size);
+}
+
+static void xram_ctrl_reset_hold(Object *obj)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj);
+
+    xram_update_irq(s);
+}
+
+static const MemoryRegionOps xram_ctrl_ops = {
+    .read = register_read_memory,
+    .write = register_write_memory,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void xram_ctrl_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(dev);
+
+    switch (s->cfg.size) {
+    case 64 * KiB:
+        s->cfg.encoded_size = 0;
+        break;
+    case 128 * KiB:
+        s->cfg.encoded_size = 1;
+        break;
+    case 256 * KiB:
+        s->cfg.encoded_size = 2;
+        break;
+    case 512 * KiB:
+        s->cfg.encoded_size = 3;
+        break;
+    case 1 * MiB:
+        s->cfg.encoded_size = 4;
+        break;
+    default:
+        error_setg(errp, "Unsupported XRAM size %" PRId64, s->cfg.size);
+        return;
+    }
+
+    memory_region_init_ram(&s->ram, OBJECT(s),
+                           object_get_canonical_path_component(OBJECT(s)),
+                           s->cfg.size, &error_fatal);
+    sysbus_init_mmio(sbd, &s->ram);
+}
+
+static void xram_ctrl_init(Object *obj)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+    s->reg_array =
+        register_init_block32(DEVICE(obj), xram_ctrl_regs_info,
+                              ARRAY_SIZE(xram_ctrl_regs_info),
+                              s->regs_info, s->regs,
+                              &xram_ctrl_ops,
+                              XLNX_XRAM_CTRL_ERR_DEBUG,
+                              XRAM_CTRL_R_MAX * 4);
+    sysbus_init_mmio(sbd, &s->reg_array->mem);
+    sysbus_init_irq(sbd, &s->irq);
+}
+
+static void xram_ctrl_finalize(Object *obj)
+{
+    XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj);
+    register_finalize_block(s->reg_array);
+}
+
+static const VMStateDescription vmstate_xram_ctrl = {
+    .name = TYPE_XLNX_XRAM_CTRL,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, XlnxXramCtrl, XRAM_CTRL_R_MAX),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property xram_ctrl_properties[] = {
+    DEFINE_PROP_UINT64("size", XlnxXramCtrl, cfg.size, 1 * MiB),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xram_ctrl_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = xram_ctrl_realize;
+    dc->vmsd = &vmstate_xram_ctrl;
+    device_class_set_props(dc, xram_ctrl_properties);
+
+    rc->phases.enter = xram_ctrl_reset_enter;
+    rc->phases.hold = xram_ctrl_reset_hold;
+}
+
+static const TypeInfo xram_ctrl_info = {
+    .name              = TYPE_XLNX_XRAM_CTRL,
+    .parent            = TYPE_SYS_BUS_DEVICE,
+    .instance_size     = sizeof(XlnxXramCtrl),
+    .class_init        = xram_ctrl_class_init,
+    .instance_init     = xram_ctrl_init,
+    .instance_finalize = xram_ctrl_finalize,
+};
+
+static void xram_ctrl_register_types(void)
+{
+    type_register_static(&xram_ctrl_info);
+}
+
+type_init(xram_ctrl_register_types)
diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c
index bf91803d65..ff611f18fb 100644
--- a/hw/net/allwinner-sun8i-emac.c
+++ b/hw/net/allwinner-sun8i-emac.c
@@ -339,35 +339,40 @@ static void allwinner_sun8i_emac_update_irq(AwSun8iEmacState *s)
     qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0);
 }
 
-static uint32_t allwinner_sun8i_emac_next_desc(AwSun8iEmacState *s,
-                                               FrameDescriptor *desc,
-                                               size_t min_size)
+static bool allwinner_sun8i_emac_desc_owned(FrameDescriptor *desc,
+                                            size_t min_buf_size)
 {
-    uint32_t paddr = desc->next;
+    return (desc->status & DESC_STATUS_CTL) && (min_buf_size == 0 ||
+           (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_buf_size);
+}
 
-    dma_memory_read(&s->dma_as, paddr, desc, sizeof(*desc));
+static void allwinner_sun8i_emac_get_desc(AwSun8iEmacState *s,
+                                          FrameDescriptor *desc,
+                                          uint32_t phys_addr)
+{
+    dma_memory_read(&s->dma_as, phys_addr, desc, sizeof(*desc));
+}
 
-    if ((desc->status & DESC_STATUS_CTL) &&
-        (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
-        return paddr;
-    } else {
-        return 0;
-    }
+static uint32_t allwinner_sun8i_emac_next_desc(AwSun8iEmacState *s,
+                                               FrameDescriptor *desc)
+{
+    const uint32_t nxt = desc->next;
+    allwinner_sun8i_emac_get_desc(s, desc, nxt);
+    return nxt;
 }
 
-static uint32_t allwinner_sun8i_emac_get_desc(AwSun8iEmacState *s,
-                                              FrameDescriptor *desc,
-                                              uint32_t start_addr,
-                                              size_t min_size)
+static uint32_t allwinner_sun8i_emac_find_desc(AwSun8iEmacState *s,
+                                               FrameDescriptor *desc,
+                                               uint32_t start_addr,
+                                               size_t min_size)
 {
     uint32_t desc_addr = start_addr;
 
     /* Note that the list is a cycle. Last entry points back to the head. */
     while (desc_addr != 0) {
-        dma_memory_read(&s->dma_as, desc_addr, desc, sizeof(*desc));
+        allwinner_sun8i_emac_get_desc(s, desc, desc_addr);
 
-        if ((desc->status & DESC_STATUS_CTL) &&
-            (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
+        if (allwinner_sun8i_emac_desc_owned(desc, min_size)) {
             return desc_addr;
         } else if (desc->next == start_addr) {
             break;
@@ -383,14 +388,14 @@ static uint32_t allwinner_sun8i_emac_rx_desc(AwSun8iEmacState *s,
                                              FrameDescriptor *desc,
                                              size_t min_size)
 {
-    return allwinner_sun8i_emac_get_desc(s, desc, s->rx_desc_curr, min_size);
+    return allwinner_sun8i_emac_find_desc(s, desc, s->rx_desc_curr, min_size);
 }
 
 static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s,
-                                             FrameDescriptor *desc,
-                                             size_t min_size)
+                                             FrameDescriptor *desc)
 {
-    return allwinner_sun8i_emac_get_desc(s, desc, s->tx_desc_head, min_size);
+    allwinner_sun8i_emac_get_desc(s, desc, s->tx_desc_curr);
+    return s->tx_desc_curr;
 }
 
 static void allwinner_sun8i_emac_flush_desc(AwSun8iEmacState *s,
@@ -470,7 +475,8 @@ static ssize_t allwinner_sun8i_emac_receive(NetClientState *nc,
         bytes_left -= desc_bytes;
 
         /* Move to the next descriptor */
-        s->rx_desc_curr = allwinner_sun8i_emac_next_desc(s, &desc, 64);
+        s->rx_desc_curr = allwinner_sun8i_emac_find_desc(s, &desc, desc.next,
+                                                         AW_SUN8I_EMAC_MIN_PKT_SZ);
         if (!s->rx_desc_curr) {
             /* Not enough buffer space available */
             s->int_sta |= INT_STA_RX_BUF_UA;
@@ -495,10 +501,10 @@ static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s)
     size_t transmitted = 0;
     static uint8_t packet_buf[2048];
 
-    s->tx_desc_curr = allwinner_sun8i_emac_tx_desc(s, &desc, 0);
+    s->tx_desc_curr = allwinner_sun8i_emac_tx_desc(s, &desc);
 
     /* Read all transmit descriptors */
-    while (s->tx_desc_curr != 0) {
+    while (allwinner_sun8i_emac_desc_owned(&desc, 0)) {
 
         /* Read from physical memory into packet buffer */
         bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
@@ -524,7 +530,7 @@ static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s)
             packet_bytes = 0;
             transmitted++;
         }
-        s->tx_desc_curr = allwinner_sun8i_emac_next_desc(s, &desc, 0);
+        s->tx_desc_curr = allwinner_sun8i_emac_next_desc(s, &desc);
     }
 
     /* Raise transmit completed interrupt */
diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c
index 8dbe6ac651..f959cb9d60 100644
--- a/hw/timer/sse-timer.c
+++ b/hw/timer/sse-timer.c
@@ -415,6 +415,7 @@ static void sse_timer_realize(DeviceState *dev, Error **errp)
 
     if (!s->counter) {
         error_setg(errp, "counter property was not set");
+        return;
     }
 
     s->counter_notifier.notify = sse_timer_counter_callback;
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index c2883a2f6c..1b23e8e18c 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -155,6 +155,7 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
                                       hwaddr virt_end)
 {
     IOMMUTLBEvent event;
+    uint64_t delta = virt_end - virt_start;
 
     if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) {
         return;
@@ -164,12 +165,24 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
 
     event.type = IOMMU_NOTIFIER_UNMAP;
     event.entry.target_as = &address_space_memory;
-    event.entry.addr_mask = virt_end - virt_start;
-    event.entry.iova = virt_start;
     event.entry.perm = IOMMU_NONE;
     event.entry.translated_addr = 0;
+    event.entry.addr_mask = delta;
+    event.entry.iova = virt_start;
 
-    memory_region_notify_iommu(mr, 0, event);
+    if (delta == UINT64_MAX) {
+        memory_region_notify_iommu(mr, 0, event);
+    }
+
+
+    while (virt_start != virt_end + 1) {
+        uint64_t mask = dma_aligned_pow2_mask(virt_start, virt_end, 64);
+
+        event.entry.addr_mask = mask;
+        event.entry.iova = virt_start;
+        memory_region_notify_iommu(mr, 0, event);
+        virt_start += mask + 1;
+    }
 }
 
 static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value,