target-arm queue:

* versal: Support XRAMs and XRAM controller
  * smmu: Various minor bug fixes
  * SVE emulation: fix bugs handling odd vector lengths
  * allwinner-sun8i-emac: traverse transmit queue using TX_CUR_DESC register value
  * tests/acceptance: fix orangepi-pc acceptance tests
  * hw/timer/sse-timer: Propagate eventual error in sse_timer_realize()
  * hw/arm/virt: KVM: The IPA lower bound is 32
  * npcm7xx: support MFT module
  * pl110, pxa2xx_lcd: tidy up template headers
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmBODPcZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3oxdD/9Tbgsd3yZ/zQMKECEbEczB
 F0wgRz5FLFOx5O+Wfz35ByjCySSms5yaikDaBxqZdvfI9CXbvhwBtt+kEj4xcmUc
 0KHYxaAnv//yMqEtpN0gyaVPs0+/BBjEH6kvlOLMngDs4x1Yp7fUh+gqpVxw+V0b
 v1fAZlfWj4SlAeiarTY9HJ9IZ5REFY7AxA0WdZl0cVT/keKf1Np9EGzBGBQIyn40
 zFFLTktJSCmAkN7uUYEmmvcNUAJ1J7YlM1Sm3v4qmAHhRhB3a76qNk9/fDXqEs+Y
 OimIcsnHf/EyHQd8auwl2yLZ36tyDcUILUwRafFoQ12Krz7eSFon8xNnnSBFlgoM
 qmsGHN+AQXpXDaT7PPqx2ckR4vIZcp5dWp4B+rD8XFLhHU9p4FsZwtVfiwWH1K+y
 WOoGPqIo6o7IMOhTf7+NfVMj9COKDbyr9KzteoIOnrKVzc1JQaZVMFD/MufrtH39
 hq7DdAl7MX+pKHKqaNPw8WFA9b8Th6ZCbmN1kyQpIFlj7/MoivJ7EQBtBIj6kBbS
 oy7Z/tI0rzaw6D44OO6yqnJVi2vMKEPzMZptoKYIK8OXNZyIAWOvyvT5tFPg8YCT
 f4QeF2J794NAIFF9SGK9hnFEE/vajzQm248IZVKFHVfhOJ3ev+FjNSdRySInAFY/
 yoZlbfk1u9zula3I1Z0z3Q==
 =7lkG
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210314' into staging

target-arm queue:
 * versal: Support XRAMs and XRAM controller
 * smmu: Various minor bug fixes
 * SVE emulation: fix bugs handling odd vector lengths
 * allwinner-sun8i-emac: traverse transmit queue using TX_CUR_DESC register value
 * tests/acceptance: fix orangepi-pc acceptance tests
 * hw/timer/sse-timer: Propagate eventual error in sse_timer_realize()
 * hw/arm/virt: KVM: The IPA lower bound is 32
 * npcm7xx: support MFT module
 * pl110, pxa2xx_lcd: tidy up template headers

# gpg: Signature made Sun 14 Mar 2021 13:17:43 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20210314: (39 commits)
  hw/display/pxa2xx: Inline template header
  hw/display/pxa2xx: Apply whitespace-only coding style fixes to template header
  hw/display/pxa2xx: Apply brace-related coding style fixes to template header
  hw/display/pxa2xx: Remove use of BITS in pxa2xx_template.h
  hw/display/pxa2xx_lcd: Remove dest_width state field
  hw/display/pxa2xx_lcd: Remove dead code for non-32-bpp surfaces
  hw/display/pl110: Remove use of BITS from pl110_template.h
  hw/display/pl110: Pull included-once parts of template header into pl110.c
  hw/display/pl110: Remove dead code for non-32-bpp surfaces
  tests/qtest: Test PWM fan RPM using MFT in PWM test
  hw/arm: Connect PWM fans in NPCM7XX boards
  hw/arm: Add MFT device to NPCM7xx Soc
  hw/misc: Add NPCM7XX MFT Module
  hw/misc: Add GPIOs for duty in NPCM7xx PWM
  hw/arm/virt: KVM: The IPA lower bound is 32
  accel: kvm: Fix kvm_type invocation
  hw/timer/sse-timer: Propagate eventual error in sse_timer_realize()
  tests/acceptance: drop ARMBIAN_ARTIFACTS_CACHED condition for orangepi-pc, cubieboard tests
  tests/acceptance: update sunxi kernel from armbian to 5.10.16
  tests/acceptance/boot_linux_console: change URL for test_arm_orangepi_bionic_20_08
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Peter Maydell 2021-03-14 13:18:49 +00:00
commit 6f8a81fc29
39 changed files with 2235 additions and 937 deletions

View File

@ -2068,6 +2068,8 @@ static int kvm_init(MachineState *ms)
"kvm-type", "kvm-type",
&error_abort); &error_abort);
type = mc->kvm_type(ms, kvm_type); type = mc->kvm_type(ms, kvm_type);
} else if (mc->kvm_type) {
type = mc->kvm_type(ms, NULL);
} }
do { do {

View File

@ -45,6 +45,7 @@ Supported devices
* Pulse Width Modulation (PWM) * Pulse Width Modulation (PWM)
* SMBus controller (SMBF) * SMBus controller (SMBF)
* Ethernet controller (EMC) * Ethernet controller (EMC)
* Tachometer
Missing devices Missing devices
--------------- ---------------
@ -63,7 +64,6 @@ Missing devices
* Peripheral SPI controller (PSPI) * Peripheral SPI controller (PSPI)
* SD/MMC host * SD/MMC host
* PECI interface * PECI interface
* Tachometer
* PCI and PCIe root complex and bridges * PCI and PCIe root complex and bridges
* VDM and MCTP support * VDM and MCTP support
* Serial I/O expansion * Serial I/O expansion

View File

@ -30,6 +30,7 @@ Implemented devices:
- 8 ADMA (Xilinx zDMA) channels - 8 ADMA (Xilinx zDMA) channels
- 2 SD Controllers - 2 SD Controllers
- OCM (256KB of On Chip Memory) - OCM (256KB of On Chip Memory)
- XRAM (4MB of on chip Accelerator RAM)
- DDR memory - DDR memory
QEMU does not yet model any other devices, including the PL and the AI Engine. QEMU does not yet model any other devices, including the PL and the AI Engine.

View File

@ -122,6 +122,14 @@ enum NPCM7xxInterrupt {
NPCM7XX_SMBUS15_IRQ, NPCM7XX_SMBUS15_IRQ,
NPCM7XX_PWM0_IRQ = 93, /* PWM module 0 */ NPCM7XX_PWM0_IRQ = 93, /* PWM module 0 */
NPCM7XX_PWM1_IRQ, /* PWM module 1 */ 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_EMC2RX_IRQ = 114,
NPCM7XX_EMC2TX_IRQ, NPCM7XX_EMC2TX_IRQ,
NPCM7XX_GPIO0_IRQ = 116, NPCM7XX_GPIO0_IRQ = 116,
@ -172,6 +180,18 @@ static const hwaddr npcm7xx_pwm_addr[] = {
0xf0104000, 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. */ /* Direct memory-mapped access to each SMBus Module. */
static const hwaddr npcm7xx_smbus_addr[] = { static const hwaddr npcm7xx_smbus_addr[] = {
0xf0080000, 0xf0080000,
@ -417,6 +437,10 @@ static void npcm7xx_init(Object *obj)
object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM); 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++) { for (i = 0; i < ARRAY_SIZE(s->emc); i++) {
object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC); 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)); 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. * EMC Modules. Cannot fail.
* The mapping of the device to its netdev backend works as follows: * 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.peci", 0xf0100000, 4 * KiB);
create_unimplemented_device("npcm7xx.siox[1]", 0xf0101000, 4 * KiB); create_unimplemented_device("npcm7xx.siox[1]", 0xf0101000, 4 * KiB);
create_unimplemented_device("npcm7xx.siox[2]", 0xf0102000, 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.pspi1", 0xf0200000, 4 * KiB);
create_unimplemented_device("npcm7xx.pspi2", 0xf0201000, 4 * KiB); create_unimplemented_device("npcm7xx.pspi2", 0xf0201000, 4 * KiB);
create_unimplemented_device("npcm7xx.ahbpci", 0xf0400000, 1 * MiB); create_unimplemented_device("npcm7xx.ahbpci", 0xf0400000, 1 * MiB);

View File

@ -21,6 +21,7 @@
#include "hw/core/cpu.h" #include "hw/core/cpu.h"
#include "hw/i2c/smbus_eeprom.h" #include "hw/i2c/smbus_eeprom.h"
#include "hw/loader.h" #include "hw/loader.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-common.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); 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) static void npcm750_evb_i2c_init(NPCM7xxState *soc)
{ {
/* lm75 temperature sensor on SVB, tmp105 is compatible */ /* 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); 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) static void quanta_gsj_i2c_init(NPCM7xxState *soc)
{ {
/* GSJ machine have 4 max31725 temperature sensors, tmp105 is compatible. */ /* 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. */ /* 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) static void npcm750_evb_init(MachineState *machine)
{ {
NPCM7xxState *soc; NPCM7xxState *soc;
@ -153,6 +250,7 @@ static void npcm750_evb_init(MachineState *machine)
npcm7xx_load_bootrom(machine, soc); npcm7xx_load_bootrom(machine, soc);
npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
npcm750_evb_i2c_init(soc); npcm750_evb_i2c_init(soc);
npcm750_evb_fan_init(NPCM7XX_MACHINE(machine), soc);
npcm7xx_load_kernel(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", npcm7xx_connect_flash(&soc->fiu[0], 0, "mx25l25635e",
drive_get(IF_MTD, 0, 0)); drive_get(IF_MTD, 0, 0));
quanta_gsj_i2c_init(soc); quanta_gsj_i2c_init(soc);
quanta_gsj_fan_init(NPCM7XX_MACHINE(machine), soc);
npcm7xx_load_kernel(machine, soc); npcm7xx_load_kernel(machine, soc);
} }

View File

@ -151,14 +151,21 @@ inline void
smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova, smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
uint8_t tg, uint64_t num_pages, uint8_t ttl) uint8_t tg, uint64_t num_pages, uint8_t ttl)
{ {
if (ttl && (num_pages == 1)) {
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 */ /* if tg is not set we use 4KB range invalidation */
uint8_t granule = tg ? tg * 2 + 10 : 12; 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);
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 = { SMMUIOTLBPageInvInfo info = {
.asid = asid, .iova = iova, .asid = asid, .iova = iova,
.mask = (num_pages * 1 << granule) - 1}; .mask = (num_pages * 1 << granule) - 1};
@ -166,7 +173,6 @@ smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
g_hash_table_foreach_remove(s->iotlb, g_hash_table_foreach_remove(s->iotlb,
smmu_hash_remove_by_asid_iova, smmu_hash_remove_by_asid_iova,
&info); &info);
}
} }
inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)

View File

@ -104,4 +104,9 @@ typedef struct SMMUIOTLBPageInvInfo {
uint64_t mask; uint64_t mask;
} SMMUIOTLBPageInvInfo; } SMMUIOTLBPageInvInfo;
typedef struct SMMUSIDRange {
uint32_t start;
uint32_t end;
} SMMUSIDRange;
#endif #endif

View File

@ -32,6 +32,7 @@
#include "hw/arm/smmuv3.h" #include "hw/arm/smmuv3.h"
#include "smmuv3-internal.h" #include "smmuv3-internal.h"
#include "smmu-internal.h"
/** /**
* smmuv3_trigger_irq - pulse @irq if enabled and update * 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); uint16_t vmid = CMD_VMID(cmd);
bool leaf = CMD_LEAF(cmd); bool leaf = CMD_LEAF(cmd);
uint8_t tg = CMD_TG(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; int asid = -1;
if (tg) { if (tg) {
@ -874,9 +876,38 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
if (type == SMMU_CMD_TLBI_NH_VA) { if (type == SMMU_CMD_TLBI_NH_VA) {
asid = CMD_ASID(cmd); 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); /* Split invalidations into ^2 range invalidations */
smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl); 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) 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 */ 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); uint8_t range = CMD_STE_RANGE(&cmd);
uint64_t end = start + (1ULL << (range + 1)) - 1;
SMMUSIDRange sid_range = {start, end};
if (CMD_SSEC(&cmd)) { if (CMD_SSEC(&cmd)) {
cmd_error = SMMU_CERROR_ILL; cmd_error = SMMU_CERROR_ILL;
break; break;
} }
end = start + (1 << (range + 1)) - 1;
trace_smmuv3_cmdq_cfgi_ste_range(start, end); trace_smmuv3_cmdq_cfgi_ste_range(start, end);
g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste,
for (i = start; i <= end; i++) { &sid_range);
IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i);
SMMUDevice *sdev;
if (!mr) {
continue;
}
sdev = container_of(mr, SMMUDevice, iommu);
smmuv3_flush_config(sdev);
}
break; break;
} }
case SMMU_CMD_CFGI_CD: case SMMU_CMD_CFGI_CD:

View File

@ -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_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_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_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_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(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_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_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_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=%d STE bypass 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=%d abort on 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=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" 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_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64
smmuv3_decode_cd(uint32_t oas) "oas=%d" 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_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_ste_range(int start, int end) "start=0x%x - end=0x%x"
smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %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 %d (hits=%d, misses=%d, hit rate=%d)" 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 %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=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_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(void) ""
smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" 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_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_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 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

View File

@ -2548,14 +2548,23 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
static int virt_kvm_type(MachineState *ms, const char *type_str) static int virt_kvm_type(MachineState *ms, const char *type_str)
{ {
VirtMachineState *vms = VIRT_MACHINE(ms); VirtMachineState *vms = VIRT_MACHINE(ms);
int max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms); int max_vm_pa_size, requested_pa_size;
int 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 */ /* we freeze the memory map to compute the highest gpa */
virt_set_memmap(vms); virt_set_memmap(vms);
requested_pa_size = 64 - clz64(vms->highest_gpa); 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) { if (requested_pa_size > max_vm_pa_size) {
error_report("-m and ,maxmem option values " error_report("-m and ,maxmem option values "
"require an IPA range (%d bits) larger than " "require an IPA range (%d bits) larger than "
@ -2564,11 +2573,11 @@ static int virt_kvm_type(MachineState *ms, const char *type_str)
exit(1); exit(1);
} }
/* /*
* By default we return 0 which corresponds to an implicit legacy * We return the requested PA log size, unless KVM only supports
* 40b IPA setting. Otherwise we return the actual requested PA * the implicit legacy 40b IPA setting, in which case the kvm_type
* logsize * 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) static void virt_machine_class_init(ObjectClass *oc, void *data)

View File

@ -10,6 +10,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/log.h" #include "qemu/log.h"
#include "qemu/module.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]); 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 /* This takes the board allocated linear DDR memory and creates aliases
* for each split DDR range/aperture on the Versal address map. * 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_admas(s, pic);
versal_create_sds(s, pic); versal_create_sds(s, pic);
versal_create_rtc(s, pic); versal_create_rtc(s, pic);
versal_create_xrams(s, pic);
versal_map_ddr(s); versal_map_ddr(s);
versal_unimp(s); versal_unimp(s);

View File

@ -123,16 +123,84 @@ static const unsigned char *idregs[] = {
pl111_id 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" #include "pl110_template.h"
#define BITS 15 #define ORDER 1
#include "pl110_template.h" #include "pl110_template.h"
#define BITS 16 #define ORDER 2
#include "pl110_template.h" #include "pl110_template.h"
#define BITS 24 #undef BORDER
#define RGB
#define BORDER rgb
#define ORDER 0
#include "pl110_template.h" #include "pl110_template.h"
#define BITS 32 #define ORDER 1
#include "pl110_template.h" #include "pl110_template.h"
#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) static int pl110_enabled(PL110State *s)
{ {
@ -144,9 +212,7 @@ static void pl110_update_display(void *opaque)
PL110State *s = (PL110State *)opaque; PL110State *s = (PL110State *)opaque;
SysBusDevice *sbd; SysBusDevice *sbd;
DisplaySurface *surface = qemu_console_surface(s->con); DisplaySurface *surface = qemu_console_surface(s->con);
drawfn* fntable;
drawfn fn; drawfn fn;
int dest_width;
int src_width; int src_width;
int bpp_offset; int bpp_offset;
int first; int first;
@ -158,33 +224,6 @@ static void pl110_update_display(void *opaque)
sbd = SYS_BUS_DEVICE(s); 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) if (s->cr & PL110_CR_BGR)
bpp_offset = 0; bpp_offset = 0;
else else
@ -218,12 +257,13 @@ static void pl110_update_display(void *opaque)
} }
} }
if (s->cr & PL110_CR_BEBO) if (s->cr & PL110_CR_BEBO) {
fn = fntable[s->bpp + 8 + bpp_offset]; fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
else if (s->cr & PL110_CR_BEPO) } else if (s->cr & PL110_CR_BEPO) {
fn = fntable[s->bpp + 16 + bpp_offset]; fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
else } else {
fn = fntable[s->bpp + bpp_offset]; fn = pl110_draw_fn_32[s->bpp + bpp_offset];
}
src_width = s->cols; src_width = s->cols;
switch (s->bpp) { switch (s->bpp) {
@ -247,7 +287,6 @@ static void pl110_update_display(void *opaque)
src_width <<= 2; src_width <<= 2;
break; break;
} }
dest_width *= s->cols;
first = 0; first = 0;
if (s->invalidate) { if (s->invalidate) {
framebuffer_update_memory_section(&s->fbsection, framebuffer_update_memory_section(&s->fbsection,
@ -258,7 +297,7 @@ static void pl110_update_display(void *opaque)
framebuffer_update_display(surface, &s->fbsection, framebuffer_update_display(surface, &s->fbsection,
s->cols, s->rows, s->cols, s->rows,
src_width, dest_width, 0, src_width, s->cols * 4, 0,
s->invalidate, s->invalidate,
fn, s->palette, fn, s->palette,
&first, &last); &first, &last);

View File

@ -10,118 +10,22 @@
*/ */
#ifndef ORDER #ifndef ORDER
#error "pl110_template.h is only for inclusion by pl110.c"
#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
#endif #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 #if ORDER == 0
#define NAME glue(glue(lblp_, BORDER), BITS) #define NAME glue(lblp_, BORDER)
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
#define SWAP_WORDS 1 #define SWAP_WORDS 1
#endif #endif
#elif ORDER == 1 #elif ORDER == 1
#define NAME glue(glue(bbbp_, BORDER), BITS) #define NAME glue(bbbp_, BORDER)
#ifndef HOST_WORDS_BIGENDIAN #ifndef HOST_WORDS_BIGENDIAN
#define SWAP_WORDS 1 #define SWAP_WORDS 1
#endif #endif
#else #else
#define SWAP_PIXELS 1 #define SWAP_PIXELS 1
#define NAME glue(glue(lbbp_, BORDER), BITS) #define NAME glue(lbbp_, BORDER)
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
#define SWAP_WORDS 1 #define SWAP_WORDS 1
#endif #endif
@ -270,14 +174,14 @@ static void glue(pl110_draw_line16_,NAME)(void *opaque, uint8_t *d, const uint8_
MSB = (data & 0x1f) << 3; MSB = (data & 0x1f) << 3;
data >>= 5; data >>= 5;
#endif #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; LSB = (data & 0x1f) << 3;
data >>= 5; data >>= 5;
g = (data & 0x3f) << 2; g = (data & 0x3f) << 2;
data >>= 6; data >>= 6;
MSB = (data & 0x1f) << 3; MSB = (data & 0x1f) << 3;
data >>= 5; 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 MSB
#undef LSB #undef LSB
width -= 2; width -= 2;
@ -307,7 +211,7 @@ static void glue(pl110_draw_line32_,NAME)(void *opaque, uint8_t *d, const uint8_
g = (data >> 16) & 0xff; g = (data >> 16) & 0xff;
MSB = (data >> 8) & 0xff; MSB = (data >> 8) & 0xff;
#endif #endif
COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b)); COPY_PIXEL(d, rgb_to_pixel32(r, g, b));
#undef MSB #undef MSB
#undef LSB #undef LSB
width--; width--;
@ -338,14 +242,14 @@ static void glue(pl110_draw_line16_555_,NAME)(void *opaque, uint8_t *d, const ui
data >>= 5; data >>= 5;
MSB = (data & 0x1f) << 3; MSB = (data & 0x1f) << 3;
data >>= 5; 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; LSB = (data & 0x1f) << 3;
data >>= 5; data >>= 5;
g = (data & 0x1f) << 3; g = (data & 0x1f) << 3;
data >>= 5; data >>= 5;
MSB = (data & 0x1f) << 3; MSB = (data & 0x1f) << 3;
data >>= 6; 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 MSB
#undef LSB #undef LSB
width -= 2; width -= 2;
@ -376,14 +280,14 @@ static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_
data >>= 4; data >>= 4;
MSB = (data & 0xf) << 4; MSB = (data & 0xf) << 4;
data >>= 8; 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; LSB = (data & 0xf) << 4;
data >>= 4; data >>= 4;
g = (data & 0xf) << 4; g = (data & 0xf) << 4;
data >>= 4; data >>= 4;
MSB = (data & 0xf) << 4; MSB = (data & 0xf) << 4;
data >>= 8; 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 MSB
#undef LSB #undef LSB
width -= 2; width -= 2;
@ -395,5 +299,3 @@ static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_
#undef NAME #undef NAME
#undef SWAP_WORDS #undef SWAP_WORDS
#undef ORDER #undef ORDER
#endif

View File

@ -45,7 +45,6 @@ struct PXA2xxLCDState {
int invalidated; int invalidated;
QemuConsole *con; QemuConsole *con;
drawfn *line_fn[2];
int dest_width; int dest_width;
int xres, yres; int xres, yres;
int pal_for; int pal_for;
@ -188,6 +187,435 @@ typedef struct QEMU_PACKED {
#define LDCMD_SOFINT (1 << 22) #define LDCMD_SOFINT (1 << 22)
#define LDCMD_PAL (1 << 26) #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 */ /* Route internal interrupt lines to the global IC */
static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s) 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, static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
hwaddr addr, int *miny, int *maxy) hwaddr addr, int *miny, int *maxy)
{ {
DisplaySurface *surface = qemu_console_surface(s->con); DisplaySurface *surface = qemu_console_surface(s->con);
int src_width, dest_width; int src_width, dest_width;
drawfn fn = NULL; drawfn fn = pxa2xx_drawfn(s);
if (s->dest_width)
fn = s->line_fn[s->transp][s->bpp];
if (!fn) if (!fn)
return; return;
@ -693,14 +1128,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s,
else if (s->bpp > pxa_lcdc_8bpp) else if (s->bpp > pxa_lcdc_8bpp)
src_width *= 2; src_width *= 2;
dest_width = s->xres * s->dest_width; dest_width = s->xres * DEST_PIXEL_WIDTH;
*miny = 0; *miny = 0;
if (s->invalidated) { if (s->invalidated) {
framebuffer_update_memory_section(&s->fbsection, s->sysmem, framebuffer_update_memory_section(&s->fbsection, s->sysmem,
addr, s->yres, src_width); addr, s->yres, src_width);
} }
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, 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, s->invalidated,
fn, s->dma_ch[0].palette, miny, maxy); 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); DisplaySurface *surface = qemu_console_surface(s->con);
int src_width, dest_width; int src_width, dest_width;
drawfn fn = NULL; drawfn fn = pxa2xx_drawfn(s);
if (s->dest_width)
fn = s->line_fn[s->transp][s->bpp];
if (!fn) if (!fn)
return; return;
@ -724,14 +1157,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s,
else if (s->bpp > pxa_lcdc_8bpp) else if (s->bpp > pxa_lcdc_8bpp)
src_width *= 2; src_width *= 2;
dest_width = s->yres * s->dest_width; dest_width = s->yres * DEST_PIXEL_WIDTH;
*miny = 0; *miny = 0;
if (s->invalidated) { if (s->invalidated) {
framebuffer_update_memory_section(&s->fbsection, s->sysmem, framebuffer_update_memory_section(&s->fbsection, s->sysmem,
addr, s->yres, src_width); addr, s->yres, src_width);
} }
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, 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, s->invalidated,
fn, s->dma_ch[0].palette, fn, s->dma_ch[0].palette,
miny, maxy); miny, maxy);
@ -742,10 +1175,7 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
{ {
DisplaySurface *surface = qemu_console_surface(s->con); DisplaySurface *surface = qemu_console_surface(s->con);
int src_width, dest_width; int src_width, dest_width;
drawfn fn = NULL; drawfn fn = pxa2xx_drawfn(s);
if (s->dest_width) {
fn = s->line_fn[s->transp][s->bpp];
}
if (!fn) { if (!fn) {
return; return;
} }
@ -759,14 +1189,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s,
src_width *= 2; src_width *= 2;
} }
dest_width = s->xres * s->dest_width; dest_width = s->xres * DEST_PIXEL_WIDTH;
*miny = 0; *miny = 0;
if (s->invalidated) { if (s->invalidated) {
framebuffer_update_memory_section(&s->fbsection, s->sysmem, framebuffer_update_memory_section(&s->fbsection, s->sysmem,
addr, s->yres, src_width); addr, s->yres, src_width);
} }
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, 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, s->invalidated,
fn, s->dma_ch[0].palette, miny, maxy); 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); DisplaySurface *surface = qemu_console_surface(s->con);
int src_width, dest_width; int src_width, dest_width;
drawfn fn = NULL; drawfn fn = pxa2xx_drawfn(s);
if (s->dest_width) {
fn = s->line_fn[s->transp][s->bpp];
}
if (!fn) { if (!fn) {
return; return;
} }
@ -793,14 +1220,14 @@ static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s,
src_width *= 2; src_width *= 2;
} }
dest_width = s->yres * s->dest_width; dest_width = s->yres * DEST_PIXEL_WIDTH;
*miny = 0; *miny = 0;
if (s->invalidated) { if (s->invalidated) {
framebuffer_update_memory_section(&s->fbsection, s->sysmem, framebuffer_update_memory_section(&s->fbsection, s->sysmem,
addr, s->yres, src_width); addr, s->yres, src_width);
} }
framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, 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, s->invalidated,
fn, s->dma_ch[0].palette, fn, s->dma_ch[0].palette,
miny, maxy); 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 = { static const GraphicHwOps pxa2xx_ops = {
.invalidate = pxa2xx_invalidate_display, .invalidate = pxa2xx_invalidate_display,
.gfx_update = pxa2xx_update_display, .gfx_update = pxa2xx_update_display,
@ -1010,7 +1426,6 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
hwaddr base, qemu_irq irq) hwaddr base, qemu_irq irq)
{ {
PXA2xxLCDState *s; PXA2xxLCDState *s;
DisplaySurface *surface;
s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState));
s->invalidated = 1; s->invalidated = 1;
@ -1024,41 +1439,6 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
memory_region_add_subregion(sysmem, base, &s->iomem); memory_region_add_subregion(sysmem, base, &s->iomem);
s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s); 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); vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s);

View File

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

View File

@ -35,6 +35,7 @@
#include "hw/i386/x86-iommu.h" #include "hw/i386/x86-iommu.h"
#include "hw/pci-host/q35.h" #include "hw/pci-host/q35.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "sysemu/dma.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/i386/apic_internal.h" #include "hw/i386/apic_internal.h"
#include "kvm/kvm_i386.h" #include "kvm/kvm_i386.h"
@ -1884,6 +1885,8 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
case 3: case 3:
mask = 7; /* Mask bit 2:0 in the SID field */ mask = 7; /* Mask bit 2:0 in the SID field */
break; break;
default:
g_assert_not_reached();
} }
mask = ~mask; mask = ~mask;
@ -3453,24 +3456,6 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
return vtd_dev_as; 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. */ /* Unmap the whole range in the notifier's scope. */
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) 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) { while (remain >= VTD_PAGE_SIZE) {
IOMMUTLBEvent event; 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.type = IOMMU_NOTIFIER_UNMAP;
event.entry.iova = start; event.entry.iova = start;
event.entry.addr_mask = mask - 1; event.entry.addr_mask = mask;
event.entry.target_as = &address_space_memory; event.entry.target_as = &address_space_memory;
event.entry.perm = IOMMU_NONE; event.entry.perm = IOMMU_NONE;
/* This field is meaningless for unmap */ /* 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); memory_region_notify_iommu_one(n, &event);
start += mask; start += size;
remain -= mask; remain -= size;
} }
assert(!remain); assert(!remain);

View File

@ -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( softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
'npcm7xx_clk.c', 'npcm7xx_clk.c',
'npcm7xx_gcr.c', 'npcm7xx_gcr.c',
'npcm7xx_mft.c',
'npcm7xx_pwm.c', 'npcm7xx_pwm.c',
'npcm7xx_rng.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_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_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_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_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))

540
hw/misc/npcm7xx_mft.c Normal file
View File

@ -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);

View File

@ -139,6 +139,7 @@ static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p)
trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path, trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path,
p->index, p->duty, duty); p->index, p->duty, duty);
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); SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
int i; int i;
QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->pwm) != NPCM7XX_PWM_PER_MODULE);
for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) { for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
NPCM7xxPWM *p = &s->pwm[i]; NPCM7xxPWM *p = &s->pwm[i];
p->module = s; p->module = s;
@ -501,6 +503,8 @@ static void npcm7xx_pwm_init(Object *obj)
object_property_add_uint32_ptr(obj, "duty[*]", object_property_add_uint32_ptr(obj, "duty[*]",
&s->pwm[i].duty, OBJ_PROP_FLAG_READ); &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 = { static const VMStateDescription vmstate_npcm7xx_pwm = {

View File

@ -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_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_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.c
npcm7xx_rng_read(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" 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" npcm7xx_rng_write(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u"

253
hw/misc/xlnx-versal-xramc.c Normal file
View File

@ -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)

View File

@ -339,23 +339,29 @@ static void allwinner_sun8i_emac_update_irq(AwSun8iEmacState *s)
qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0); qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0);
} }
static uint32_t allwinner_sun8i_emac_next_desc(AwSun8iEmacState *s, static bool allwinner_sun8i_emac_desc_owned(FrameDescriptor *desc,
FrameDescriptor *desc, size_t min_buf_size)
size_t min_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));
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_get_desc(AwSun8iEmacState *s, 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));
}
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_find_desc(AwSun8iEmacState *s,
FrameDescriptor *desc, FrameDescriptor *desc,
uint32_t start_addr, uint32_t start_addr,
size_t min_size) size_t min_size)
@ -364,10 +370,9 @@ static uint32_t allwinner_sun8i_emac_get_desc(AwSun8iEmacState *s,
/* Note that the list is a cycle. Last entry points back to the head. */ /* Note that the list is a cycle. Last entry points back to the head. */
while (desc_addr != 0) { 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) && if (allwinner_sun8i_emac_desc_owned(desc, min_size)) {
(desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) {
return desc_addr; return desc_addr;
} else if (desc->next == start_addr) { } else if (desc->next == start_addr) {
break; break;
@ -383,14 +388,14 @@ static uint32_t allwinner_sun8i_emac_rx_desc(AwSun8iEmacState *s,
FrameDescriptor *desc, FrameDescriptor *desc,
size_t min_size) 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, static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s,
FrameDescriptor *desc, FrameDescriptor *desc)
size_t min_size)
{ {
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, 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; bytes_left -= desc_bytes;
/* Move to the next descriptor */ /* 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) { if (!s->rx_desc_curr) {
/* Not enough buffer space available */ /* Not enough buffer space available */
s->int_sta |= INT_STA_RX_BUF_UA; s->int_sta |= INT_STA_RX_BUF_UA;
@ -495,10 +501,10 @@ static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s)
size_t transmitted = 0; size_t transmitted = 0;
static uint8_t packet_buf[2048]; 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 */ /* 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 */ /* Read from physical memory into packet buffer */
bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK;
@ -524,7 +530,7 @@ static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s)
packet_bytes = 0; packet_bytes = 0;
transmitted++; 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 */ /* Raise transmit completed interrupt */

View File

@ -415,6 +415,7 @@ static void sse_timer_realize(DeviceState *dev, Error **errp)
if (!s->counter) { if (!s->counter) {
error_setg(errp, "counter property was not set"); error_setg(errp, "counter property was not set");
return;
} }
s->counter_notifier.notify = sse_timer_counter_callback; s->counter_notifier.notify = sse_timer_counter_callback;

View File

@ -155,6 +155,7 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
hwaddr virt_end) hwaddr virt_end)
{ {
IOMMUTLBEvent event; IOMMUTLBEvent event;
uint64_t delta = virt_end - virt_start;
if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) { if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) {
return; return;
@ -164,12 +165,24 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
event.type = IOMMU_NOTIFIER_UNMAP; event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.target_as = &address_space_memory; 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.perm = IOMMU_NONE;
event.entry.translated_addr = 0; event.entry.translated_addr = 0;
event.entry.addr_mask = delta;
event.entry.iova = virt_start;
if (delta == UINT64_MAX) {
memory_region_notify_iommu(mr, 0, event); 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, static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value,

View File

@ -18,12 +18,14 @@
#include "hw/boards.h" #include "hw/boards.h"
#include "hw/adc/npcm7xx_adc.h" #include "hw/adc/npcm7xx_adc.h"
#include "hw/core/split-irq.h"
#include "hw/cpu/a9mpcore.h" #include "hw/cpu/a9mpcore.h"
#include "hw/gpio/npcm7xx_gpio.h" #include "hw/gpio/npcm7xx_gpio.h"
#include "hw/i2c/npcm7xx_smbus.h" #include "hw/i2c/npcm7xx_smbus.h"
#include "hw/mem/npcm7xx_mc.h" #include "hw/mem/npcm7xx_mc.h"
#include "hw/misc/npcm7xx_clk.h" #include "hw/misc/npcm7xx_clk.h"
#include "hw/misc/npcm7xx_gcr.h" #include "hw/misc/npcm7xx_gcr.h"
#include "hw/misc/npcm7xx_mft.h"
#include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_pwm.h"
#include "hw/misc/npcm7xx_rng.h" #include "hw/misc/npcm7xx_rng.h"
#include "hw/net/npcm7xx_emc.h" #include "hw/net/npcm7xx_emc.h"
@ -47,8 +49,16 @@
#define NPCM7XX_GIC_CPU_IF_ADDR (0xf03fe100) /* GIC within A9 */ #define NPCM7XX_GIC_CPU_IF_ADDR (0xf03fe100) /* GIC within A9 */
#define NPCM7XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */ #define NPCM7XX_BOARD_SETUP_ADDR (0xffff1000) /* Boot ROM */
#define NPCM7XX_NR_PWM_MODULES 2
typedef struct NPCM7xxMachine { typedef struct NPCM7xxMachine {
MachineState parent; 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; } NPCM7xxMachine;
#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx") #define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
@ -81,7 +91,8 @@ typedef struct NPCM7xxState {
NPCM7xxCLKState clk; NPCM7xxCLKState clk;
NPCM7xxTimerCtrlState tim[3]; NPCM7xxTimerCtrlState tim[3];
NPCM7xxADCState adc; NPCM7xxADCState adc;
NPCM7xxPWMState pwm[2]; NPCM7xxPWMState pwm[NPCM7XX_NR_PWM_MODULES];
NPCM7xxMFTState mft[8];
NPCM7xxOTPState key_storage; NPCM7xxOTPState key_storage;
NPCM7xxOTPState fuse_array; NPCM7xxOTPState fuse_array;
NPCM7xxMCState mc; NPCM7xxMCState mc;

View File

@ -14,6 +14,7 @@
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "hw/arm/boot.h" #include "hw/arm/boot.h"
#include "hw/or-irq.h"
#include "hw/sd/sdhci.h" #include "hw/sd/sdhci.h"
#include "hw/intc/arm_gicv3.h" #include "hw/intc/arm_gicv3.h"
#include "hw/char/pl011.h" #include "hw/char/pl011.h"
@ -22,6 +23,7 @@
#include "hw/rtc/xlnx-zynqmp-rtc.h" #include "hw/rtc/xlnx-zynqmp-rtc.h"
#include "qom/object.h" #include "qom/object.h"
#include "hw/usb/xlnx-usb-subsystem.h" #include "hw/usb/xlnx-usb-subsystem.h"
#include "hw/misc/xlnx-versal-xramc.h"
#define TYPE_XLNX_VERSAL "xlnx-versal" #define TYPE_XLNX_VERSAL "xlnx-versal"
OBJECT_DECLARE_SIMPLE_TYPE(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_GEMS 2
#define XLNX_VERSAL_NR_ADMAS 8 #define XLNX_VERSAL_NR_ADMAS 8
#define XLNX_VERSAL_NR_SDS 2 #define XLNX_VERSAL_NR_SDS 2
#define XLNX_VERSAL_NR_XRAM 4
#define XLNX_VERSAL_NR_IRQS 192 #define XLNX_VERSAL_NR_IRQS 192
struct Versal { struct Versal {
@ -62,6 +65,11 @@ struct Versal {
XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS]; XlnxZDMA adma[XLNX_VERSAL_NR_ADMAS];
VersalUsb2 usb; VersalUsb2 usb;
} iou; } iou;
struct {
qemu_or_irq irq_orgate;
XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM];
} xram;
} lpd; } lpd;
/* The Platform Management Controller subsystem. */ /* The Platform Management Controller subsystem. */
@ -96,6 +104,7 @@ struct Versal {
#define VERSAL_GEM1_IRQ_0 58 #define VERSAL_GEM1_IRQ_0 58
#define VERSAL_GEM1_WAKE_IRQ_0 59 #define VERSAL_GEM1_WAKE_IRQ_0 59
#define VERSAL_ADMA_IRQ_0 60 #define VERSAL_ADMA_IRQ_0 60
#define VERSAL_XRAM_IRQ_0 79
#define VERSAL_RTC_APB_ERR_IRQ 121 #define VERSAL_RTC_APB_ERR_IRQ 121
#define VERSAL_SD0_IRQ_0 126 #define VERSAL_SD0_IRQ_0 126
#define VERSAL_RTC_ALARM_IRQ 142 #define VERSAL_RTC_ALARM_IRQ 142
@ -128,6 +137,10 @@ struct Versal {
#define MM_OCM 0xfffc0000U #define MM_OCM 0xfffc0000U
#define MM_OCM_SIZE 0x40000 #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 0xFF9D0000
#define MM_USB2_CTRL_REGS_SIZE 0x10000 #define MM_USB2_CTRL_REGS_SIZE 0x10000

View File

@ -128,6 +128,7 @@ typedef struct {
* @kvm_type: * @kvm_type:
* Return the type of KVM corresponding to the kvm-type string option or * Return the type of KVM corresponding to the kvm-type string option or
* computed based on other criteria such as the host kernel capabilities. * computed based on other criteria such as the host kernel capabilities.
* kvm-type may be NULL if it is not needed.
* @numa_mem_supported: * @numa_mem_supported:
* true if '--numa node.mem' option is supported and false otherwise * true if '--numa node.mem' option is supported and false otherwise
* @smp_parse: * @smp_parse:

View File

@ -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 */

View File

@ -77,6 +77,7 @@ typedef struct NPCM7xxPWM {
* @iomem: Memory region through which registers are accessed. * @iomem: Memory region through which registers are accessed.
* @clock: The PWM clock. * @clock: The PWM clock.
* @pwm: The PWM channels owned by this module. * @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. * @ppr: The prescaler register.
* @csr: The clock selector register. * @csr: The clock selector register.
* @pcr: The control register. * @pcr: The control register.
@ -90,6 +91,7 @@ struct NPCM7xxPWMState {
Clock *clock; 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 ppr;
uint32_t csr; uint32_t csr;

View File

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

View File

@ -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, void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
QEMUSGList *sg, enum BlockAcctType type); 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 #endif

View File

@ -330,3 +330,29 @@ void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
{ {
block_acct_start(blk_get_stats(blk), cookie, sg->size, type); 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;
}
}

View File

@ -230,12 +230,14 @@ bool kvm_arm_pmu_supported(void)
return kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3); 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); KVMState *s = KVM_STATE(ms->accelerator);
int ret; int ret;
ret = kvm_check_extension(s, KVM_CAP_ARM_VM_IPA_SIZE); ret = kvm_check_extension(s, KVM_CAP_ARM_VM_IPA_SIZE);
*fixed_ipa = ret <= 0;
return ret > 0 ? ret : 40; return ret > 0 ? ret : 40;
} }

View File

@ -311,10 +311,12 @@ bool kvm_arm_sve_supported(void);
/** /**
* kvm_arm_get_max_vm_ipa_size: * kvm_arm_get_max_vm_ipa_size:
* @ms: Machine state handle * @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 * 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: * kvm_arm_sync_mpstate_to_kvm:
@ -409,7 +411,7 @@ static inline void kvm_arm_add_vcpu_properties(Object *obj)
g_assert_not_reached(); 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(); g_assert_not_reached();
} }

View File

@ -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); intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ);
int esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); int esz = FIELD_EX32(pred_desc, PREDDESC, ESZ);
intptr_t high = FIELD_EX32(pred_desc, PREDDESC, DATA); intptr_t high = FIELD_EX32(pred_desc, PREDDESC, DATA);
int esize = 1 << esz;
uint64_t *d = vd; uint64_t *d = vd;
intptr_t i; 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); mm = extract64(mm, high * half, half);
nn = expand_bits(nn, esz); nn = expand_bits(nn, esz);
mm = expand_bits(mm, esz); mm = expand_bits(mm, esz);
d[0] = nn + (mm << (1 << esz)); d[0] = nn | (mm << esize);
} else { } else {
ARMPredicateReg tmp_n, tmp_m; ARMPredicateReg tmp;
/* We produce output faster than we consume input. /* We produce output faster than we consume input.
Therefore we must be mindful of possible overlap. */ Therefore we must be mindful of possible overlap. */
if ((vn - vd) < (uintptr_t)oprsz) { if (vd == vn) {
vn = memcpy(&tmp_n, vn, oprsz); vn = memcpy(&tmp, vn, oprsz);
if (vd == vm) {
vm = vn;
} }
if ((vm - vd) < (uintptr_t)oprsz) { } else if (vd == vm) {
vm = memcpy(&tmp_m, vm, oprsz); vm = memcpy(&tmp, vm, oprsz);
} }
if (high) { if (high) {
high = oprsz >> 1; high = oprsz >> 1;
} }
if ((high & 3) == 0) { if ((oprsz & 7) == 0) {
uint32_t *n = vn, *m = vm; uint32_t *n = vn, *m = vm;
high >>= 2; 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 nn = n[H4(high + i)];
uint64_t mm = m[H4(high + i)]; uint64_t mm = m[H4(high + i)];
nn = expand_bits(nn, esz); nn = expand_bits(nn, esz);
mm = expand_bits(mm, esz); mm = expand_bits(mm, esz);
d[i] = nn + (mm << (1 << esz)); d[i] = nn | (mm << esize);
} }
} else { } else {
uint8_t *n = vn, *m = vm; 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); nn = expand_bits(nn, esz);
mm = expand_bits(mm, 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) { if (oprsz <= 8) {
l = compress_bits(n[0] >> odd, esz); l = compress_bits(n[0] >> odd, esz);
h = compress_bits(m[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 { } else {
ARMPredicateReg tmp_m; ARMPredicateReg tmp_m;
intptr_t oprsz_16 = oprsz / 16; 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]; h = n[2 * i + 1];
l = compress_bits(l >> odd, esz); l = compress_bits(l >> odd, esz);
h = compress_bits(h >> 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 * For VL which is not a multiple of 512, the results from M do not
from M into TMP_M and then copy it into place afterward. */ * 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) { 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++) { for (i = 0; i < oprsz_16; i++) {
l = m[2 * i + 0]; l = m[2 * i + 0];
h = m[2 * i + 1]; h = m[2 * i + 1];
l = compress_bits(l >> odd, esz); l = compress_bits(l >> odd, esz);
h = compress_bits(h >> 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); swap_memmove(vd + oprsz / 2, &tmp_m, oprsz / 2);
} else { } 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]; h = m[2 * i + 1];
l = compress_bits(l >> odd, esz); l = compress_bits(l >> odd, esz);
h = compress_bits(h >> 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; high = oprsz >> 1;
} }
if ((high & 3) == 0) { if ((oprsz & 7) == 0) {
uint32_t *n = vn; uint32_t *n = vn;
high >>= 2; 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 nn = n[H4(high + i)];
d[i] = expand_bits(nn, 0); 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) 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 words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8);
intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); 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) 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, void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg,
uint32_t pred_desc) 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)) { if (last_active_pred(vn, vg, oprsz)) {
compute_brk_z(vd, vm, vg, oprsz, true); compute_brk_z(vd, vm, vg, oprsz, true);
} else { } 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 HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg,
uint32_t pred_desc) 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)) { if (last_active_pred(vn, vg, oprsz)) {
return compute_brks_z(vd, vm, vg, oprsz, true); return compute_brks_z(vd, vm, vg, oprsz, true);
} else { } 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, void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg,
uint32_t pred_desc) 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)) { if (last_active_pred(vn, vg, oprsz)) {
compute_brk_z(vd, vm, vg, oprsz, false); compute_brk_z(vd, vm, vg, oprsz, false);
} else { } 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 HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg,
uint32_t pred_desc) 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)) { if (last_active_pred(vn, vg, oprsz)) {
return compute_brks_z(vd, vm, vg, oprsz, false); return compute_brks_z(vd, vm, vg, oprsz, false);
} else { } 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) 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); compute_brk_z(vd, vn, vg, oprsz, true);
} }
uint32_t HELPER(sve_brkas_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); return compute_brks_z(vd, vn, vg, oprsz, true);
} }
void HELPER(sve_brkb_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); compute_brk_z(vd, vn, vg, oprsz, false);
} }
uint32_t HELPER(sve_brkbs_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); return compute_brks_z(vd, vn, vg, oprsz, false);
} }
void HELPER(sve_brka_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); compute_brk_m(vd, vn, vg, oprsz, true);
} }
uint32_t HELPER(sve_brkas_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); return compute_brks_m(vd, vn, vg, oprsz, true);
} }
void HELPER(sve_brkb_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); compute_brk_m(vd, vn, vg, oprsz, false);
} }
uint32_t HELPER(sve_brkbs_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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); return compute_brks_m(vd, vn, vg, oprsz, false);
} }
void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) 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)) { if (!last_active_pred(vn, vg, oprsz)) {
do_zero(vd, 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) 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)) { if (last_active_pred(vn, vg, oprsz)) {
return predtest_ones(vd, oprsz, -1); return predtest_ones(vd, oprsz, -1);
} else { } 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) 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 words = DIV_ROUND_UP(FIELD_EX32(pred_desc, PREDDESC, OPRSZ), 8);
intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ);
uint64_t *n = vn, *g = vg, sum = 0, mask = pred_esz_masks[esz]; uint64_t *n = vn, *g = vg, sum = 0, mask = pred_esz_masks[esz];
intptr_t i; 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; uint64_t t = n[i] & g[i] & mask;
sum += ctpop64(t); 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) 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 oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ);
intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ);
uint64_t esz_mask = pred_esz_masks[esz]; uint64_t esz_mask = pred_esz_masks[esz];
ARMPredicateReg *d = vd; ARMPredicateReg *d = vd;
uint32_t flags; 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) \ 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)]; \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \
for (i = 0; i < oprsz; ) { \ for (i = 0; i < oprsz; ) { \
uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \

View File

@ -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_ptr t_p = tcg_temp_new_ptr();
TCGv_i32 t_desc; TCGv_i32 t_desc;
unsigned vsz = pred_full_reg_size(s); unsigned desc = 0;
unsigned desc;
desc = vsz - 2; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s));
desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); desc = FIELD_DP32(desc, PREDDESC, ESZ, esz);
tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg));
t_desc = tcg_const_i32(desc); 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 n = tcg_temp_new_ptr();
TCGv_ptr m = tcg_temp_new_ptr(); TCGv_ptr m = tcg_temp_new_ptr();
TCGv_ptr g = 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(d, cpu_env, pred_full_reg_offset(s, a->rd));
tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); 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 d = tcg_temp_new_ptr();
TCGv_ptr n = tcg_temp_new_ptr(); TCGv_ptr n = tcg_temp_new_ptr();
TCGv_ptr g = 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(d, cpu_env, pred_full_reg_offset(s, a->rd));
tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); 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 { } else {
TCGv_ptr t_pn = tcg_temp_new_ptr(); TCGv_ptr t_pn = tcg_temp_new_ptr();
TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr();
unsigned desc; unsigned desc = 0;
TCGv_i32 t_desc; TCGv_i32 t_desc;
desc = psz - 2; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, psz);
desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); 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_pn, cpu_env, pred_full_reg_offset(s, pn));
tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); 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_i64 op0, op1, t0, t1, tmax;
TCGv_i32 t2, t3; TCGv_i32 t2, t3;
TCGv_ptr ptr; TCGv_ptr ptr;
unsigned desc, vsz = vec_full_reg_size(s); unsigned vsz = vec_full_reg_size(s);
unsigned desc = 0;
TCGCond cond; TCGCond cond;
if (!sve_access_check(s)) { if (!sve_access_check(s)) {
@ -3162,8 +3162,8 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a)
/* Scale elements to bits. */ /* Scale elements to bits. */
tcg_gen_shli_i32(t2, t2, a->esz); tcg_gen_shli_i32(t2, t2, a->esz);
desc = (vsz / 8) - 2; desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8);
desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz);
t3 = tcg_const_i32(desc); t3 = tcg_const_i32(desc);
ptr = tcg_temp_new_ptr(); 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 vsz = vec_full_reg_size(s);
unsigned p2vsz = pow2ceil(vsz); 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_ptr t_zn, t_pg, status;
TCGv_i64 temp; TCGv_i64 temp;

View File

@ -507,20 +507,18 @@ class BootLinuxConsole(LinuxKernelTest):
self.wait_for_console_pattern('Boot successful.') self.wait_for_console_pattern('Boot successful.')
# TODO user command, for now the uart is stuck # 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): def test_arm_cubieboard_initrd(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
:avocado: tags=machine:cubieboard :avocado: tags=machine:cubieboard
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
initrd_url = ('https://github.com/groeck/linux-build-test/raw/' initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
'2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
@ -549,20 +547,18 @@ class BootLinuxConsole(LinuxKernelTest):
'system-control@1c00000') 'system-control@1c00000')
# cubieboard's reboot is not functioning; omit reboot test. # 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): def test_arm_cubieboard_sata(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
:avocado: tags=machine:cubieboard :avocado: tags=machine:cubieboard
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' rootfs_url = ('https://github.com/groeck/linux-build-test/raw/'
'2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
@ -678,20 +674,18 @@ class BootLinuxConsole(LinuxKernelTest):
self.wait_for_console_pattern( self.wait_for_console_pattern(
'Give root password for system maintenance') '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): def test_arm_orangepi(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc :avocado: tags=machine:orangepi-pc
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
self.vm.set_console() self.vm.set_console()
@ -705,20 +699,18 @@ class BootLinuxConsole(LinuxKernelTest):
console_pattern = 'Kernel command line: %s' % kernel_command_line console_pattern = 'Kernel command line: %s' % kernel_command_line
self.wait_for_console_pattern(console_pattern) 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): def test_arm_orangepi_initrd(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc :avocado: tags=machine:orangepi-pc
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
initrd_url = ('https://github.com/groeck/linux-build-test/raw/' initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
'2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'
@ -749,8 +741,6 @@ class BootLinuxConsole(LinuxKernelTest):
# Wait for VM to shut down gracefully # Wait for VM to shut down gracefully
self.vm.wait() self.vm.wait()
@skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'),
'Test artifacts fetched from unreliable apt.armbian.com')
def test_arm_orangepi_sd(self): def test_arm_orangepi_sd(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
@ -758,12 +748,12 @@ class BootLinuxConsole(LinuxKernelTest):
:avocado: tags=device:sd :avocado: tags=device:sd
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun8i-h3-orangepi-pc.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/'
'kci-2019.02/armel/base/rootfs.ext2.xz') 'kci-2019.02/armel/base/rootfs.ext2.xz')
@ -802,7 +792,27 @@ class BootLinuxConsole(LinuxKernelTest):
# Wait for VM to shut down gracefully # Wait for VM to shut down gracefully
self.vm.wait() 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.set_console()
self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
'-nic', 'user', '-nic', 'user',
@ -828,54 +838,6 @@ class BootLinuxConsole(LinuxKernelTest):
'to <orangepipc>') 'to <orangepipc>')
self.wait_for_console_pattern('Starting Load Kernel Modules...') 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') @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
def test_arm_orangepi_uboot_netbsd9(self): def test_arm_orangepi_uboot_netbsd9(self):
""" """

View File

@ -177,20 +177,18 @@ class ReplayKernelNormal(ReplayKernelBase):
self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1)
@skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') @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): def test_arm_cubieboard_initrd(self):
""" """
:avocado: tags=arch:arm :avocado: tags=arch:arm
:avocado: tags=machine:cubieboard :avocado: tags=machine:cubieboard
""" """
deb_url = ('https://apt.armbian.com/pool/main/l/' deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') 'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' deb_hash = '9fa84beda245cabf0b4fa84cf6eaa7738ead1da0'
deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash)
kernel_path = self.extract_from_deb(deb_path, kernel_path = self.extract_from_deb(deb_path,
'/boot/vmlinuz-4.20.7-sunxi') '/boot/vmlinuz-5.10.16-sunxi')
dtb_path = '/usr/lib/linux-image-dev-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = '/usr/lib/linux-image-current-sunxi/sun4i-a10-cubieboard.dtb'
dtb_path = self.extract_from_deb(deb_path, dtb_path) dtb_path = self.extract_from_deb(deb_path, dtb_path)
initrd_url = ('https://github.com/groeck/linux-build-test/raw/' initrd_url = ('https://github.com/groeck/linux-build-test/raw/'
'2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/'

View File

@ -45,6 +45,7 @@
#define PLL_FBDV(rv) extract32((rv), 16, 12) #define PLL_FBDV(rv) extract32((rv), 16, 12)
#define PLL_OTDV1(rv) extract32((rv), 8, 3) #define PLL_OTDV1(rv) extract32((rv), 8, 3)
#define PLL_OTDV2(rv) extract32((rv), 13, 3) #define PLL_OTDV2(rv) extract32((rv), 13, 3)
#define APB4CKDIV(rv) extract32((rv), 30, 2)
#define APB3CKDIV(rv) extract32((rv), 28, 2) #define APB3CKDIV(rv) extract32((rv), 28, 2)
#define CLK2CKDIV(rv) extract32((rv), 0, 1) #define CLK2CKDIV(rv) extract32((rv), 0, 1)
#define CLK4CKDIV(rv) extract32((rv), 26, 2) #define CLK4CKDIV(rv) extract32((rv), 26, 2)
@ -52,6 +53,49 @@
#define MAX_DUTY 1000000 #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 { typedef struct PWMModule {
int irq; int irq;
uint64_t base_addr; 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); 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) static uint32_t get_pll(uint32_t con)
{ {
return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con) return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
* PLL_OTDV2(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; uint64_t freq = REF_HZ;
uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL); uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
uint32_t pllcon; uint32_t pllcon;
uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1); uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2); uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
switch (CPUCKSEL(clksel)) { switch (CPUCKSEL(clksel)) {
case 0: case 0:
@ -241,7 +302,7 @@ static uint64_t read_pclk(QTestState *qts)
g_assert_not_reached(); g_assert_not_reached();
} }
freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv2)); freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
return freq; 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, static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
uint32_t cnr) 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) 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); 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) 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); 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); 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 */ /* Check pwm registers can be reset to default value */
static void test_init(gconstpointer test_data) static void test_init(gconstpointer test_data)
{ {
const TestData *td = 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 module = pwm_module_index(td->module);
int pwm = pwm_index(td->pwm); int pwm = pwm_index(td->pwm);
@ -369,7 +557,7 @@ static void test_init(gconstpointer test_data)
static void test_oneshot(gconstpointer test_data) static void test_oneshot(gconstpointer test_data)
{ {
const TestData *td = 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 module = pwm_module_index(td->module);
int pwm = pwm_index(td->pwm); int pwm = pwm_index(td->pwm);
uint32_t ppr, csr, pcr; uint32_t ppr, csr, pcr;
@ -400,13 +588,15 @@ static void test_oneshot(gconstpointer test_data)
static void test_toggle(gconstpointer test_data) static void test_toggle(gconstpointer test_data)
{ {
const TestData *td = 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 module = pwm_module_index(td->module);
int pwm = pwm_index(td->pwm); int pwm = pwm_index(td->pwm);
uint32_t ppr, csr, pcr, cnr, cmr; uint32_t ppr, csr, pcr, cnr, cmr;
int i, j, k, l; int i, j, k, l;
uint64_t expected_freq, expected_duty; uint64_t expected_freq, expected_duty;
mft_init(qts, td);
pcr = CH_EN | CH_MOD; pcr = CH_EN | CH_MOD;
for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) { for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
ppr = ppr_list[i]; ppr = ppr_list[i];
@ -440,6 +630,9 @@ static void test_toggle(gconstpointer test_data)
==, expected_freq); ==, expected_freq);
} }
/* Test MFT's RPM is correct. */
mft_verify_rpm(qts, td, expected_duty);
/* Test inverted mode */ /* Test inverted mode */
expected_duty = pwm_compute_duty(cnr, cmr, true); expected_duty = pwm_compute_duty(cnr, cmr, true);
pwm_write_pcr(qts, td, pcr | CH_INV); pwm_write_pcr(qts, td, pcr | CH_INV);