diff options
39 files changed, 2232 insertions, 934 deletions
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f88a523..37b0a18 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2068,6 +2068,8 @@ static int kvm_init(MachineState *ms) "kvm-type", &error_abort); type = mc->kvm_type(ms, kvm_type); + } else if (mc->kvm_type) { + type = mc->kvm_type(ms, NULL); } do { diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index f9fb922..d3cf2d9 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -45,6 +45,7 @@ Supported devices * Pulse Width Modulation (PWM) * SMBus controller (SMBF) * Ethernet controller (EMC) + * Tachometer Missing devices --------------- @@ -63,7 +64,6 @@ Missing devices * Peripheral SPI controller (PSPI) * SD/MMC host * PECI interface - * Tachometer * PCI and PCIe root complex and bridges * VDM and MCTP support * Serial I/O expansion diff --git a/docs/system/arm/xlnx-versal-virt.rst b/docs/system/arm/xlnx-versal-virt.rst index 2602d0f..27f7350 100644 --- a/docs/system/arm/xlnx-versal-virt.rst +++ b/docs/system/arm/xlnx-versal-virt.rst @@ -30,6 +30,7 @@ Implemented devices: - 8 ADMA (Xilinx zDMA) channels - 2 SD Controllers - OCM (256KB of On Chip Memory) +- XRAM (4MB of on chip Accelerator RAM) - DDR memory QEMU does not yet model any other devices, including the PL and the AI Engine. diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 9bd1e83..495b0f8 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 fbf6ce8..e22fe4b 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 405d5c5..84d2c62 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 55147f2..2d75b31 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 bd1f970..3b87324 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 a335ee8..b79a91a 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 c08bf11..aa2bbd1 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 628e77e..7960969 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 02b0d45..4bf15c1 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 36ba791..877419a 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 dfff994..2887ce4 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 c64eebc..0000000 --- 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 b4f5094..6be8f32 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/misc/meson.build b/hw/misc/meson.build index 00356cf..7a2b0d0 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 0000000..a30583a --- /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 ce192bb..2be5bd2 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 cae0055..b87d0b4 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 0000000..e5b719a --- /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 bf91803..ff611f1 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 8dbe6ac..f959cb9 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 c2883a2..1b23e8e 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, diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index d32849a..61ecc57 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -18,12 +18,14 @@ #include "hw/boards.h" #include "hw/adc/npcm7xx_adc.h" +#include "hw/core/split-irq.h" #include "hw/cpu/a9mpcore.h" #include "hw/gpio/npcm7xx_gpio.h" #include "hw/i2c/npcm7xx_smbus.h" #include "hw/mem/npcm7xx_mc.h" #include "hw/misc/npcm7xx_clk.h" #include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm7xx_mft.h" #include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_rng.h" #include "hw/net/npcm7xx_emc.h" @@ -47,8 +49,16 @@ #define NPCM7XX_GIC_CPU_IF_ADDR (0xf03fe100) /* GIC within A9 */ #define NPCM7XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */ +#define NPCM7XX_NR_PWM_MODULES 2 + typedef struct NPCM7xxMachine { MachineState parent; + /* + * PWM fan splitter. each splitter connects to one PWM output and + * multiple MFT inputs. + */ + SplitIRQ fan_splitter[NPCM7XX_NR_PWM_MODULES * + NPCM7XX_PWM_PER_MODULE]; } NPCM7xxMachine; #define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx") @@ -81,7 +91,8 @@ typedef struct NPCM7xxState { NPCM7xxCLKState clk; NPCM7xxTimerCtrlState tim[3]; NPCM7xxADCState adc; - NPCM7xxPWMState pwm[2]; + NPCM7xxPWMState pwm[NPCM7XX_NR_PWM_MODULES]; + NPCM7xxMFTState mft[8]; NPCM7xxOTPState key_storage; NPCM7xxOTPState fuse_array; NPCM7xxMCState mc; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 2b76885..22a8fa5 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -14,6 +14,7 @@ #include "hw/sysbus.h" #include "hw/arm/boot.h" +#include "hw/or-irq.h" #include "hw/sd/sdhci.h" #include "hw/intc/arm_gicv3.h" #include "hw/char/pl011.h" @@ -22,6 +23,7 @@ #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "qom/object.h" #include "hw/usb/xlnx-usb-subsystem.h" +#include "hw/misc/xlnx-versal-xramc.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -31,6 +33,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) #define XLNX_VERSAL_NR_GEMS 2 #define XLNX_VERSAL_NR_ADMAS 8 #define XLNX_VERSAL_NR_SDS 2 +#define XLNX_VERSAL_NR_XRAM 4 #define XLNX_VERSAL_NR_IRQS 192 struct Versal { @@ -62,6 +65,11 @@ struct Versal { XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; VersalUsb2 usb; } iou; + + struct { + qemu_or_irq irq_orgate; + XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM]; + } xram; } lpd; /* The Platform Management Controller subsystem. */ @@ -96,6 +104,7 @@ struct Versal { #define VERSAL_GEM1_IRQ_0 58 #define VERSAL_GEM1_WAKE_IRQ_0 59 #define VERSAL_ADMA_IRQ_0 60 +#define VERSAL_XRAM_IRQ_0 79 #define VERSAL_RTC_APB_ERR_IRQ 121 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_RTC_ALARM_IRQ 142 @@ -128,6 +137,10 @@ struct Versal { #define MM_OCM 0xfffc0000U #define MM_OCM_SIZE 0x40000 +#define MM_XRAM 0xfe800000 +#define MM_XRAMC 0xff8e0000 +#define MM_XRAMC_SIZE 0x10000 + #define MM_USB2_CTRL_REGS 0xFF9D0000 #define MM_USB2_CTRL_REGS_SIZE 0x10000 diff --git a/include/hw/boards.h b/include/hw/boards.h index 5fda5fd..4a90549 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -128,6 +128,7 @@ typedef struct { * @kvm_type: * Return the type of KVM corresponding to the kvm-type string option or * computed based on other criteria such as the host kernel capabilities. + * kvm-type may be NULL if it is not needed. * @numa_mem_supported: * true if '--numa node.mem' option is supported and false otherwise * @smp_parse: diff --git a/include/hw/misc/npcm7xx_mft.h b/include/hw/misc/npcm7xx_mft.h new file mode 100644 index 0000000..36785e3 --- /dev/null +++ b/include/hw/misc/npcm7xx_mft.h @@ -0,0 +1,70 @@ +/* + * 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. + */ +#ifndef NPCM7XX_MFT_H +#define NPCM7XX_MFT_H + +#include "exec/memory.h" +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +/* Max Fan input number. */ +#define NPCM7XX_MFT_MAX_FAN_INPUT 19 + +/* + * Number of registers in one MFT module. Don't change this without increasing + * the version_id in vmstate. + */ +#define NPCM7XX_MFT_NR_REGS (0x20 / sizeof(uint16_t)) + +/* + * The MFT can take up to 4 inputs: A0, B0, A1, B1. It can measure one A and one + * B simultaneously. NPCM7XX_MFT_INASEL and NPCM7XX_MFT_INBSEL are used to + * select which A or B input are used. + */ +#define NPCM7XX_MFT_FANIN_COUNT 4 + +/** + * struct NPCM7xxMFTState - Multi Functional Tachometer device state. + * @parent: System bus device. + * @iomem: Memory region through which registers are accessed. + * @clock_in: The input clock for MFT from CLK module. + * @clock_{1,2}: The counter clocks for NPCM7XX_MFT_CNT{1,2} + * @irq: The IRQ for this MFT state. + * @regs: The MMIO registers. + * @max_rpm: The maximum rpm for fans. Order: A0, B0, A1, B1. + * @duty: The duty cycles for fans, relative to NPCM7XX_PWM_MAX_DUTY. + */ +typedef struct NPCM7xxMFTState { + SysBusDevice parent; + + MemoryRegion iomem; + + Clock *clock_in; + Clock *clock_1, *clock_2; + qemu_irq irq; + uint16_t regs[NPCM7XX_MFT_NR_REGS]; + + uint32_t max_rpm[NPCM7XX_MFT_FANIN_COUNT]; + uint32_t duty[NPCM7XX_MFT_FANIN_COUNT]; +} NPCM7xxMFTState; + +#define TYPE_NPCM7XX_MFT "npcm7xx-mft" +#define NPCM7XX_MFT(obj) \ + OBJECT_CHECK(NPCM7xxMFTState, (obj), TYPE_NPCM7XX_MFT) + +#endif /* NPCM7XX_MFT_H */ diff --git a/include/hw/misc/npcm7xx_pwm.h b/include/hw/misc/npcm7xx_pwm.h index 5a689d3..7ad632a 100644 --- a/include/hw/misc/npcm7xx_pwm.h +++ b/include/hw/misc/npcm7xx_pwm.h @@ -77,6 +77,7 @@ typedef struct NPCM7xxPWM { * @iomem: Memory region through which registers are accessed. * @clock: The PWM clock. * @pwm: The PWM channels owned by this module. + * @duty_gpio_out: The duty cycle of each PWM channels as a output GPIO. * @ppr: The prescaler register. * @csr: The clock selector register. * @pcr: The control register. @@ -89,7 +90,8 @@ struct NPCM7xxPWMState { MemoryRegion iomem; Clock *clock; - NPCM7xxPWM pwm[NPCM7XX_PWM_PER_MODULE]; + NPCM7xxPWM pwm[NPCM7XX_PWM_PER_MODULE]; + qemu_irq duty_gpio_out[NPCM7XX_PWM_PER_MODULE]; uint32_t ppr; uint32_t csr; diff --git a/include/hw/misc/xlnx-versal-xramc.h b/include/hw/misc/xlnx-versal-xramc.h new file mode 100644 index 0000000..d3d1862 --- /dev/null +++ b/include/hw/misc/xlnx-versal-xramc.h @@ -0,0 +1,97 @@ +/* + * 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> + */ + +#ifndef XLNX_VERSAL_XRAMC_H +#define XLNX_VERSAL_XRAMC_H + +#include "hw/sysbus.h" +#include "hw/register.h" + +#define TYPE_XLNX_XRAM_CTRL "xlnx.versal-xramc" + +#define XLNX_XRAM_CTRL(obj) \ + OBJECT_CHECK(XlnxXramCtrl, (obj), TYPE_XLNX_XRAM_CTRL) + +REG32(XRAM_ERR_CTRL, 0x0) + FIELD(XRAM_ERR_CTRL, UE_RES, 3, 1) + FIELD(XRAM_ERR_CTRL, PWR_ERR_RES, 2, 1) + FIELD(XRAM_ERR_CTRL, PZ_ERR_RES, 1, 1) + FIELD(XRAM_ERR_CTRL, APB_ERR_RES, 0, 1) +REG32(XRAM_ISR, 0x4) + FIELD(XRAM_ISR, INV_APB, 0, 1) +REG32(XRAM_IMR, 0x8) + FIELD(XRAM_IMR, INV_APB, 0, 1) +REG32(XRAM_IEN, 0xc) + FIELD(XRAM_IEN, INV_APB, 0, 1) +REG32(XRAM_IDS, 0x10) + FIELD(XRAM_IDS, INV_APB, 0, 1) +REG32(XRAM_ECC_CNTL, 0x14) + FIELD(XRAM_ECC_CNTL, FI_MODE, 2, 1) + FIELD(XRAM_ECC_CNTL, DET_ONLY, 1, 1) + FIELD(XRAM_ECC_CNTL, ECC_ON_OFF, 0, 1) +REG32(XRAM_CLR_EXE, 0x18) + FIELD(XRAM_CLR_EXE, MON_7, 7, 1) + FIELD(XRAM_CLR_EXE, MON_6, 6, 1) + FIELD(XRAM_CLR_EXE, MON_5, 5, 1) + FIELD(XRAM_CLR_EXE, MON_4, 4, 1) + FIELD(XRAM_CLR_EXE, MON_3, 3, 1) + FIELD(XRAM_CLR_EXE, MON_2, 2, 1) + FIELD(XRAM_CLR_EXE, MON_1, 1, 1) + FIELD(XRAM_CLR_EXE, MON_0, 0, 1) +REG32(XRAM_CE_FFA, 0x1c) + FIELD(XRAM_CE_FFA, ADDR, 0, 20) +REG32(XRAM_CE_FFD0, 0x20) +REG32(XRAM_CE_FFD1, 0x24) +REG32(XRAM_CE_FFD2, 0x28) +REG32(XRAM_CE_FFD3, 0x2c) +REG32(XRAM_CE_FFE, 0x30) + FIELD(XRAM_CE_FFE, SYNDROME, 0, 16) +REG32(XRAM_UE_FFA, 0x34) + FIELD(XRAM_UE_FFA, ADDR, 0, 20) +REG32(XRAM_UE_FFD0, 0x38) +REG32(XRAM_UE_FFD1, 0x3c) +REG32(XRAM_UE_FFD2, 0x40) +REG32(XRAM_UE_FFD3, 0x44) +REG32(XRAM_UE_FFE, 0x48) + FIELD(XRAM_UE_FFE, SYNDROME, 0, 16) +REG32(XRAM_FI_D0, 0x4c) +REG32(XRAM_FI_D1, 0x50) +REG32(XRAM_FI_D2, 0x54) +REG32(XRAM_FI_D3, 0x58) +REG32(XRAM_FI_SY, 0x5c) + FIELD(XRAM_FI_SY, DATA, 0, 16) +REG32(XRAM_RMW_UE_FFA, 0x70) + FIELD(XRAM_RMW_UE_FFA, ADDR, 0, 20) +REG32(XRAM_FI_CNTR, 0x74) + FIELD(XRAM_FI_CNTR, COUNT, 0, 24) +REG32(XRAM_IMP, 0x80) + FIELD(XRAM_IMP, SIZE, 0, 4) +REG32(XRAM_PRDY_DBG, 0x84) + FIELD(XRAM_PRDY_DBG, ISLAND3, 12, 4) + FIELD(XRAM_PRDY_DBG, ISLAND2, 8, 4) + FIELD(XRAM_PRDY_DBG, ISLAND1, 4, 4) + FIELD(XRAM_PRDY_DBG, ISLAND0, 0, 4) +REG32(XRAM_SAFETY_CHK, 0xff8) + +#define XRAM_CTRL_R_MAX (R_XRAM_SAFETY_CHK + 1) + +typedef struct XlnxXramCtrl { + SysBusDevice parent_obj; + MemoryRegion ram; + qemu_irq irq; + + struct { + uint64_t size; + unsigned int encoded_size; + } cfg; + + RegisterInfoArray *reg_array; + uint32_t regs[XRAM_CTRL_R_MAX]; + RegisterInfo regs_info[XRAM_CTRL_R_MAX]; +} XlnxXramCtrl; +#endif diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index a052f7b..3201e79 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -296,4 +296,16 @@ uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg); void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie, QEMUSGList *sg, enum BlockAcctType type); +/** + * dma_aligned_pow2_mask: Return the address bit mask of the largest + * power of 2 size less or equal than @end - @start + 1, aligned with @start, + * and bounded by 1 << @max_addr_bits bits. + * + * @start: range start address + * @end: range end address (greater than @start) + * @max_addr_bits: max address bits (<= 64) + */ +uint64_t dma_aligned_pow2_mask(uint64_t start, uint64_t end, + int max_addr_bits); + #endif diff --git a/softmmu/dma-helpers.c b/softmmu/dma-helpers.c index 29001b5..7d766a5 100644 --- a/softmmu/dma-helpers.c +++ b/softmmu/dma-helpers.c @@ -330,3 +330,29 @@ void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie, { block_acct_start(blk_get_stats(blk), cookie, sg->size, type); } + +uint64_t dma_aligned_pow2_mask(uint64_t start, uint64_t end, int max_addr_bits) +{ + uint64_t max_mask = UINT64_MAX, addr_mask = end - start; + uint64_t alignment_mask, size_mask; + + if (max_addr_bits != 64) { + max_mask = (1ULL << max_addr_bits) - 1; + } + + alignment_mask = start ? (start & -start) - 1 : max_mask; + alignment_mask = MIN(alignment_mask, max_mask); + size_mask = MIN(addr_mask, max_mask); + + if (alignment_mask <= size_mask) { + /* Increase the alignment of start */ + return alignment_mask; + } else { + /* Find the largest page mask from size */ + if (addr_mask == UINT64_MAX) { + return UINT64_MAX; + } + return (1ULL << (63 - clz64(addr_mask + 1))) - 1; + } +} + diff --git a/target/arm/kvm.c b/target/arm/kvm.c index bebea90..d8381ba 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -230,12 +230,14 @@ bool kvm_arm_pmu_supported(void) return kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3); } -int kvm_arm_get_max_vm_ipa_size(MachineState *ms) +int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) { KVMState *s = KVM_STATE(ms->accelerator); int ret; ret = kvm_check_extension(s, KVM_CAP_ARM_VM_IPA_SIZE); + *fixed_ipa = ret <= 0; + return ret > 0 ? ret : 40; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 68ec970..34f8daa 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -311,10 +311,12 @@ bool kvm_arm_sve_supported(void); /** * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle + * @fixed_ipa: True when the IPA limit is fixed at 40. This is the case + * for legacy KVM. * * Returns the number of bits in the IPA address space supported by KVM */ -int kvm_arm_get_max_vm_ipa_size(MachineState *ms); +int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa); /** * kvm_arm_sync_mpstate_to_kvm: @@ -409,7 +411,7 @@ static inline void kvm_arm_add_vcpu_properties(Object *obj) g_assert_not_reached(); } -static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms) +static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) { g_assert_not_reached(); } diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 844db08..fd6c58f 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -1871,6 +1871,7 @@ void HELPER(sve_zip_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); int esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); intptr_t high = FIELD_EX32(pred_desc, PREDDESC, DATA); + int esize = 1 << esz; uint64_t *d = vd; intptr_t i; @@ -1883,33 +1884,35 @@ void HELPER(sve_zip_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) mm = extract64(mm, high * half, half); nn = expand_bits(nn, esz); mm = expand_bits(mm, esz); - d[0] = nn + (mm << (1 << esz)); + d[0] = nn | (mm << esize); } else { - ARMPredicateReg tmp_n, tmp_m; + ARMPredicateReg tmp; /* We produce output faster than we consume input. Therefore we must be mindful of possible overlap. */ - if ((vn - vd) < (uintptr_t)oprsz) { - vn = memcpy(&tmp_n, vn, oprsz); - } - if ((vm - vd) < (uintptr_t)oprsz) { - vm = memcpy(&tmp_m, vm, oprsz); + if (vd == vn) { + vn = memcpy(&tmp, vn, oprsz); + if (vd == vm) { + vm = vn; + } + } else if (vd == vm) { + vm = memcpy(&tmp, vm, oprsz); } if (high) { high = oprsz >> 1; } - if ((high & 3) == 0) { + if ((oprsz & 7) == 0) { uint32_t *n = vn, *m = vm; high >>= 2; - for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + for (i = 0; i < oprsz / 8; i++) { uint64_t nn = n[H4(high + i)]; uint64_t mm = m[H4(high + i)]; nn = expand_bits(nn, esz); mm = expand_bits(mm, esz); - d[i] = nn + (mm << (1 << esz)); + d[i] = nn | (mm << esize); } } else { uint8_t *n = vn, *m = vm; @@ -1921,7 +1924,7 @@ void HELPER(sve_zip_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) nn = expand_bits(nn, esz); mm = expand_bits(mm, esz); - d16[H2(i)] = nn + (mm << (1 << esz)); + d16[H2(i)] = nn | (mm << esize); } } } @@ -1939,7 +1942,7 @@ void HELPER(sve_uzp_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) if (oprsz <= 8) { l = compress_bits(n[0] >> odd, esz); h = compress_bits(m[0] >> odd, esz); - d[0] = extract64(l + (h << (4 * oprsz)), 0, 8 * oprsz); + d[0] = l | (h << (4 * oprsz)); } else { ARMPredicateReg tmp_m; intptr_t oprsz_16 = oprsz / 16; @@ -1953,23 +1956,35 @@ void HELPER(sve_uzp_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) h = n[2 * i + 1]; l = compress_bits(l >> odd, esz); h = compress_bits(h >> odd, esz); - d[i] = l + (h << 32); + d[i] = l | (h << 32); } - /* For VL which is not a power of 2, the results from M do not - align nicely with the uint64_t for D. Put the aligned results - from M into TMP_M and then copy it into place afterward. */ + /* + * For VL which is not a multiple of 512, the results from M do not + * align nicely with the uint64_t for D. Put the aligned results + * from M into TMP_M and then copy it into place afterward. + */ if (oprsz & 15) { - d[i] = compress_bits(n[2 * i] >> odd, esz); + int final_shift = (oprsz & 15) * 2; + + l = n[2 * i + 0]; + h = n[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + d[i] = l | (h << final_shift); for (i = 0; i < oprsz_16; i++) { l = m[2 * i + 0]; h = m[2 * i + 1]; l = compress_bits(l >> odd, esz); h = compress_bits(h >> odd, esz); - tmp_m.p[i] = l + (h << 32); + tmp_m.p[i] = l | (h << 32); } - tmp_m.p[i] = compress_bits(m[2 * i] >> odd, esz); + l = m[2 * i + 0]; + h = m[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + tmp_m.p[i] = l | (h << final_shift); swap_memmove(vd + oprsz / 2, &tmp_m, oprsz / 2); } else { @@ -1978,7 +1993,7 @@ void HELPER(sve_uzp_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) h = m[2 * i + 1]; l = compress_bits(l >> odd, esz); h = compress_bits(h >> odd, esz); - d[oprsz_16 + i] = l + (h << 32); + d[oprsz_16 + i] = l | (h << 32); } } } @@ -2090,11 +2105,11 @@ void HELPER(sve_punpk_p)(void *vd, void *vn, uint32_t pred_desc) high = oprsz >> 1; } - if ((high & 3) == 0) { + if ((oprsz & 7) == 0) { uint32_t *n = vn; high >>= 2; - for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + for (i = 0; i < oprsz / 8; i++) { uint64_t nn = n[H4(high + i)]; d[i] = expand_bits(nn, 0); } @@ -2222,10 +2237,10 @@ void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc) */ int32_t HELPER(sve_last_active_element)(void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; - intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8); + intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); - return last_active_element(vg, DIV_ROUND_UP(oprsz, 8), esz); + return last_active_element(vg, words, esz); } void HELPER(sve_splice)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) @@ -2695,7 +2710,7 @@ static uint32_t do_zero(ARMPredicateReg *d, intptr_t oprsz) void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { compute_brk_z(vd, vm, vg, oprsz, true); } else { @@ -2706,7 +2721,7 @@ void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, uint32_t HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { return compute_brks_z(vd, vm, vg, oprsz, true); } else { @@ -2717,7 +2732,7 @@ uint32_t HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg, void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { compute_brk_z(vd, vm, vg, oprsz, false); } else { @@ -2728,7 +2743,7 @@ void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg, uint32_t HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { return compute_brks_z(vd, vm, vg, oprsz, false); } else { @@ -2738,56 +2753,55 @@ uint32_t HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg, void HELPER(sve_brka_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); compute_brk_z(vd, vn, vg, oprsz, true); } uint32_t HELPER(sve_brkas_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); return compute_brks_z(vd, vn, vg, oprsz, true); } void HELPER(sve_brkb_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); compute_brk_z(vd, vn, vg, oprsz, false); } uint32_t HELPER(sve_brkbs_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); return compute_brks_z(vd, vn, vg, oprsz, false); } void HELPER(sve_brka_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); compute_brk_m(vd, vn, vg, oprsz, true); } uint32_t HELPER(sve_brkas_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); return compute_brks_m(vd, vn, vg, oprsz, true); } void HELPER(sve_brkb_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); compute_brk_m(vd, vn, vg, oprsz, false); } uint32_t HELPER(sve_brkbs_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); return compute_brks_m(vd, vn, vg, oprsz, false); } void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; - + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (!last_active_pred(vn, vg, oprsz)) { do_zero(vd, oprsz); } @@ -2812,8 +2826,7 @@ static uint32_t predtest_ones(ARMPredicateReg *d, intptr_t oprsz, uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; - + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { return predtest_ones(vd, oprsz, -1); } else { @@ -2823,12 +2836,12 @@ uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) { - intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; - intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8); + intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); uint64_t *n = vn, *g = vg, sum = 0, mask = pred_esz_masks[esz]; intptr_t i; - for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + for (i = 0; i < words; ++i) { uint64_t t = n[i] & g[i] & mask; sum += ctpop64(t); } @@ -2837,8 +2850,8 @@ uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) { - uintptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; - intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); uint64_t esz_mask = pred_esz_masks[esz]; ARMPredicateReg *d = vd; uint32_t flags; @@ -2883,7 +2896,7 @@ static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ } \ uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ { \ - uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_maxsz(desc); \ + uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ for (i = 0; i < oprsz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 27402af..0eefb61 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -2302,11 +2302,10 @@ static void find_last_active(DisasContext *s, TCGv_i32 ret, int esz, int pg) */ TCGv_ptr t_p = tcg_temp_new_ptr(); TCGv_i32 t_desc; - unsigned vsz = pred_full_reg_size(s); - unsigned desc; + unsigned desc = 0; - desc = vsz - 2; - desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s)); + desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); t_desc = tcg_const_i32(desc); @@ -2851,7 +2850,7 @@ static bool do_brk3(DisasContext *s, arg_rprr_s *a, TCGv_ptr n = tcg_temp_new_ptr(); TCGv_ptr m = tcg_temp_new_ptr(); TCGv_ptr g = tcg_temp_new_ptr(); - TCGv_i32 t = tcg_const_i32(vsz - 2); + TCGv_i32 t = tcg_const_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); @@ -2885,7 +2884,7 @@ static bool do_brk2(DisasContext *s, arg_rpr_s *a, TCGv_ptr d = tcg_temp_new_ptr(); TCGv_ptr n = tcg_temp_new_ptr(); TCGv_ptr g = tcg_temp_new_ptr(); - TCGv_i32 t = tcg_const_i32(vsz - 2); + TCGv_i32 t = tcg_const_i32(FIELD_DP32(0, PREDDESC, OPRSZ, vsz)); tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); @@ -2968,11 +2967,11 @@ static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) } else { TCGv_ptr t_pn = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); - unsigned desc; + unsigned desc = 0; TCGv_i32 t_desc; - desc = psz - 2; - desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + desc = FIELD_DP32(desc, PREDDESC, OPRSZ, psz); + desc = FIELD_DP32(desc, PREDDESC, ESZ, esz); tcg_gen_addi_ptr(t_pn, cpu_env, pred_full_reg_offset(s, pn)); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); @@ -3098,7 +3097,8 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) TCGv_i64 op0, op1, t0, t1, tmax; TCGv_i32 t2, t3; TCGv_ptr ptr; - unsigned desc, vsz = vec_full_reg_size(s); + unsigned vsz = vec_full_reg_size(s); + unsigned desc = 0; TCGCond cond; if (!sve_access_check(s)) { @@ -3162,8 +3162,8 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) /* Scale elements to bits. */ tcg_gen_shli_i32(t2, t2, a->esz); - desc = (vsz / 8) - 2; - desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); + desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); t3 = tcg_const_i32(desc); ptr = tcg_temp_new_ptr(); @@ -3440,7 +3440,7 @@ static void do_reduce(DisasContext *s, arg_rpr_esz *a, { unsigned vsz = vec_full_reg_size(s); unsigned p2vsz = pow2ceil(vsz); - TCGv_i32 t_desc = tcg_const_i32(simd_desc(vsz, p2vsz, 0)); + TCGv_i32 t_desc = tcg_const_i32(simd_desc(vsz, vsz, p2vsz)); TCGv_ptr t_zn, t_pg, status; TCGv_i64 temp; diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index eb01286..1ca32ec 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -507,20 +507,18 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern('Boot successful.') # TODO user command, for now the uart is stuck - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_cubieboard_initrd(self): """ :avocado: tags=arch:arm :avocado: tags=machine:cubieboard """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -549,20 +547,18 @@ class BootLinuxConsole(LinuxKernelTest): 'system-control@1c00000') # cubieboard's reboot is not functioning; omit reboot test. - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_cubieboard_sata(self): """ :avocado: tags=arch:arm :avocado: tags=machine:cubieboard """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -678,20 +674,18 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern( 'Give root password for system maintenance') - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_orangepi(self): """ :avocado: tags=arch:arm :avocado: tags=machine:orangepi-pc """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) self.vm.set_console() @@ -705,20 +699,18 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_orangepi_initrd(self): """ :avocado: tags=arch:arm :avocado: tags=machine:orangepi-pc """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -749,8 +741,6 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_orangepi_sd(self): """ :avocado: tags=arch:arm @@ -758,12 +748,12 @@ class BootLinuxConsole(LinuxKernelTest): :avocado: tags=device:sd """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' 'kci-2019.02/armel/base/rootfs.ext2.xz') @@ -802,7 +792,27 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() - def do_test_arm_orangepi_uboot_armbian(self, image_path): + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_orangepi_bionic_20_08(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + :avocado: tags=device:sd + """ + + # This test download a 275 MiB compressed image and expand it + # to 1036 MiB, but the underlying filesystem is 1552 MiB... + # As we expand it to 2 GiB we are safe. + + image_url = ('https://archive.armbian.com/orangepipc/archive/' + 'Armbian_20.08.1_Orangepipc_bionic_current_5.8.5.img.xz') + image_hash = ('b4d6775f5673486329e45a0586bf06b6' + 'dbe792199fd182ac6b9c7bb6c7d3e6dd') + image_path_xz = self.fetch_asset(image_url, asset_hash=image_hash, + algorithm='sha256') + image_path = archive.extract(image_path_xz, self.workdir) + image_pow2ceil_expand(image_path) + self.vm.set_console() self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', '-nic', 'user', @@ -828,54 +838,6 @@ class BootLinuxConsole(LinuxKernelTest): 'to <orangepipc>') self.wait_for_console_pattern('Starting Load Kernel Modules...') - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - @skipUnless(P7ZIP_AVAILABLE, '7z not installed') - def test_arm_orangepi_bionic_19_11(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - """ - - # This test download a 196MB compressed image and expand it to 1GB - image_url = ('https://dl.armbian.com/orangepipc/archive/' - 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z') - image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e' - image_path_7z = self.fetch_asset(image_url, asset_hash=image_hash) - image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img' - image_path = os.path.join(self.workdir, image_name) - process.run("7z e -o%s %s" % (self.workdir, image_path_7z)) - image_pow2ceil_expand(image_path) - - self.do_test_arm_orangepi_uboot_armbian(image_path) - - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_orangepi_bionic_20_08(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - """ - - # This test download a 275 MiB compressed image and expand it - # to 1036 MiB, but the underlying filesystem is 1552 MiB... - # As we expand it to 2 GiB we are safe. - - image_url = ('https://dl.armbian.com/orangepipc/archive/' - 'Armbian_20.08.1_Orangepipc_bionic_current_5.8.5.img.xz') - image_hash = ('b4d6775f5673486329e45a0586bf06b6' - 'dbe792199fd182ac6b9c7bb6c7d3e6dd') - image_path_xz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_xz, self.workdir) - image_pow2ceil_expand(image_path) - - self.do_test_arm_orangepi_uboot_armbian(image_path) - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') def test_arm_orangepi_uboot_netbsd9(self): """ diff --git a/tests/acceptance/replay_kernel.py b/tests/acceptance/replay_kernel.py index c1cb862..71facda 100644 --- a/tests/acceptance/replay_kernel.py +++ b/tests/acceptance/replay_kernel.py @@ -177,20 +177,18 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), - 'Test artifacts fetched from unreliable apt.armbian.com') def test_arm_cubieboard_initrd(self): """ :avocado: tags=arch:arm :avocado: tags=machine:cubieboard """ deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') - deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb') + deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0' deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.20.7-sunxi') - dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-5.10.16-sunxi') + dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index 3d82654..bd15a1c 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -45,6 +45,7 @@ #define PLL_FBDV(rv) extract32((rv), 16, 12) #define PLL_OTDV1(rv) extract32((rv), 8, 3) #define PLL_OTDV2(rv) extract32((rv), 13, 3) +#define APB4CKDIV(rv) extract32((rv), 30, 2) #define APB3CKDIV(rv) extract32((rv), 28, 2) #define CLK2CKDIV(rv) extract32((rv), 0, 1) #define CLK4CKDIV(rv) extract32((rv), 26, 2) @@ -52,6 +53,49 @@ #define MAX_DUTY 1000000 +/* MFT (PWM fan) related */ +#define MFT_BA(n) (0xf0180000 + ((n) * 0x1000)) +#define MFT_IRQ(n) (96 + (n)) +#define MFT_CNT1 0x00 +#define MFT_CRA 0x02 +#define MFT_CRB 0x04 +#define MFT_CNT2 0x06 +#define MFT_PRSC 0x08 +#define MFT_CKC 0x0a +#define MFT_MCTRL 0x0c +#define MFT_ICTRL 0x0e +#define MFT_ICLR 0x10 +#define MFT_IEN 0x12 +#define MFT_CPA 0x14 +#define MFT_CPB 0x16 +#define MFT_CPCFG 0x18 +#define MFT_INASEL 0x1a +#define MFT_INBSEL 0x1c + +#define MFT_MCTRL_ALL 0x64 +#define MFT_ICLR_ALL 0x3f +#define MFT_IEN_ALL 0x3f +#define MFT_CPCFG_EQ_MODE 0x44 + +#define MFT_CKC_C2CSEL BIT(3) +#define MFT_CKC_C1CSEL BIT(0) + +#define MFT_ICTRL_TFPND BIT(5) +#define MFT_ICTRL_TEPND BIT(4) +#define MFT_ICTRL_TDPND BIT(3) +#define MFT_ICTRL_TCPND BIT(2) +#define MFT_ICTRL_TBPND BIT(1) +#define MFT_ICTRL_TAPND BIT(0) + +#define MFT_MAX_CNT 0xffff +#define MFT_TIMEOUT 0x5000 + +#define DEFAULT_RPM 19800 +#define DEFAULT_PRSC 255 +#define MFT_PULSE_PER_REVOLUTION 2 + +#define MAX_ERROR 1 + typedef struct PWMModule { int irq; uint64_t base_addr; @@ -210,19 +254,36 @@ static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index) return pwm_qom_get(qts, path, name); } +static void mft_qom_set(QTestState *qts, int index, const char *name, + uint32_t value) +{ + QDict *response; + char *path = g_strdup_printf("/machine/soc/mft[%d]", index); + + g_test_message("Setting properties %s of mft[%d] with value %u", + name, index, value); + response = qtest_qmp(qts, "{ 'execute': 'qom-set'," + " 'arguments': { 'path': %s, " + " 'property': %s, 'value': %u}}", + path, name, value); + /* The qom set message returns successfully. */ + g_assert_true(qdict_haskey(response, "return")); +} + static uint32_t get_pll(uint32_t con) { return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con) * PLL_OTDV2(con)); } -static uint64_t read_pclk(QTestState *qts) +static uint64_t read_pclk(QTestState *qts, bool mft) { uint64_t freq = REF_HZ; uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL); uint32_t pllcon; uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1); uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2); + uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2); switch (CPUCKSEL(clksel)) { case 0: @@ -241,7 +302,7 @@ static uint64_t read_pclk(QTestState *qts) g_assert_not_reached(); } - freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv2)); + freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv); return freq; } @@ -267,7 +328,7 @@ static uint32_t pwm_selector(uint32_t csr) static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr, uint32_t cnr) { - return read_pclk(qts) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); + return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); } static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) @@ -301,6 +362,28 @@ static void pwm_write(QTestState *qts, const TestData *td, unsigned offset, qtest_writel(qts, td->module->base_addr + offset, value); } +static uint8_t mft_readb(QTestState *qts, int index, unsigned offset) +{ + return qtest_readb(qts, MFT_BA(index) + offset); +} + +static uint16_t mft_readw(QTestState *qts, int index, unsigned offset) +{ + return qtest_readw(qts, MFT_BA(index) + offset); +} + +static void mft_writeb(QTestState *qts, int index, unsigned offset, + uint8_t value) +{ + qtest_writeb(qts, MFT_BA(index) + offset, value); +} + +static void mft_writew(QTestState *qts, int index, unsigned offset, + uint16_t value) +{ + return qtest_writew(qts, MFT_BA(index) + offset, value); +} + static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td) { return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8); @@ -351,11 +434,116 @@ static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value) pwm_write(qts, td, td->pwm->cmr_offset, value); } +static int mft_compute_index(const TestData *td) +{ + int index = pwm_module_index(td->module) * ARRAY_SIZE(pwm_list) + + pwm_index(td->pwm); + + g_assert_cmpint(index, <, + ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)); + + return index; +} + +static void mft_reset_counters(QTestState *qts, int index) +{ + mft_writew(qts, index, MFT_CNT1, MFT_MAX_CNT); + mft_writew(qts, index, MFT_CNT2, MFT_MAX_CNT); + mft_writew(qts, index, MFT_CRA, MFT_MAX_CNT); + mft_writew(qts, index, MFT_CRB, MFT_MAX_CNT); + mft_writew(qts, index, MFT_CPA, MFT_MAX_CNT - MFT_TIMEOUT); + mft_writew(qts, index, MFT_CPB, MFT_MAX_CNT - MFT_TIMEOUT); +} + +static void mft_init(QTestState *qts, const TestData *td) +{ + int index = mft_compute_index(td); + + /* Enable everything */ + mft_writeb(qts, index, MFT_CKC, 0); + mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); + mft_writeb(qts, index, MFT_MCTRL, MFT_MCTRL_ALL); + mft_writeb(qts, index, MFT_IEN, MFT_IEN_ALL); + mft_writeb(qts, index, MFT_INASEL, 0); + mft_writeb(qts, index, MFT_INBSEL, 0); + + /* Set cpcfg to use EQ mode, same as kernel driver */ + mft_writeb(qts, index, MFT_CPCFG, MFT_CPCFG_EQ_MODE); + + /* Write default counters, timeout and prescaler */ + mft_reset_counters(qts, index); + mft_writeb(qts, index, MFT_PRSC, DEFAULT_PRSC); + + /* Write default max rpm via QMP */ + mft_qom_set(qts, index, "max_rpm[0]", DEFAULT_RPM); + mft_qom_set(qts, index, "max_rpm[1]", DEFAULT_RPM); +} + +static int32_t mft_compute_cnt(uint32_t rpm, uint64_t clk) +{ + uint64_t cnt; + + if (rpm == 0) { + return -1; + } + + cnt = clk * 60 / ((DEFAULT_PRSC + 1) * rpm * MFT_PULSE_PER_REVOLUTION); + if (cnt >= MFT_TIMEOUT) { + return -1; + } + return MFT_MAX_CNT - cnt; +} + +static void mft_verify_rpm(QTestState *qts, const TestData *td, uint64_t duty) +{ + int index = mft_compute_index(td); + uint16_t cnt, cr; + uint32_t rpm = DEFAULT_RPM * duty / MAX_DUTY; + uint64_t clk = read_pclk(qts, true); + int32_t expected_cnt = mft_compute_cnt(rpm, clk); + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + g_test_message( + "verifying rpm for mft[%d]: clk: %" PRIu64 ", duty: %" PRIu64 ", rpm: %u, cnt: %d", + index, clk, duty, rpm, expected_cnt); + + /* Verify rpm for fan A */ + /* Stop capture */ + mft_writeb(qts, index, MFT_CKC, 0); + mft_writeb(qts, index, MFT_ICLR, MFT_ICLR_ALL); + mft_reset_counters(qts, index); + g_assert_cmphex(mft_readw(qts, index, MFT_CNT1), ==, MFT_MAX_CNT); + g_assert_cmphex(mft_readw(qts, index, MFT_CRA), ==, MFT_MAX_CNT); + g_assert_cmphex(mft_readw(qts, index, MFT_CPA), ==, + MFT_MAX_CNT - MFT_TIMEOUT); + /* Start capture */ + mft_writeb(qts, index, MFT_CKC, MFT_CKC_C1CSEL); + g_assert_true(qtest_get_irq(qts, MFT_IRQ(index))); + if (expected_cnt == -1) { + g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TEPND); + } else { + g_assert_cmphex(mft_readb(qts, index, MFT_ICTRL), ==, MFT_ICTRL_TAPND); + cnt = mft_readw(qts, index, MFT_CNT1); + /* + * Due to error in clock measurement and rounding, we might have a small + * error in measuring RPM. + */ + g_assert_cmphex(cnt + MAX_ERROR, >=, expected_cnt); + g_assert_cmphex(cnt, <=, expected_cnt + MAX_ERROR); + cr = mft_readw(qts, index, MFT_CRA); + g_assert_cmphex(cnt, ==, cr); + } + + /* Verify rpm for fan B */ + + qtest_irq_intercept_out(qts, "/machine/soc/a9mpcore/gic"); +} + /* Check pwm registers can be reset to default value */ static void test_init(gconstpointer test_data) { const TestData *td = test_data; - QTestState *qts = qtest_init("-machine quanta-gsj"); + QTestState *qts = qtest_init("-machine npcm750-evb"); int module = pwm_module_index(td->module); int pwm = pwm_index(td->pwm); @@ -369,7 +557,7 @@ static void test_init(gconstpointer test_data) static void test_oneshot(gconstpointer test_data) { const TestData *td = test_data; - QTestState *qts = qtest_init("-machine quanta-gsj"); + QTestState *qts = qtest_init("-machine npcm750-evb"); int module = pwm_module_index(td->module); int pwm = pwm_index(td->pwm); uint32_t ppr, csr, pcr; @@ -400,13 +588,15 @@ static void test_oneshot(gconstpointer test_data) static void test_toggle(gconstpointer test_data) { const TestData *td = test_data; - QTestState *qts = qtest_init("-machine quanta-gsj"); + QTestState *qts = qtest_init("-machine npcm750-evb"); int module = pwm_module_index(td->module); int pwm = pwm_index(td->pwm); uint32_t ppr, csr, pcr, cnr, cmr; int i, j, k, l; uint64_t expected_freq, expected_duty; + mft_init(qts, td); + pcr = CH_EN | CH_MOD; for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { ppr = ppr_list[i]; @@ -440,6 +630,9 @@ static void test_toggle(gconstpointer test_data) ==, expected_freq); } + /* Test MFT's RPM is correct. */ + mft_verify_rpm(qts, td, expected_duty); + /* Test inverted mode */ expected_duty = pwm_compute_duty(cnr, cmr, true); pwm_write_pcr(qts, td, pcr | CH_INV); |