mirror of https://github.com/xemu-project/xemu.git
ppc patch queue for 2022-10-29:
This queue has the second part of the ppc4xx_sdram cleanups, doorbell instructions for POWER8, new pflash handling for the e500 machine and a Radix MMU regression fix. It also has a lot of performance optimizations in the PowerPC emulation done by the researchers of the Eldorado institute. Between using gvec for VMX/VSX instructions, a full rework of the interrupt model and PMU optimizations, they managed to drastically speed up the emulation of powernv8/9/10 machines. Here's an example with avocado tests: - with master: tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8: PASS (38.89 s) tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9: PASS (43.89 s) - with this queue applied: tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8: PASS (21.23 s) tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9: PASS (22.58 s) Other ppc machines, like pseries, also had a noticeable performance boost. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCY10J/gAKCRA82cqW3gMx ZAbjAPwKNbE1wE2POJbMALBQAM5MewwLMV/UKGjE6jA7HAbb/AEA9e3o11FoUmSJ rZkmTvMzBQZ81mMGRlS0cnqbrr4ADgc= =gnKY -----END PGP SIGNATURE----- Merge tag 'pull-ppc-20221029' of https://gitlab.com/danielhb/qemu into staging ppc patch queue for 2022-10-29: This queue has the second part of the ppc4xx_sdram cleanups, doorbell instructions for POWER8, new pflash handling for the e500 machine and a Radix MMU regression fix. It also has a lot of performance optimizations in the PowerPC emulation done by the researchers of the Eldorado institute. Between using gvec for VMX/VSX instructions, a full rework of the interrupt model and PMU optimizations, they managed to drastically speed up the emulation of powernv8/9/10 machines. Here's an example with avocado tests: - with master: tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8: PASS (38.89 s) tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9: PASS (43.89 s) - with this queue applied: tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv8: PASS (21.23 s) tests/avocado/boot_linux_console.py:BootLinuxConsole.test_ppc_powernv9: PASS (22.58 s) Other ppc machines, like pseries, also had a noticeable performance boost. # -----BEGIN PGP SIGNATURE----- # # iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCY10J/gAKCRA82cqW3gMx # ZAbjAPwKNbE1wE2POJbMALBQAM5MewwLMV/UKGjE6jA7HAbb/AEA9e3o11FoUmSJ # rZkmTvMzBQZ81mMGRlS0cnqbrr4ADgc= # =gnKY # -----END PGP SIGNATURE----- # gpg: Signature made Sat 29 Oct 2022 07:09:50 EDT # gpg: using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164 # gpg: Good signature from "Daniel Henrique Barboza <danielhb413@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 17EB FF99 23D0 1800 AF28 3819 3CD9 CA96 DE03 3164 * tag 'pull-ppc-20221029' of https://gitlab.com/danielhb/qemu: (63 commits) target/ppc: Fix regression in Radix MMU hw/ppc/e500: Implement pflash handling hw/sd/sdhci: Rename ESDHC_* defines to USDHC_* hw/sd/sdhci-internal: Unexport ESDHC defines hw/block/pflash_cfi0{1, 2}: Error out if device length isn't a power of two docs/system/ppc/ppce500: Use qemu-system-ppc64 across the board(s) target/ppc: Increment PMC5 with inline insns target/ppc: Add new PMC HFLAGS ppc4xx_sdram: Add errp parameter to ppc4xx_sdram_banks() ppc4xx_sdram: Convert DDR SDRAM controller to new bank handling ppc4xx_sdram: Generalise bank setup ppc4xx_sdram: Rename local state variable for brevity ppc4xx_sdram: Use hwaddr for memory bank size ppc4xx_sdram: Move ppc4xx_sdram_banks() to ppc4xx_sdram.c ppc4xx_devs.c: Move DDR SDRAM controller model to ppc4xx_sdram.c ppc440_uc.c: Move DDR2 SDRAM controller model to ppc4xx_sdram.c target/ppc: move the p*_interrupt_powersave methods to excp_helper.c target/ppc: unify cpu->has_work based on cs->interrupt_request target/ppc: introduce ppc_maybe_interrupt target/ppc: remove ppc_store_lpcr from CONFIG_USER_ONLY builds ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
179938097d
|
@ -113,7 +113,7 @@ To boot the 32-bit Linux kernel:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc{64|32} -M ppce500 -cpu e500mc -smp 4 -m 2G \
|
||||
$ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel vmlinux \
|
||||
-initrd /path/to/rootfs.cpio \
|
||||
|
@ -154,10 +154,10 @@ interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc -M ppce500 -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-bios u-boot \
|
||||
-nic tap,ifname=tap0,script=no,downscript=no,model=e1000
|
||||
$ qemu-system-ppc64 -M ppce500 -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-bios u-boot \
|
||||
-nic tap,ifname=tap0,script=no,downscript=no,model=e1000
|
||||
|
||||
The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device
|
||||
if “-device eTSEC” is given to QEMU:
|
||||
|
@ -165,3 +165,18 @@ if “-device eTSEC” is given to QEMU:
|
|||
.. code-block:: bash
|
||||
|
||||
-netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0
|
||||
|
||||
Root file system on flash drive
|
||||
-------------------------------
|
||||
|
||||
Rather than using a root file system on ram disk, it is possible to have it on
|
||||
CFI flash. Given an ext2 image whose size must be a power of two, it can be used
|
||||
as follows:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc64 -M ppce500 -cpu e500mc -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel vmlinux \
|
||||
-drive if=pflash,file=/path/to/rootfs.ext2,format=raw \
|
||||
-append "rootwait root=/dev/mtdblock0"
|
||||
|
|
|
@ -690,7 +690,7 @@ static const MemoryRegionOps pflash_cfi01_ops = {
|
|||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl)
|
||||
static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl, Error **errp)
|
||||
{
|
||||
uint64_t blocks_per_device, sector_len_per_device, device_len;
|
||||
int num_devices;
|
||||
|
@ -708,6 +708,10 @@ static void pflash_cfi01_fill_cfi_table(PFlashCFI01 *pfl)
|
|||
sector_len_per_device = pfl->sector_len / num_devices;
|
||||
}
|
||||
device_len = sector_len_per_device * blocks_per_device;
|
||||
if (!is_power_of_2(device_len)) {
|
||||
error_setg(errp, "Device size must be a power of two.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hardcoded CFI table */
|
||||
/* Standard "QRY" string */
|
||||
|
@ -865,7 +869,7 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
|
|||
*/
|
||||
pfl->cmd = 0x00;
|
||||
pfl->status = 0x80; /* WSM ready */
|
||||
pflash_cfi01_fill_cfi_table(pfl);
|
||||
pflash_cfi01_fill_cfi_table(pfl, errp);
|
||||
}
|
||||
|
||||
static void pflash_cfi01_system_reset(DeviceState *dev)
|
||||
|
|
|
@ -880,6 +880,11 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(pfl->chip_len)) {
|
||||
error_setg(errp, "Device size must be a power of two.");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl),
|
||||
&pflash_cfi02_ops, pfl, pfl->name,
|
||||
pfl->chip_len, errp);
|
||||
|
|
|
@ -126,6 +126,7 @@ config E500
|
|||
select ETSEC
|
||||
select GPIO_MPC8XXX
|
||||
select OPENPIC
|
||||
select PFLASH_CFI01
|
||||
select PLATFORM_BUS
|
||||
select PPCE500_PCI
|
||||
select SERIAL
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
#include "e500-ccsr.h"
|
||||
#include "net/net.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "sysemu/block-backend-io.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/reset.h"
|
||||
|
@ -267,6 +269,31 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
static void create_devtree_flash(SysBusDevice *sbdev,
|
||||
PlatformDevtreeData *data)
|
||||
{
|
||||
g_autofree char *name = NULL;
|
||||
uint64_t num_blocks = object_property_get_uint(OBJECT(sbdev),
|
||||
"num-blocks",
|
||||
&error_fatal);
|
||||
uint64_t sector_length = object_property_get_uint(OBJECT(sbdev),
|
||||
"sector-length",
|
||||
&error_fatal);
|
||||
uint64_t bank_width = object_property_get_uint(OBJECT(sbdev),
|
||||
"width",
|
||||
&error_fatal);
|
||||
hwaddr flashbase = 0;
|
||||
hwaddr flashsize = num_blocks * sector_length;
|
||||
void *fdt = data->fdt;
|
||||
|
||||
name = g_strdup_printf("%s/nor@%" PRIx64, data->node, flashbase);
|
||||
qemu_fdt_add_subnode(fdt, name);
|
||||
qemu_fdt_setprop_string(fdt, name, "compatible", "cfi-flash");
|
||||
qemu_fdt_setprop_sized_cells(fdt, name, "reg",
|
||||
1, flashbase, 1, flashsize);
|
||||
qemu_fdt_setprop_cell(fdt, name, "bank-width", bank_width);
|
||||
}
|
||||
|
||||
static void platform_bus_create_devtree(PPCE500MachineState *pms,
|
||||
void *fdt, const char *mpic)
|
||||
{
|
||||
|
@ -276,6 +303,8 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms,
|
|||
uint64_t addr = pmc->platform_bus_base;
|
||||
uint64_t size = pmc->platform_bus_size;
|
||||
int irq_start = pmc->platform_bus_first_irq;
|
||||
SysBusDevice *sbdev;
|
||||
bool ambiguous;
|
||||
|
||||
/* Create a /platform node that we can put all devices into */
|
||||
|
||||
|
@ -302,6 +331,13 @@ static void platform_bus_create_devtree(PPCE500MachineState *pms,
|
|||
/* Loop through all dynamic sysbus devices and create nodes for them */
|
||||
foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data);
|
||||
|
||||
sbdev = SYS_BUS_DEVICE(object_resolve_path_type("", TYPE_PFLASH_CFI01,
|
||||
&ambiguous));
|
||||
if (sbdev) {
|
||||
assert(!ambiguous);
|
||||
create_devtree_flash(sbdev, &data);
|
||||
}
|
||||
|
||||
g_free(node);
|
||||
}
|
||||
|
||||
|
@ -856,6 +892,7 @@ void ppce500_init(MachineState *machine)
|
|||
unsigned int pci_irq_nrs[PCI_NUM_PINS] = {1, 2, 3, 4};
|
||||
IrqLines *irqs;
|
||||
DeviceState *dev, *mpicdev;
|
||||
DriveInfo *dinfo;
|
||||
CPUPPCState *firstenv = NULL;
|
||||
MemoryRegion *ccsr_addr_space;
|
||||
SysBusDevice *s;
|
||||
|
@ -1024,6 +1061,48 @@ void ppce500_init(MachineState *machine)
|
|||
pmc->platform_bus_base,
|
||||
&pms->pbus_dev->mmio);
|
||||
|
||||
dinfo = drive_get(IF_PFLASH, 0, 0);
|
||||
if (dinfo) {
|
||||
BlockBackend *blk = blk_by_legacy_dinfo(dinfo);
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
uint64_t mmio_size = memory_region_size(&pms->pbus_dev->mmio);
|
||||
uint64_t size = bdrv_getlength(bs);
|
||||
uint32_t sector_len = 64 * KiB;
|
||||
|
||||
if (!is_power_of_2(size)) {
|
||||
error_report("Size of pflash file must be a power of two.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (size > mmio_size) {
|
||||
error_report("Size of pflash file must not be bigger than %" PRIu64
|
||||
" bytes.", mmio_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(size, sector_len)) {
|
||||
error_report("Size of pflash file must be a multiple of %" PRIu32
|
||||
".", sector_len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dev = qdev_new(TYPE_PFLASH_CFI01);
|
||||
qdev_prop_set_drive(dev, "drive", blk);
|
||||
qdev_prop_set_uint32(dev, "num-blocks", size / sector_len);
|
||||
qdev_prop_set_uint64(dev, "sector-length", sector_len);
|
||||
qdev_prop_set_uint8(dev, "width", 2);
|
||||
qdev_prop_set_bit(dev, "big-endian", true);
|
||||
qdev_prop_set_uint16(dev, "id0", 0x89);
|
||||
qdev_prop_set_uint16(dev, "id1", 0x18);
|
||||
qdev_prop_set_uint16(dev, "id2", 0x0000);
|
||||
qdev_prop_set_uint16(dev, "id3", 0x0);
|
||||
qdev_prop_set_string(dev, "name", "e500.flash");
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
memory_region_add_subregion(&pms->pbus_dev->mmio, 0,
|
||||
pflash_cfi01_get_memory(PFLASH_CFI01(dev)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Smart firmware defaults ahead!
|
||||
*
|
||||
|
|
|
@ -59,8 +59,9 @@ ppc_ss.add(when: 'CONFIG_PPC440', if_true: files(
|
|||
'ppc440_bamboo.c',
|
||||
'ppc440_pcix.c', 'ppc440_uc.c'))
|
||||
ppc_ss.add(when: 'CONFIG_PPC4XX', if_true: files(
|
||||
'ppc4xx_devs.c',
|
||||
'ppc4xx_pci.c',
|
||||
'ppc4xx_devs.c'))
|
||||
'ppc4xx_sdram.c'))
|
||||
ppc_ss.add(when: 'CONFIG_SAM460EX', if_true: files('sam460ex.c'))
|
||||
# PReP
|
||||
ppc_ss.add(when: 'CONFIG_PREP', if_true: files('prep.c'))
|
||||
|
|
|
@ -58,6 +58,7 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu)
|
|||
env->msr |= MSR_HVB; /* Hypervisor mode */
|
||||
env->spr[SPR_HRMOR] = pc->hrmor;
|
||||
hreg_compute_hflags(env);
|
||||
ppc_maybe_interrupt(env);
|
||||
|
||||
pcc->intc_reset(pc->chip, cpu);
|
||||
}
|
||||
|
|
17
hw/ppc/ppc.c
17
hw/ppc/ppc.c
|
@ -40,9 +40,8 @@
|
|||
static void cpu_ppc_tb_stop (CPUPPCState *env);
|
||||
static void cpu_ppc_tb_start (CPUPPCState *env);
|
||||
|
||||
void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
|
||||
void ppc_set_irq(PowerPCCPU *cpu, int irq, int level)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
unsigned int old_pending;
|
||||
bool locked = false;
|
||||
|
@ -56,21 +55,17 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
|
|||
old_pending = env->pending_interrupts;
|
||||
|
||||
if (level) {
|
||||
env->pending_interrupts |= 1 << n_IRQ;
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
env->pending_interrupts |= irq;
|
||||
} else {
|
||||
env->pending_interrupts &= ~(1 << n_IRQ);
|
||||
if (env->pending_interrupts == 0) {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
env->pending_interrupts &= ~irq;
|
||||
}
|
||||
|
||||
if (old_pending != env->pending_interrupts) {
|
||||
kvmppc_set_interrupt(cpu, n_IRQ, level);
|
||||
ppc_maybe_interrupt(env);
|
||||
kvmppc_set_interrupt(cpu, irq, level);
|
||||
}
|
||||
|
||||
|
||||
trace_ppc_irq_set_exit(env, n_IRQ, level, env->pending_interrupts,
|
||||
trace_ppc_irq_set_exit(env, irq, level, env->pending_interrupts,
|
||||
CPU(cpu)->interrupt_request);
|
||||
|
||||
if (locked) {
|
||||
|
|
|
@ -10,21 +10,14 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/irq.h"
|
||||
#include "exec/memory.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "ppc440.h"
|
||||
#include "qom/object.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* L2 Cache as SRAM */
|
||||
|
@ -478,331 +471,6 @@ void ppc4xx_sdr_init(CPUPPCState *env)
|
|||
sdr, &dcr_read_sdr, &dcr_write_sdr);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SDRAM controller */
|
||||
enum {
|
||||
SDRAM0_CFGADDR = 0x10,
|
||||
SDRAM0_CFGDATA,
|
||||
SDRAM_R0BAS = 0x40,
|
||||
SDRAM_R1BAS,
|
||||
SDRAM_R2BAS,
|
||||
SDRAM_R3BAS,
|
||||
SDRAM_CONF1HB = 0x45,
|
||||
SDRAM_PLBADDULL = 0x4a,
|
||||
SDRAM_CONF1LL = 0x4b,
|
||||
SDRAM_CONFPATHB = 0x4f,
|
||||
SDRAM_PLBADDUHB = 0x50,
|
||||
};
|
||||
|
||||
static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||
{
|
||||
uint32_t bcr;
|
||||
|
||||
switch (ram_size) {
|
||||
case 8 * MiB:
|
||||
bcr = 0xffc0;
|
||||
break;
|
||||
case 16 * MiB:
|
||||
bcr = 0xff80;
|
||||
break;
|
||||
case 32 * MiB:
|
||||
bcr = 0xff00;
|
||||
break;
|
||||
case 64 * MiB:
|
||||
bcr = 0xfe00;
|
||||
break;
|
||||
case 128 * MiB:
|
||||
bcr = 0xfc00;
|
||||
break;
|
||||
case 256 * MiB:
|
||||
bcr = 0xf800;
|
||||
break;
|
||||
case 512 * MiB:
|
||||
bcr = 0xf000;
|
||||
break;
|
||||
case 1 * GiB:
|
||||
bcr = 0xe000;
|
||||
break;
|
||||
case 2 * GiB:
|
||||
bcr = 0xc000;
|
||||
break;
|
||||
case 4 * GiB:
|
||||
bcr = 0x8000;
|
||||
break;
|
||||
default:
|
||||
error_report("invalid RAM size " TARGET_FMT_plx, ram_size);
|
||||
return 0;
|
||||
}
|
||||
bcr |= ram_base >> 2 & 0xffe00000;
|
||||
bcr |= 1;
|
||||
|
||||
return bcr;
|
||||
}
|
||||
|
||||
static inline hwaddr sdram_ddr2_base(uint32_t bcr)
|
||||
{
|
||||
return (bcr & 0xffe00000) << 2;
|
||||
}
|
||||
|
||||
static uint64_t sdram_ddr2_size(uint32_t bcr)
|
||||
{
|
||||
uint64_t size;
|
||||
int sh;
|
||||
|
||||
sh = 1024 - ((bcr >> 6) & 0x3ff);
|
||||
size = 8 * MiB * sh;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void sdram_bank_map(Ppc4xxSdramBank *bank)
|
||||
{
|
||||
memory_region_init(&bank->container, NULL, "sdram-container", bank->size);
|
||||
memory_region_add_subregion(&bank->container, 0, &bank->ram);
|
||||
memory_region_add_subregion(get_system_memory(), bank->base,
|
||||
&bank->container);
|
||||
}
|
||||
|
||||
static void sdram_bank_unmap(Ppc4xxSdramBank *bank)
|
||||
{
|
||||
memory_region_del_subregion(get_system_memory(), &bank->container);
|
||||
memory_region_del_subregion(&bank->container, &bank->ram);
|
||||
object_unparent(OBJECT(&bank->container));
|
||||
}
|
||||
|
||||
static void sdram_ddr2_set_bcr(Ppc4xxSdramDdr2State *sdram, int i,
|
||||
uint32_t bcr, int enabled)
|
||||
{
|
||||
if (sdram->bank[i].bcr & 1) {
|
||||
/* First unmap RAM if enabled */
|
||||
trace_ppc4xx_sdram_unmap(sdram_ddr2_base(sdram->bank[i].bcr),
|
||||
sdram_ddr2_size(sdram->bank[i].bcr));
|
||||
sdram_bank_unmap(&sdram->bank[i]);
|
||||
}
|
||||
sdram->bank[i].bcr = bcr & 0xffe0ffc1;
|
||||
if (enabled && (bcr & 1)) {
|
||||
trace_ppc4xx_sdram_map(sdram_ddr2_base(bcr), sdram_ddr2_size(bcr));
|
||||
sdram_bank_map(&sdram->bank[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdram_ddr2_map_bcr(Ppc4xxSdramDdr2State *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sdram->nbanks; i++) {
|
||||
if (sdram->bank[i].size) {
|
||||
sdram_ddr2_set_bcr(sdram, i,
|
||||
sdram_ddr2_bcr(sdram->bank[i].base,
|
||||
sdram->bank[i].size), 1);
|
||||
} else {
|
||||
sdram_ddr2_set_bcr(sdram, i, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sdram_ddr2_unmap_bcr(Ppc4xxSdramDdr2State *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sdram->nbanks; i++) {
|
||||
if (sdram->bank[i].size) {
|
||||
sdram_ddr2_set_bcr(sdram, i, sdram->bank[i].bcr & ~1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *sdram = opaque;
|
||||
uint32_t ret = 0;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM_R0BAS:
|
||||
case SDRAM_R1BAS:
|
||||
case SDRAM_R2BAS:
|
||||
case SDRAM_R3BAS:
|
||||
if (sdram->bank[dcrn - SDRAM_R0BAS].size) {
|
||||
ret = sdram_ddr2_bcr(sdram->bank[dcrn - SDRAM_R0BAS].base,
|
||||
sdram->bank[dcrn - SDRAM_R0BAS].size);
|
||||
}
|
||||
break;
|
||||
case SDRAM_CONF1HB:
|
||||
case SDRAM_CONF1LL:
|
||||
case SDRAM_CONFPATHB:
|
||||
case SDRAM_PLBADDULL:
|
||||
case SDRAM_PLBADDUHB:
|
||||
break;
|
||||
case SDRAM0_CFGADDR:
|
||||
ret = sdram->addr;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (sdram->addr) {
|
||||
case 0x14: /* SDRAM_MCSTAT (405EX) */
|
||||
case 0x1F:
|
||||
ret = 0x80000000;
|
||||
break;
|
||||
case 0x21: /* SDRAM_MCOPT2 */
|
||||
ret = sdram->mcopt2;
|
||||
break;
|
||||
case 0x40: /* SDRAM_MB0CF */
|
||||
ret = 0x00008001;
|
||||
break;
|
||||
case 0x7A: /* SDRAM_DLCR */
|
||||
ret = 0x02000000;
|
||||
break;
|
||||
case 0xE1: /* SDR0_DDR0 */
|
||||
ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SDRAM_DDR2_MCOPT2_DCEN BIT(27)
|
||||
|
||||
static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *sdram = opaque;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM_R0BAS:
|
||||
case SDRAM_R1BAS:
|
||||
case SDRAM_R2BAS:
|
||||
case SDRAM_R3BAS:
|
||||
case SDRAM_CONF1HB:
|
||||
case SDRAM_CONF1LL:
|
||||
case SDRAM_CONFPATHB:
|
||||
case SDRAM_PLBADDULL:
|
||||
case SDRAM_PLBADDUHB:
|
||||
break;
|
||||
case SDRAM0_CFGADDR:
|
||||
sdram->addr = val;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (sdram->addr) {
|
||||
case 0x00: /* B0CR */
|
||||
break;
|
||||
case 0x21: /* SDRAM_MCOPT2 */
|
||||
if (!(sdram->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
|
||||
(val & SDRAM_DDR2_MCOPT2_DCEN)) {
|
||||
trace_ppc4xx_sdram_enable("enable");
|
||||
/* validate all RAM mappings */
|
||||
sdram_ddr2_map_bcr(sdram);
|
||||
sdram->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN;
|
||||
} else if ((sdram->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
|
||||
!(val & SDRAM_DDR2_MCOPT2_DCEN)) {
|
||||
trace_ppc4xx_sdram_enable("disable");
|
||||
/* invalidate all RAM mappings */
|
||||
sdram_ddr2_unmap_bcr(sdram);
|
||||
sdram->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr2_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *sdram = PPC4xx_SDRAM_DDR2(dev);
|
||||
|
||||
sdram->addr = 0;
|
||||
sdram->mcopt2 = 0;
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
/*
|
||||
* SoC also has 4 GiB but that causes problem with 32 bit
|
||||
* builds (4*GiB overflows the 32 bit ram_addr_t).
|
||||
*/
|
||||
const ram_addr_t valid_bank_sizes[] = {
|
||||
2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB,
|
||||
64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0
|
||||
};
|
||||
|
||||
if (s->nbanks < 1 || s->nbanks > 4) {
|
||||
error_setg(errp, "Invalid number of RAM banks");
|
||||
return;
|
||||
}
|
||||
if (!s->dram_mr) {
|
||||
error_setg(errp, "Missing dram memory region");
|
||||
return;
|
||||
}
|
||||
ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, valid_bank_sizes);
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R0BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R1BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R2BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R3BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONF1HB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONF1LL,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
}
|
||||
|
||||
static Property ppc4xx_sdram_ddr2_props[] = {
|
||||
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION,
|
||||
MemoryRegion *),
|
||||
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc4xx_sdram_ddr2_realize;
|
||||
dc->reset = ppc4xx_sdram_ddr2_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
device_class_set_props(dc, ppc4xx_sdram_ddr2_props);
|
||||
}
|
||||
|
||||
void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s)
|
||||
{
|
||||
sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21);
|
||||
sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000);
|
||||
}
|
||||
|
||||
static const TypeInfo ppc4xx_types[] = {
|
||||
{
|
||||
.name = TYPE_PPC4xx_SDRAM_DDR2,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxSdramDdr2State),
|
||||
.class_init = ppc4xx_sdram_ddr2_class_init,
|
||||
}
|
||||
};
|
||||
DEFINE_TYPES(ppc4xx_types)
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PLB to AHB bridge */
|
||||
enum {
|
||||
|
|
|
@ -23,419 +23,10 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SDRAM controller */
|
||||
enum {
|
||||
SDRAM0_CFGADDR = 0x010,
|
||||
SDRAM0_CFGDATA = 0x011,
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX: TOFIX: some patches have made this code become inconsistent:
|
||||
* there are type inconsistencies, mixing hwaddr, target_ulong
|
||||
* and uint32_t
|
||||
*/
|
||||
static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||
{
|
||||
uint32_t bcr;
|
||||
|
||||
switch (ram_size) {
|
||||
case 4 * MiB:
|
||||
bcr = 0;
|
||||
break;
|
||||
case 8 * MiB:
|
||||
bcr = 0x20000;
|
||||
break;
|
||||
case 16 * MiB:
|
||||
bcr = 0x40000;
|
||||
break;
|
||||
case 32 * MiB:
|
||||
bcr = 0x60000;
|
||||
break;
|
||||
case 64 * MiB:
|
||||
bcr = 0x80000;
|
||||
break;
|
||||
case 128 * MiB:
|
||||
bcr = 0xA0000;
|
||||
break;
|
||||
case 256 * MiB:
|
||||
bcr = 0xC0000;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__,
|
||||
ram_size);
|
||||
return 0;
|
||||
}
|
||||
bcr |= ram_base & 0xFF800000;
|
||||
bcr |= 1;
|
||||
|
||||
return bcr;
|
||||
}
|
||||
|
||||
static inline hwaddr sdram_ddr_base(uint32_t bcr)
|
||||
{
|
||||
return bcr & 0xFF800000;
|
||||
}
|
||||
|
||||
static target_ulong sdram_ddr_size(uint32_t bcr)
|
||||
{
|
||||
target_ulong size;
|
||||
int sh;
|
||||
|
||||
sh = (bcr >> 17) & 0x7;
|
||||
if (sh == 7) {
|
||||
size = -1;
|
||||
} else {
|
||||
size = (4 * MiB) << sh;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void sdram_ddr_set_bcr(Ppc4xxSdramDdrState *sdram, int i,
|
||||
uint32_t bcr, int enabled)
|
||||
{
|
||||
if (sdram->bank[i].bcr & 1) {
|
||||
/* Unmap RAM */
|
||||
trace_ppc4xx_sdram_unmap(sdram_ddr_base(sdram->bank[i].bcr),
|
||||
sdram_ddr_size(sdram->bank[i].bcr));
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
&sdram->bank[i].container);
|
||||
memory_region_del_subregion(&sdram->bank[i].container,
|
||||
&sdram->bank[i].ram);
|
||||
object_unparent(OBJECT(&sdram->bank[i].container));
|
||||
}
|
||||
sdram->bank[i].bcr = bcr & 0xFFDEE001;
|
||||
if (enabled && (bcr & 1)) {
|
||||
trace_ppc4xx_sdram_map(sdram_ddr_base(bcr), sdram_ddr_size(bcr));
|
||||
memory_region_init(&sdram->bank[i].container, NULL, "sdram-container",
|
||||
sdram_ddr_size(bcr));
|
||||
memory_region_add_subregion(&sdram->bank[i].container, 0,
|
||||
&sdram->bank[i].ram);
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
sdram_ddr_base(bcr),
|
||||
&sdram->bank[i].container);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdram_ddr_map_bcr(Ppc4xxSdramDdrState *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sdram->nbanks; i++) {
|
||||
if (sdram->bank[i].size != 0) {
|
||||
sdram_ddr_set_bcr(sdram, i, sdram_ddr_bcr(sdram->bank[i].base,
|
||||
sdram->bank[i].size), 1);
|
||||
} else {
|
||||
sdram_ddr_set_bcr(sdram, i, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sdram_ddr_unmap_bcr(Ppc4xxSdramDdrState *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sdram->nbanks; i++) {
|
||||
trace_ppc4xx_sdram_unmap(sdram_ddr_base(sdram->bank[i].bcr),
|
||||
sdram_ddr_size(sdram->bank[i].bcr));
|
||||
memory_region_del_subregion(get_system_memory(),
|
||||
&sdram->bank[i].ram);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxSdramDdrState *sdram = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM0_CFGADDR:
|
||||
ret = sdram->addr;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (sdram->addr) {
|
||||
case 0x00: /* SDRAM_BESR0 */
|
||||
ret = sdram->besr0;
|
||||
break;
|
||||
case 0x08: /* SDRAM_BESR1 */
|
||||
ret = sdram->besr1;
|
||||
break;
|
||||
case 0x10: /* SDRAM_BEAR */
|
||||
ret = sdram->bear;
|
||||
break;
|
||||
case 0x20: /* SDRAM_CFG */
|
||||
ret = sdram->cfg;
|
||||
break;
|
||||
case 0x24: /* SDRAM_STATUS */
|
||||
ret = sdram->status;
|
||||
break;
|
||||
case 0x30: /* SDRAM_RTR */
|
||||
ret = sdram->rtr;
|
||||
break;
|
||||
case 0x34: /* SDRAM_PMIT */
|
||||
ret = sdram->pmit;
|
||||
break;
|
||||
case 0x40: /* SDRAM_B0CR */
|
||||
ret = sdram->bank[0].bcr;
|
||||
break;
|
||||
case 0x44: /* SDRAM_B1CR */
|
||||
ret = sdram->bank[1].bcr;
|
||||
break;
|
||||
case 0x48: /* SDRAM_B2CR */
|
||||
ret = sdram->bank[2].bcr;
|
||||
break;
|
||||
case 0x4C: /* SDRAM_B3CR */
|
||||
ret = sdram->bank[3].bcr;
|
||||
break;
|
||||
case 0x80: /* SDRAM_TR */
|
||||
ret = -1; /* ? */
|
||||
break;
|
||||
case 0x94: /* SDRAM_ECCCFG */
|
||||
ret = sdram->ecccfg;
|
||||
break;
|
||||
case 0x98: /* SDRAM_ECCESR */
|
||||
ret = sdram->eccesr;
|
||||
break;
|
||||
default: /* Error */
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Avoid gcc warning */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxSdramDdrState *sdram = opaque;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM0_CFGADDR:
|
||||
sdram->addr = val;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (sdram->addr) {
|
||||
case 0x00: /* SDRAM_BESR0 */
|
||||
sdram->besr0 &= ~val;
|
||||
break;
|
||||
case 0x08: /* SDRAM_BESR1 */
|
||||
sdram->besr1 &= ~val;
|
||||
break;
|
||||
case 0x10: /* SDRAM_BEAR */
|
||||
sdram->bear = val;
|
||||
break;
|
||||
case 0x20: /* SDRAM_CFG */
|
||||
val &= 0xFFE00000;
|
||||
if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
|
||||
trace_ppc4xx_sdram_enable("enable");
|
||||
/* validate all RAM mappings */
|
||||
sdram_ddr_map_bcr(sdram);
|
||||
sdram->status &= ~0x80000000;
|
||||
} else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
|
||||
trace_ppc4xx_sdram_enable("disable");
|
||||
/* invalidate all RAM mappings */
|
||||
sdram_ddr_unmap_bcr(sdram);
|
||||
sdram->status |= 0x80000000;
|
||||
}
|
||||
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) {
|
||||
sdram->status |= 0x40000000;
|
||||
} else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) {
|
||||
sdram->status &= ~0x40000000;
|
||||
}
|
||||
sdram->cfg = val;
|
||||
break;
|
||||
case 0x24: /* SDRAM_STATUS */
|
||||
/* Read-only register */
|
||||
break;
|
||||
case 0x30: /* SDRAM_RTR */
|
||||
sdram->rtr = val & 0x3FF80000;
|
||||
break;
|
||||
case 0x34: /* SDRAM_PMIT */
|
||||
sdram->pmit = (val & 0xF8000000) | 0x07C00000;
|
||||
break;
|
||||
case 0x40: /* SDRAM_B0CR */
|
||||
sdram_ddr_set_bcr(sdram, 0, val, sdram->cfg & 0x80000000);
|
||||
break;
|
||||
case 0x44: /* SDRAM_B1CR */
|
||||
sdram_ddr_set_bcr(sdram, 1, val, sdram->cfg & 0x80000000);
|
||||
break;
|
||||
case 0x48: /* SDRAM_B2CR */
|
||||
sdram_ddr_set_bcr(sdram, 2, val, sdram->cfg & 0x80000000);
|
||||
break;
|
||||
case 0x4C: /* SDRAM_B3CR */
|
||||
sdram_ddr_set_bcr(sdram, 3, val, sdram->cfg & 0x80000000);
|
||||
break;
|
||||
case 0x80: /* SDRAM_TR */
|
||||
sdram->tr = val & 0x018FC01F;
|
||||
break;
|
||||
case 0x94: /* SDRAM_ECCCFG */
|
||||
sdram->ecccfg = val & 0x00F00000;
|
||||
break;
|
||||
case 0x98: /* SDRAM_ECCESR */
|
||||
val &= 0xFFF0F000;
|
||||
if (sdram->eccesr == 0 && val != 0) {
|
||||
qemu_irq_raise(sdram->irq);
|
||||
} else if (sdram->eccesr != 0 && val == 0) {
|
||||
qemu_irq_lower(sdram->irq);
|
||||
}
|
||||
sdram->eccesr = val;
|
||||
break;
|
||||
default: /* Error */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxSdramDdrState *sdram = PPC4xx_SDRAM_DDR(dev);
|
||||
|
||||
sdram->addr = 0;
|
||||
sdram->bear = 0;
|
||||
sdram->besr0 = 0; /* No error */
|
||||
sdram->besr1 = 0; /* No error */
|
||||
sdram->cfg = 0;
|
||||
sdram->ecccfg = 0; /* No ECC */
|
||||
sdram->eccesr = 0; /* No error */
|
||||
sdram->pmit = 0x07C00000;
|
||||
sdram->rtr = 0x05F00000;
|
||||
sdram->tr = 0x00854009;
|
||||
/* We pre-initialize RAM banks */
|
||||
sdram->status = 0;
|
||||
sdram->cfg = 0x00800000;
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
const ram_addr_t valid_bank_sizes[] = {
|
||||
256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0
|
||||
};
|
||||
|
||||
if (s->nbanks < 1 || s->nbanks > 4) {
|
||||
error_setg(errp, "Invalid number of RAM banks");
|
||||
return;
|
||||
}
|
||||
if (!s->dram_mr) {
|
||||
error_setg(errp, "Missing dram memory region");
|
||||
return;
|
||||
}
|
||||
ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, valid_bank_sizes);
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
|
||||
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
|
||||
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
|
||||
}
|
||||
|
||||
static Property ppc4xx_sdram_ddr_props[] = {
|
||||
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION,
|
||||
MemoryRegion *),
|
||||
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc4xx_sdram_ddr_realize;
|
||||
dc->reset = ppc4xx_sdram_ddr_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
device_class_set_props(dc, ppc4xx_sdram_ddr_props);
|
||||
}
|
||||
|
||||
void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s)
|
||||
{
|
||||
sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20);
|
||||
sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Split RAM between SDRAM banks.
|
||||
*
|
||||
* sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1]
|
||||
* and must be 0-terminated.
|
||||
*
|
||||
* The 4xx SDRAM controller supports a small number of banks, and each bank
|
||||
* must be one of a small set of sizes. The number of banks and the supported
|
||||
* sizes varies by SoC.
|
||||
*/
|
||||
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
||||
Ppc4xxSdramBank ram_banks[],
|
||||
const ram_addr_t sdram_bank_sizes[])
|
||||
{
|
||||
ram_addr_t size_left = memory_region_size(ram);
|
||||
ram_addr_t base = 0;
|
||||
ram_addr_t bank_size;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < nr_banks; i++) {
|
||||
for (j = 0; sdram_bank_sizes[j] != 0; j++) {
|
||||
bank_size = sdram_bank_sizes[j];
|
||||
if (bank_size <= size_left) {
|
||||
char name[32];
|
||||
|
||||
ram_banks[i].base = base;
|
||||
ram_banks[i].size = bank_size;
|
||||
base += bank_size;
|
||||
size_left -= bank_size;
|
||||
snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
|
||||
memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram,
|
||||
ram_banks[i].base, ram_banks[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!size_left) {
|
||||
/* No need to use the remaining banks. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_left) {
|
||||
ram_addr_t used_size = memory_region_size(ram) - size_left;
|
||||
GString *s = g_string_new(NULL);
|
||||
|
||||
for (i = 0; sdram_bank_sizes[i]; i++) {
|
||||
g_string_append_printf(s, "%" PRIi64 "%s",
|
||||
sdram_bank_sizes[i] / MiB,
|
||||
sdram_bank_sizes[i + 1] ? ", " : "");
|
||||
}
|
||||
error_report("at most %d bank%s of %s MiB each supported",
|
||||
nr_banks, nr_banks == 1 ? "" : "s", s->str);
|
||||
error_printf("Possible valid RAM size: %" PRIi64 " MiB\n",
|
||||
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
|
||||
|
||||
g_string_free(s, true);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* MAL */
|
||||
|
@ -963,11 +554,6 @@ static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data)
|
|||
|
||||
static const TypeInfo ppc4xx_types[] = {
|
||||
{
|
||||
.name = TYPE_PPC4xx_SDRAM_DDR,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxSdramDdrState),
|
||||
.class_init = ppc4xx_sdram_ddr_class_init,
|
||||
}, {
|
||||
.name = TYPE_PPC4xx_MAL,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxMalState),
|
||||
|
|
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
* QEMU PowerPC 4xx embedded processors SDRAM controller emulation
|
||||
*
|
||||
* DDR SDRAM controller:
|
||||
* Copyright (c) 2007 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* DDR2 SDRAM controller:
|
||||
* Copyright (c) 2012 François Revol
|
||||
* Copyright (c) 2016-2019 BALATON Zoltan
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h" /* get_system_memory() */
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Shared functions */
|
||||
|
||||
/*
|
||||
* Split RAM between SDRAM banks.
|
||||
*
|
||||
* sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1]
|
||||
* and must be 0-terminated.
|
||||
*
|
||||
* The 4xx SDRAM controller supports a small number of banks, and each bank
|
||||
* must be one of a small set of sizes. The number of banks and the supported
|
||||
* sizes varies by SoC.
|
||||
*/
|
||||
static bool ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
||||
Ppc4xxSdramBank ram_banks[],
|
||||
const ram_addr_t sdram_bank_sizes[],
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
ram_addr_t size_left = memory_region_size(ram);
|
||||
ram_addr_t base = 0;
|
||||
ram_addr_t bank_size;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < nr_banks; i++) {
|
||||
for (j = 0; sdram_bank_sizes[j] != 0; j++) {
|
||||
bank_size = sdram_bank_sizes[j];
|
||||
if (bank_size <= size_left) {
|
||||
char name[32];
|
||||
|
||||
ram_banks[i].base = base;
|
||||
ram_banks[i].size = bank_size;
|
||||
base += bank_size;
|
||||
size_left -= bank_size;
|
||||
snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
|
||||
memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram,
|
||||
ram_banks[i].base, ram_banks[i].size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!size_left) {
|
||||
/* No need to use the remaining banks. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (size_left) {
|
||||
ram_addr_t used_size = memory_region_size(ram) - size_left;
|
||||
GString *s = g_string_new(NULL);
|
||||
|
||||
for (i = 0; sdram_bank_sizes[i]; i++) {
|
||||
g_string_append_printf(s, "%" PRIi64 "%s",
|
||||
sdram_bank_sizes[i] / MiB,
|
||||
sdram_bank_sizes[i + 1] ? ", " : "");
|
||||
}
|
||||
error_setg(errp, "Invalid SDRAM banks");
|
||||
error_append_hint(errp, "at most %d bank%s of %s MiB each supported\n",
|
||||
nr_banks, nr_banks == 1 ? "" : "s", s->str);
|
||||
error_append_hint(errp, "Possible valid RAM size: %" PRIi64 " MiB\n",
|
||||
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
|
||||
|
||||
g_string_free(s, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sdram_bank_map(Ppc4xxSdramBank *bank)
|
||||
{
|
||||
trace_ppc4xx_sdram_map(bank->base, bank->size);
|
||||
memory_region_init(&bank->container, NULL, "sdram-container", bank->size);
|
||||
memory_region_add_subregion(&bank->container, 0, &bank->ram);
|
||||
memory_region_add_subregion(get_system_memory(), bank->base,
|
||||
&bank->container);
|
||||
}
|
||||
|
||||
static void sdram_bank_unmap(Ppc4xxSdramBank *bank)
|
||||
{
|
||||
trace_ppc4xx_sdram_unmap(bank->base, bank->size);
|
||||
memory_region_del_subregion(get_system_memory(), &bank->container);
|
||||
memory_region_del_subregion(&bank->container, &bank->ram);
|
||||
object_unparent(OBJECT(&bank->container));
|
||||
}
|
||||
|
||||
static void sdram_bank_set_bcr(Ppc4xxSdramBank *bank, uint32_t bcr,
|
||||
hwaddr base, hwaddr size, int enabled)
|
||||
{
|
||||
if (memory_region_is_mapped(&bank->container)) {
|
||||
sdram_bank_unmap(bank);
|
||||
}
|
||||
bank->bcr = bcr;
|
||||
bank->base = base;
|
||||
bank->size = size;
|
||||
if (enabled && (bcr & 1)) {
|
||||
sdram_bank_map(bank);
|
||||
}
|
||||
}
|
||||
|
||||
enum {
|
||||
SDRAM0_CFGADDR = 0x010,
|
||||
SDRAM0_CFGDATA = 0x011,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* DDR SDRAM controller */
|
||||
#define SDRAM_DDR_BCR_MASK 0xFFDEE001
|
||||
|
||||
static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||
{
|
||||
uint32_t bcr;
|
||||
|
||||
switch (ram_size) {
|
||||
case 4 * MiB:
|
||||
bcr = 0;
|
||||
break;
|
||||
case 8 * MiB:
|
||||
bcr = 0x20000;
|
||||
break;
|
||||
case 16 * MiB:
|
||||
bcr = 0x40000;
|
||||
break;
|
||||
case 32 * MiB:
|
||||
bcr = 0x60000;
|
||||
break;
|
||||
case 64 * MiB:
|
||||
bcr = 0x80000;
|
||||
break;
|
||||
case 128 * MiB:
|
||||
bcr = 0xA0000;
|
||||
break;
|
||||
case 256 * MiB:
|
||||
bcr = 0xC0000;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__,
|
||||
ram_size);
|
||||
return 0;
|
||||
}
|
||||
bcr |= ram_base & 0xFF800000;
|
||||
bcr |= 1;
|
||||
|
||||
return bcr;
|
||||
}
|
||||
|
||||
static inline hwaddr sdram_ddr_base(uint32_t bcr)
|
||||
{
|
||||
return bcr & 0xFF800000;
|
||||
}
|
||||
|
||||
static hwaddr sdram_ddr_size(uint32_t bcr)
|
||||
{
|
||||
hwaddr size;
|
||||
int sh;
|
||||
|
||||
sh = (bcr >> 17) & 0x7;
|
||||
if (sh == 7) {
|
||||
size = -1;
|
||||
} else {
|
||||
size = (4 * MiB) << sh;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxSdramDdrState *s = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM0_CFGADDR:
|
||||
ret = s->addr;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (s->addr) {
|
||||
case 0x00: /* SDRAM_BESR0 */
|
||||
ret = s->besr0;
|
||||
break;
|
||||
case 0x08: /* SDRAM_BESR1 */
|
||||
ret = s->besr1;
|
||||
break;
|
||||
case 0x10: /* SDRAM_BEAR */
|
||||
ret = s->bear;
|
||||
break;
|
||||
case 0x20: /* SDRAM_CFG */
|
||||
ret = s->cfg;
|
||||
break;
|
||||
case 0x24: /* SDRAM_STATUS */
|
||||
ret = s->status;
|
||||
break;
|
||||
case 0x30: /* SDRAM_RTR */
|
||||
ret = s->rtr;
|
||||
break;
|
||||
case 0x34: /* SDRAM_PMIT */
|
||||
ret = s->pmit;
|
||||
break;
|
||||
case 0x40: /* SDRAM_B0CR */
|
||||
ret = s->bank[0].bcr;
|
||||
break;
|
||||
case 0x44: /* SDRAM_B1CR */
|
||||
ret = s->bank[1].bcr;
|
||||
break;
|
||||
case 0x48: /* SDRAM_B2CR */
|
||||
ret = s->bank[2].bcr;
|
||||
break;
|
||||
case 0x4C: /* SDRAM_B3CR */
|
||||
ret = s->bank[3].bcr;
|
||||
break;
|
||||
case 0x80: /* SDRAM_TR */
|
||||
ret = -1; /* ? */
|
||||
break;
|
||||
case 0x94: /* SDRAM_ECCCFG */
|
||||
ret = s->ecccfg;
|
||||
break;
|
||||
case 0x98: /* SDRAM_ECCESR */
|
||||
ret = s->eccesr;
|
||||
break;
|
||||
default: /* Error */
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Avoid gcc warning */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxSdramDdrState *s = opaque;
|
||||
int i;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM0_CFGADDR:
|
||||
s->addr = val;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (s->addr) {
|
||||
case 0x00: /* SDRAM_BESR0 */
|
||||
s->besr0 &= ~val;
|
||||
break;
|
||||
case 0x08: /* SDRAM_BESR1 */
|
||||
s->besr1 &= ~val;
|
||||
break;
|
||||
case 0x10: /* SDRAM_BEAR */
|
||||
s->bear = val;
|
||||
break;
|
||||
case 0x20: /* SDRAM_CFG */
|
||||
val &= 0xFFE00000;
|
||||
if (!(s->cfg & 0x80000000) && (val & 0x80000000)) {
|
||||
trace_ppc4xx_sdram_enable("enable");
|
||||
/* validate all RAM mappings */
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size,
|
||||
1);
|
||||
}
|
||||
}
|
||||
s->status &= ~0x80000000;
|
||||
} else if ((s->cfg & 0x80000000) && !(val & 0x80000000)) {
|
||||
trace_ppc4xx_sdram_enable("disable");
|
||||
/* invalidate all RAM mappings */
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size,
|
||||
0);
|
||||
}
|
||||
}
|
||||
s->status |= 0x80000000;
|
||||
}
|
||||
if (!(s->cfg & 0x40000000) && (val & 0x40000000)) {
|
||||
s->status |= 0x40000000;
|
||||
} else if ((s->cfg & 0x40000000) && !(val & 0x40000000)) {
|
||||
s->status &= ~0x40000000;
|
||||
}
|
||||
s->cfg = val;
|
||||
break;
|
||||
case 0x24: /* SDRAM_STATUS */
|
||||
/* Read-only register */
|
||||
break;
|
||||
case 0x30: /* SDRAM_RTR */
|
||||
s->rtr = val & 0x3FF80000;
|
||||
break;
|
||||
case 0x34: /* SDRAM_PMIT */
|
||||
s->pmit = (val & 0xF8000000) | 0x07C00000;
|
||||
break;
|
||||
case 0x40: /* SDRAM_B0CR */
|
||||
case 0x44: /* SDRAM_B1CR */
|
||||
case 0x48: /* SDRAM_B2CR */
|
||||
case 0x4C: /* SDRAM_B3CR */
|
||||
i = (s->addr - 0x40) / 4;
|
||||
val &= SDRAM_DDR_BCR_MASK;
|
||||
if (s->bank[i].size) {
|
||||
sdram_bank_set_bcr(&s->bank[i], val,
|
||||
sdram_ddr_base(val), sdram_ddr_size(val),
|
||||
s->cfg & 0x80000000);
|
||||
}
|
||||
break;
|
||||
case 0x80: /* SDRAM_TR */
|
||||
s->tr = val & 0x018FC01F;
|
||||
break;
|
||||
case 0x94: /* SDRAM_ECCCFG */
|
||||
s->ecccfg = val & 0x00F00000;
|
||||
break;
|
||||
case 0x98: /* SDRAM_ECCESR */
|
||||
val &= 0xFFF0F000;
|
||||
if (s->eccesr == 0 && val != 0) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else if (s->eccesr != 0 && val == 0) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
s->eccesr = val;
|
||||
break;
|
||||
default: /* Error */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
|
||||
|
||||
s->addr = 0;
|
||||
s->bear = 0;
|
||||
s->besr0 = 0; /* No error */
|
||||
s->besr1 = 0; /* No error */
|
||||
s->cfg = 0;
|
||||
s->ecccfg = 0; /* No ECC */
|
||||
s->eccesr = 0; /* No error */
|
||||
s->pmit = 0x07C00000;
|
||||
s->rtr = 0x05F00000;
|
||||
s->tr = 0x00854009;
|
||||
/* We pre-initialize RAM banks */
|
||||
s->status = 0;
|
||||
s->cfg = 0x00800000;
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
const ram_addr_t valid_bank_sizes[] = {
|
||||
256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0
|
||||
};
|
||||
int i;
|
||||
|
||||
if (s->nbanks < 1 || s->nbanks > 4) {
|
||||
error_setg(errp, "Invalid number of RAM banks");
|
||||
return;
|
||||
}
|
||||
if (!s->dram_mr) {
|
||||
error_setg(errp, "Missing dram memory region");
|
||||
return;
|
||||
}
|
||||
if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank,
|
||||
valid_bank_sizes, errp)) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
s->bank[i].bcr = sdram_ddr_bcr(s->bank[i].base, s->bank[i].size);
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size, 0);
|
||||
} else {
|
||||
sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0);
|
||||
}
|
||||
trace_ppc4xx_sdram_init(sdram_ddr_base(s->bank[i].bcr),
|
||||
sdram_ddr_size(s->bank[i].bcr),
|
||||
s->bank[i].bcr);
|
||||
}
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
|
||||
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
|
||||
s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write);
|
||||
}
|
||||
|
||||
static Property ppc4xx_sdram_ddr_props[] = {
|
||||
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION,
|
||||
MemoryRegion *),
|
||||
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc4xx_sdram_ddr_realize;
|
||||
dc->reset = ppc4xx_sdram_ddr_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
device_class_set_props(dc, ppc4xx_sdram_ddr_props);
|
||||
}
|
||||
|
||||
void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s)
|
||||
{
|
||||
sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20);
|
||||
sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* DDR2 SDRAM controller */
|
||||
#define SDRAM_DDR2_BCR_MASK 0xffe0ffc1
|
||||
|
||||
enum {
|
||||
SDRAM_R0BAS = 0x40,
|
||||
SDRAM_R1BAS,
|
||||
SDRAM_R2BAS,
|
||||
SDRAM_R3BAS,
|
||||
SDRAM_CONF1HB = 0x45,
|
||||
SDRAM_PLBADDULL = 0x4a,
|
||||
SDRAM_CONF1LL = 0x4b,
|
||||
SDRAM_CONFPATHB = 0x4f,
|
||||
SDRAM_PLBADDUHB = 0x50,
|
||||
};
|
||||
|
||||
static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||
{
|
||||
uint32_t bcr;
|
||||
|
||||
switch (ram_size) {
|
||||
case 8 * MiB:
|
||||
bcr = 0xffc0;
|
||||
break;
|
||||
case 16 * MiB:
|
||||
bcr = 0xff80;
|
||||
break;
|
||||
case 32 * MiB:
|
||||
bcr = 0xff00;
|
||||
break;
|
||||
case 64 * MiB:
|
||||
bcr = 0xfe00;
|
||||
break;
|
||||
case 128 * MiB:
|
||||
bcr = 0xfc00;
|
||||
break;
|
||||
case 256 * MiB:
|
||||
bcr = 0xf800;
|
||||
break;
|
||||
case 512 * MiB:
|
||||
bcr = 0xf000;
|
||||
break;
|
||||
case 1 * GiB:
|
||||
bcr = 0xe000;
|
||||
break;
|
||||
case 2 * GiB:
|
||||
bcr = 0xc000;
|
||||
break;
|
||||
case 4 * GiB:
|
||||
bcr = 0x8000;
|
||||
break;
|
||||
default:
|
||||
error_report("invalid RAM size " TARGET_FMT_plx, ram_size);
|
||||
return 0;
|
||||
}
|
||||
bcr |= ram_base >> 2 & 0xffe00000;
|
||||
bcr |= 1;
|
||||
|
||||
return bcr;
|
||||
}
|
||||
|
||||
static inline hwaddr sdram_ddr2_base(uint32_t bcr)
|
||||
{
|
||||
return (bcr & 0xffe00000) << 2;
|
||||
}
|
||||
|
||||
static hwaddr sdram_ddr2_size(uint32_t bcr)
|
||||
{
|
||||
hwaddr size;
|
||||
int sh;
|
||||
|
||||
sh = 1024 - ((bcr >> 6) & 0x3ff);
|
||||
size = 8 * MiB * sh;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *s = opaque;
|
||||
uint32_t ret = 0;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM_R0BAS:
|
||||
case SDRAM_R1BAS:
|
||||
case SDRAM_R2BAS:
|
||||
case SDRAM_R3BAS:
|
||||
if (s->bank[dcrn - SDRAM_R0BAS].size) {
|
||||
ret = sdram_ddr2_bcr(s->bank[dcrn - SDRAM_R0BAS].base,
|
||||
s->bank[dcrn - SDRAM_R0BAS].size);
|
||||
}
|
||||
break;
|
||||
case SDRAM_CONF1HB:
|
||||
case SDRAM_CONF1LL:
|
||||
case SDRAM_CONFPATHB:
|
||||
case SDRAM_PLBADDULL:
|
||||
case SDRAM_PLBADDUHB:
|
||||
break;
|
||||
case SDRAM0_CFGADDR:
|
||||
ret = s->addr;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (s->addr) {
|
||||
case 0x14: /* SDRAM_MCSTAT (405EX) */
|
||||
case 0x1F:
|
||||
ret = 0x80000000;
|
||||
break;
|
||||
case 0x21: /* SDRAM_MCOPT2 */
|
||||
ret = s->mcopt2;
|
||||
break;
|
||||
case 0x40: /* SDRAM_MB0CF */
|
||||
ret = 0x00008001;
|
||||
break;
|
||||
case 0x7A: /* SDRAM_DLCR */
|
||||
ret = 0x02000000;
|
||||
break;
|
||||
case 0xE1: /* SDR0_DDR0 */
|
||||
ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SDRAM_DDR2_MCOPT2_DCEN BIT(27)
|
||||
|
||||
static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *s = opaque;
|
||||
int i;
|
||||
|
||||
switch (dcrn) {
|
||||
case SDRAM_R0BAS:
|
||||
case SDRAM_R1BAS:
|
||||
case SDRAM_R2BAS:
|
||||
case SDRAM_R3BAS:
|
||||
case SDRAM_CONF1HB:
|
||||
case SDRAM_CONF1LL:
|
||||
case SDRAM_CONFPATHB:
|
||||
case SDRAM_PLBADDULL:
|
||||
case SDRAM_PLBADDUHB:
|
||||
break;
|
||||
case SDRAM0_CFGADDR:
|
||||
s->addr = val;
|
||||
break;
|
||||
case SDRAM0_CFGDATA:
|
||||
switch (s->addr) {
|
||||
case 0x00: /* B0CR */
|
||||
break;
|
||||
case 0x21: /* SDRAM_MCOPT2 */
|
||||
if (!(s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
|
||||
(val & SDRAM_DDR2_MCOPT2_DCEN)) {
|
||||
trace_ppc4xx_sdram_enable("enable");
|
||||
/* validate all RAM mappings */
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size,
|
||||
1);
|
||||
}
|
||||
}
|
||||
s->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN;
|
||||
} else if ((s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) &&
|
||||
!(val & SDRAM_DDR2_MCOPT2_DCEN)) {
|
||||
trace_ppc4xx_sdram_enable("disable");
|
||||
/* invalidate all RAM mappings */
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size,
|
||||
0);
|
||||
}
|
||||
}
|
||||
s->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr2_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
|
||||
|
||||
s->addr = 0;
|
||||
s->mcopt2 = 0;
|
||||
}
|
||||
|
||||
static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
/*
|
||||
* SoC also has 4 GiB but that causes problem with 32 bit
|
||||
* builds (4*GiB overflows the 32 bit ram_addr_t).
|
||||
*/
|
||||
const ram_addr_t valid_bank_sizes[] = {
|
||||
2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB,
|
||||
64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0
|
||||
};
|
||||
int i;
|
||||
|
||||
if (s->nbanks < 1 || s->nbanks > 4) {
|
||||
error_setg(errp, "Invalid number of RAM banks");
|
||||
return;
|
||||
}
|
||||
if (!s->dram_mr) {
|
||||
error_setg(errp, "Missing dram memory region");
|
||||
return;
|
||||
}
|
||||
if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank,
|
||||
valid_bank_sizes, errp)) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < s->nbanks; i++) {
|
||||
if (s->bank[i].size) {
|
||||
s->bank[i].bcr = sdram_ddr2_bcr(s->bank[i].base, s->bank[i].size);
|
||||
s->bank[i].bcr &= SDRAM_DDR2_BCR_MASK;
|
||||
sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr,
|
||||
s->bank[i].base, s->bank[i].size, 0);
|
||||
} else {
|
||||
sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0);
|
||||
}
|
||||
trace_ppc4xx_sdram_init(sdram_ddr2_base(s->bank[i].bcr),
|
||||
sdram_ddr2_size(s->bank[i].bcr),
|
||||
s->bank[i].bcr);
|
||||
}
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R0BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R1BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R2BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_R3BAS,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONF1HB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONF1LL,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB,
|
||||
s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write);
|
||||
}
|
||||
|
||||
static Property ppc4xx_sdram_ddr2_props[] = {
|
||||
DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION,
|
||||
MemoryRegion *),
|
||||
DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc4xx_sdram_ddr2_realize;
|
||||
dc->reset = ppc4xx_sdram_ddr2_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
device_class_set_props(dc, ppc4xx_sdram_ddr2_props);
|
||||
}
|
||||
|
||||
void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s)
|
||||
{
|
||||
sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21);
|
||||
sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000);
|
||||
}
|
||||
|
||||
static const TypeInfo ppc4xx_sdram_types[] = {
|
||||
{
|
||||
.name = TYPE_PPC4xx_SDRAM_DDR,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxSdramDdrState),
|
||||
.class_init = ppc4xx_sdram_ddr_class_init,
|
||||
}, {
|
||||
.name = TYPE_PPC4xx_SDRAM_DDR2,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxSdramDdr2State),
|
||||
.class_init = ppc4xx_sdram_ddr2_class_init,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(ppc4xx_sdram_types)
|
|
@ -490,6 +490,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
|
||||
env->msr |= (1ULL << MSR_EE);
|
||||
hreg_compute_hflags(env);
|
||||
ppc_maybe_interrupt(env);
|
||||
|
||||
if (spapr_cpu->prod) {
|
||||
spapr_cpu->prod = false;
|
||||
|
@ -500,6 +501,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
cs->halted = 1;
|
||||
cs->exception_index = EXCP_HLT;
|
||||
cs->exit_request = 1;
|
||||
ppc_maybe_interrupt(env);
|
||||
}
|
||||
|
||||
return H_SUCCESS;
|
||||
|
@ -521,6 +523,7 @@ static target_ulong h_confer_self(PowerPCCPU *cpu)
|
|||
cs->halted = 1;
|
||||
cs->exception_index = EXCP_HALTED;
|
||||
cs->exit_request = 1;
|
||||
ppc_maybe_interrupt(&cpu->env);
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
@ -633,6 +636,7 @@ static target_ulong h_prod(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
spapr_cpu = spapr_cpu_state(tcpu);
|
||||
spapr_cpu->prod = true;
|
||||
cs->halted = 0;
|
||||
ppc_maybe_interrupt(&cpu->env);
|
||||
qemu_cpu_kick(cs);
|
||||
|
||||
return H_SUCCESS;
|
||||
|
@ -1669,6 +1673,7 @@ static target_ulong h_enter_nested(PowerPCCPU *cpu,
|
|||
spapr_cpu->in_nested = true;
|
||||
|
||||
hreg_compute_hflags(env);
|
||||
ppc_maybe_interrupt(env);
|
||||
tlb_flush(cs);
|
||||
env->reserve_addr = -1; /* Reset the reservation */
|
||||
|
||||
|
@ -1810,6 +1815,7 @@ out_restore_l1:
|
|||
spapr_cpu->in_nested = false;
|
||||
|
||||
hreg_compute_hflags(env);
|
||||
ppc_maybe_interrupt(env);
|
||||
tlb_flush(cs);
|
||||
env->reserve_addr = -1; /* Reset the reservation */
|
||||
|
||||
|
|
|
@ -214,9 +214,9 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
* guest.
|
||||
* For the same reason, set PSSCR_EC.
|
||||
*/
|
||||
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
|
||||
env->spr[SPR_PSSCR] |= PSSCR_EC;
|
||||
cs->halted = 1;
|
||||
ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm);
|
||||
kvmppc_set_reg_ppc_online(cpu, 0);
|
||||
qemu_cpu_kick(cs);
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ ppc40x_set_tb_clk(uint32_t value) "new frequency %" PRIu32
|
|||
ppc40x_timers_init(uint32_t value) "frequency %" PRIu32
|
||||
|
||||
ppc_irq_set(void *env, uint32_t pin, uint32_t level) "env [%p] pin %d level %d"
|
||||
ppc_irq_set_exit(void *env, uint32_t n_IRQ, uint32_t level, uint32_t pending, uint32_t request) "env [%p] n_IRQ %d level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32
|
||||
ppc_irq_set_exit(void *env, uint32_t irq, uint32_t level, uint32_t pending, uint32_t request) "env [%p] irq 0x%05" PRIx32 " level %d => pending 0x%08" PRIx32 " req 0x%08" PRIx32
|
||||
ppc_irq_set_state(const char *name, uint32_t level) "\"%s\" level %d"
|
||||
ppc_irq_reset(const char *name) "%s"
|
||||
ppc_irq_cpu(const char *action) "%s"
|
||||
|
@ -179,3 +179,4 @@ ppc405ep_clocks_setup(const char *trace) "%s"
|
|||
ppc4xx_sdram_enable(const char *trace) "%s SDRAM controller"
|
||||
ppc4xx_sdram_unmap(uint64_t addr, uint64_t size) "Unmap RAM area 0x%" PRIx64 " size 0x%" PRIx64
|
||||
ppc4xx_sdram_map(uint64_t addr, uint64_t size) "Map RAM area 0x%" PRIx64 " size 0x%" PRIx64
|
||||
ppc4xx_sdram_init(uint64_t base, uint64_t size, uint32_t bcr) "Init RAM area 0x%" PRIx64 " size 0x%" PRIx64 " bcr 0x%x"
|
||||
|
|
|
@ -288,26 +288,6 @@ enum {
|
|||
|
||||
extern const VMStateDescription sdhci_vmstate;
|
||||
|
||||
|
||||
#define ESDHC_MIX_CTRL 0x48
|
||||
|
||||
#define ESDHC_VENDOR_SPEC 0xc0
|
||||
#define ESDHC_IMX_FRC_SDCLK_ON (1 << 8)
|
||||
|
||||
#define ESDHC_DLL_CTRL 0x60
|
||||
|
||||
#define ESDHC_TUNING_CTRL 0xcc
|
||||
#define ESDHC_TUNE_CTRL_STATUS 0x68
|
||||
#define ESDHC_WTMK_LVL 0x44
|
||||
|
||||
/* Undocumented register used by guests working around erratum ERR004536 */
|
||||
#define ESDHC_UNDOCUMENTED_REG27 0x6c
|
||||
|
||||
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
|
||||
#define ESDHC_PRNSTS_SDSTB (1 << 3)
|
||||
|
||||
/*
|
||||
* Default SD/MMC host controller features information, which will be
|
||||
* presented in CAPABILITIES register of generic SD host controller at reset.
|
||||
|
|
|
@ -1577,6 +1577,25 @@ static const TypeInfo sdhci_bus_info = {
|
|||
|
||||
/* --- qdev i.MX eSDHC --- */
|
||||
|
||||
#define USDHC_MIX_CTRL 0x48
|
||||
|
||||
#define USDHC_VENDOR_SPEC 0xc0
|
||||
#define USDHC_IMX_FRC_SDCLK_ON (1 << 8)
|
||||
|
||||
#define USDHC_DLL_CTRL 0x60
|
||||
|
||||
#define USDHC_TUNING_CTRL 0xcc
|
||||
#define USDHC_TUNE_CTRL_STATUS 0x68
|
||||
#define USDHC_WTMK_LVL 0x44
|
||||
|
||||
/* Undocumented register used by guests working around erratum ERR004536 */
|
||||
#define USDHC_UNDOCUMENTED_REG27 0x6c
|
||||
|
||||
#define USDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
#define USDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
|
||||
#define USDHC_PRNSTS_SDSTB (1 << 3)
|
||||
|
||||
static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
SDHCIState *s = SYSBUS_SDHCI(opaque);
|
||||
|
@ -1596,11 +1615,11 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
|
|||
hostctl1 = SDHC_DMA_TYPE(s->hostctl1) << (8 - 3);
|
||||
|
||||
if (s->hostctl1 & SDHC_CTRL_8BITBUS) {
|
||||
hostctl1 |= ESDHC_CTRL_8BITBUS;
|
||||
hostctl1 |= USDHC_CTRL_8BITBUS;
|
||||
}
|
||||
|
||||
if (s->hostctl1 & SDHC_CTRL_4BITBUS) {
|
||||
hostctl1 |= ESDHC_CTRL_4BITBUS;
|
||||
hostctl1 |= USDHC_CTRL_4BITBUS;
|
||||
}
|
||||
|
||||
ret = hostctl1;
|
||||
|
@ -1611,21 +1630,21 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size)
|
|||
|
||||
case SDHC_PRNSTS:
|
||||
/* Add SDSTB (SD Clock Stable) bit to PRNSTS */
|
||||
ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB;
|
||||
ret = sdhci_read(opaque, offset, size) & ~USDHC_PRNSTS_SDSTB;
|
||||
if (s->clkcon & SDHC_CLOCK_INT_STABLE) {
|
||||
ret |= ESDHC_PRNSTS_SDSTB;
|
||||
ret |= USDHC_PRNSTS_SDSTB;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESDHC_VENDOR_SPEC:
|
||||
case USDHC_VENDOR_SPEC:
|
||||
ret = s->vendor_spec;
|
||||
break;
|
||||
case ESDHC_DLL_CTRL:
|
||||
case ESDHC_TUNE_CTRL_STATUS:
|
||||
case ESDHC_UNDOCUMENTED_REG27:
|
||||
case ESDHC_TUNING_CTRL:
|
||||
case ESDHC_MIX_CTRL:
|
||||
case ESDHC_WTMK_LVL:
|
||||
case USDHC_DLL_CTRL:
|
||||
case USDHC_TUNE_CTRL_STATUS:
|
||||
case USDHC_UNDOCUMENTED_REG27:
|
||||
case USDHC_TUNING_CTRL:
|
||||
case USDHC_MIX_CTRL:
|
||||
case USDHC_WTMK_LVL:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -1641,18 +1660,18 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
|||
uint32_t value = (uint32_t)val;
|
||||
|
||||
switch (offset) {
|
||||
case ESDHC_DLL_CTRL:
|
||||
case ESDHC_TUNE_CTRL_STATUS:
|
||||
case ESDHC_UNDOCUMENTED_REG27:
|
||||
case ESDHC_TUNING_CTRL:
|
||||
case ESDHC_WTMK_LVL:
|
||||
case USDHC_DLL_CTRL:
|
||||
case USDHC_TUNE_CTRL_STATUS:
|
||||
case USDHC_UNDOCUMENTED_REG27:
|
||||
case USDHC_TUNING_CTRL:
|
||||
case USDHC_WTMK_LVL:
|
||||
break;
|
||||
|
||||
case ESDHC_VENDOR_SPEC:
|
||||
case USDHC_VENDOR_SPEC:
|
||||
s->vendor_spec = value;
|
||||
switch (s->vendor) {
|
||||
case SDHCI_VENDOR_IMX:
|
||||
if (value & ESDHC_IMX_FRC_SDCLK_ON) {
|
||||
if (value & USDHC_IMX_FRC_SDCLK_ON) {
|
||||
s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF;
|
||||
} else {
|
||||
s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF;
|
||||
|
@ -1721,12 +1740,12 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
|||
* Second, split "Data Transfer Width" from bits 2 and 1 in to
|
||||
* bits 5 and 1
|
||||
*/
|
||||
if (value & ESDHC_CTRL_8BITBUS) {
|
||||
if (value & USDHC_CTRL_8BITBUS) {
|
||||
hostctl1 |= SDHC_CTRL_8BITBUS;
|
||||
}
|
||||
|
||||
if (value & ESDHC_CTRL_4BITBUS) {
|
||||
hostctl1 |= ESDHC_CTRL_4BITBUS;
|
||||
if (value & USDHC_CTRL_4BITBUS) {
|
||||
hostctl1 |= USDHC_CTRL_4BITBUS;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1749,7 +1768,7 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
|||
sdhci_write(opaque, offset, value, size);
|
||||
break;
|
||||
|
||||
case ESDHC_MIX_CTRL:
|
||||
case USDHC_MIX_CTRL:
|
||||
/*
|
||||
* So, when SD/MMC stack in Linux tries to write to "Transfer
|
||||
* Mode Register", ESDHC i.MX quirk code will translate it
|
||||
|
|
|
@ -29,18 +29,6 @@
|
|||
#include "exec/memory.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
typedef struct {
|
||||
MemoryRegion ram;
|
||||
MemoryRegion container; /* used for clipping */
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
uint32_t bcr;
|
||||
} Ppc4xxSdramBank;
|
||||
|
||||
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
||||
Ppc4xxSdramBank ram_banks[],
|
||||
const ram_addr_t sdram_bank_sizes[]);
|
||||
|
||||
#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost"
|
||||
|
||||
/*
|
||||
|
@ -111,6 +99,14 @@ struct Ppc4xxEbcState {
|
|||
};
|
||||
|
||||
/* SDRAM DDR controller */
|
||||
typedef struct {
|
||||
MemoryRegion ram;
|
||||
MemoryRegion container; /* used for clipping */
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
uint32_t bcr;
|
||||
} Ppc4xxSdramBank;
|
||||
|
||||
#define SDR0_DDR0_DDRM_ENCODE(n) ((((unsigned long)(n)) & 0x03) << 29)
|
||||
#define SDR0_DDR0_DDRM_DDR1 0x20000000
|
||||
#define SDR0_DDR0_DDRM_DDR2 0x40000000
|
||||
|
|
|
@ -73,6 +73,7 @@ void ppc_store_msr(CPUPPCState *env, target_ulong value)
|
|||
hreg_store_msr(env, value, 0);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
|
||||
{
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
|
@ -81,7 +82,10 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
|
|||
env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
|
||||
/* The gtse bit affects hflags */
|
||||
hreg_compute_hflags(env);
|
||||
|
||||
ppc_maybe_interrupt(env);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void fpscr_set_rounding_mode(CPUPPCState *env)
|
||||
{
|
||||
|
|
|
@ -696,7 +696,9 @@ enum {
|
|||
HFLAGS_PR = 14, /* MSR_PR */
|
||||
HFLAGS_PMCC0 = 15, /* MMCR0 PMCC bit 0 */
|
||||
HFLAGS_PMCC1 = 16, /* MMCR0 PMCC bit 1 */
|
||||
HFLAGS_INSN_CNT = 17, /* PMU instruction count enabled */
|
||||
HFLAGS_PMCJCE = 17, /* MMCR0 PMCjCE bit */
|
||||
HFLAGS_PMC_OTHER = 18, /* PMC other than PMC5-6 is enabled */
|
||||
HFLAGS_INSN_CNT = 19, /* PMU instruction count enabled */
|
||||
HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
|
||||
HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
|
||||
|
||||
|
@ -1358,6 +1360,7 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
|
|||
int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
|
||||
int cpuid, DumpState *s);
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void ppc_maybe_interrupt(CPUPPCState *env);
|
||||
void ppc_cpu_do_interrupt(CPUState *cpu);
|
||||
bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||
void ppc_cpu_do_system_reset(CPUState *cs);
|
||||
|
@ -1370,9 +1373,9 @@ void ppc_translate_init(void);
|
|||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void ppc_store_sdr1(CPUPPCState *env, target_ulong value);
|
||||
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
|
||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||
void ppc_store_msr(CPUPPCState *env, target_ulong value);
|
||||
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
|
||||
|
||||
void ppc_cpu_list(void);
|
||||
|
||||
|
@ -2416,27 +2419,27 @@ enum {
|
|||
/* Hardware exceptions definitions */
|
||||
enum {
|
||||
/* External hardware exception sources */
|
||||
PPC_INTERRUPT_RESET = 0, /* Reset exception */
|
||||
PPC_INTERRUPT_WAKEUP, /* Wakeup exception */
|
||||
PPC_INTERRUPT_MCK, /* Machine check exception */
|
||||
PPC_INTERRUPT_EXT, /* External interrupt */
|
||||
PPC_INTERRUPT_SMI, /* System management interrupt */
|
||||
PPC_INTERRUPT_CEXT, /* Critical external interrupt */
|
||||
PPC_INTERRUPT_DEBUG, /* External debug exception */
|
||||
PPC_INTERRUPT_THERM, /* Thermal exception */
|
||||
PPC_INTERRUPT_RESET = 0x00001, /* Reset exception */
|
||||
PPC_INTERRUPT_WAKEUP = 0x00002, /* Wakeup exception */
|
||||
PPC_INTERRUPT_MCK = 0x00004, /* Machine check exception */
|
||||
PPC_INTERRUPT_EXT = 0x00008, /* External interrupt */
|
||||
PPC_INTERRUPT_SMI = 0x00010, /* System management interrupt */
|
||||
PPC_INTERRUPT_CEXT = 0x00020, /* Critical external interrupt */
|
||||
PPC_INTERRUPT_DEBUG = 0x00040, /* External debug exception */
|
||||
PPC_INTERRUPT_THERM = 0x00080, /* Thermal exception */
|
||||
/* Internal hardware exception sources */
|
||||
PPC_INTERRUPT_DECR, /* Decrementer exception */
|
||||
PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */
|
||||
PPC_INTERRUPT_PIT, /* Programmable interval timer interrupt */
|
||||
PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */
|
||||
PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */
|
||||
PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */
|
||||
PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */
|
||||
PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
|
||||
PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */
|
||||
PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */
|
||||
PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */
|
||||
PPC_INTERRUPT_EBB, /* Event-based Branch exception */
|
||||
PPC_INTERRUPT_DECR = 0x00100, /* Decrementer exception */
|
||||
PPC_INTERRUPT_HDECR = 0x00200, /* Hypervisor decrementer exception */
|
||||
PPC_INTERRUPT_PIT = 0x00400, /* Programmable interval timer int. */
|
||||
PPC_INTERRUPT_FIT = 0x00800, /* Fixed interval timer interrupt */
|
||||
PPC_INTERRUPT_WDT = 0x01000, /* Watchdog timer interrupt */
|
||||
PPC_INTERRUPT_CDOORBELL = 0x02000, /* Critical doorbell interrupt */
|
||||
PPC_INTERRUPT_DOORBELL = 0x04000, /* Doorbell interrupt */
|
||||
PPC_INTERRUPT_PERFM = 0x08000, /* Performance monitor interrupt */
|
||||
PPC_INTERRUPT_HMI = 0x10000, /* Hypervisor Maintenance interrupt */
|
||||
PPC_INTERRUPT_HDOORBELL = 0x20000, /* Hypervisor Doorbell interrupt */
|
||||
PPC_INTERRUPT_HVIRT = 0x40000, /* Hypervisor virtualization interrupt */
|
||||
PPC_INTERRUPT_EBB = 0x80000, /* Event-based Branch exception */
|
||||
};
|
||||
|
||||
/* Processor Compatibility mask (PCR) */
|
||||
|
|
|
@ -5960,46 +5960,10 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER7(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (cs->halted) {
|
||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
return false;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
|
||||
return true;
|
||||
}
|
||||
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return FIELD_EX64(env->msr, MSR, EE) &&
|
||||
(cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
dc->fw_name = "PowerPC,POWER7";
|
||||
dc->desc = "POWER7";
|
||||
|
@ -6008,7 +5972,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
|||
pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
pcc->init_proc = init_proc_POWER7;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
cc->has_work = cpu_has_work_POWER7;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
|
@ -6133,54 +6096,10 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER8(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (cs->halted) {
|
||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
return false;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
|
||||
return true;
|
||||
}
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
|
||||
return true;
|
||||
}
|
||||
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return FIELD_EX64(env->msr, MSR, EE) &&
|
||||
(cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
dc->fw_name = "PowerPC,POWER8";
|
||||
dc->desc = "POWER8";
|
||||
|
@ -6189,7 +6108,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
|||
pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
pcc->init_proc = init_proc_POWER8;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
cc->has_work = cpu_has_work_POWER8;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
|
@ -6351,71 +6269,10 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER9(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (cs->halted) {
|
||||
uint64_t psscr = env->spr[SPR_PSSCR];
|
||||
|
||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If EC is clear, just return true on any pending interrupt */
|
||||
if (!(psscr & PSSCR_EC)) {
|
||||
return true;
|
||||
}
|
||||
/* External Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_EEE)) {
|
||||
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
|
||||
if (!heic || !FIELD_EX64_HV(env->msr) ||
|
||||
FIELD_EX64(env->msr, MSR, PR)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Decrementer Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_DEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Machine Check or Hypervisor Maintenance Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK |
|
||||
1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Privileged Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_PDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor virtualization exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
|
||||
return true;
|
||||
}
|
||||
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return FIELD_EX64(env->msr, MSR, EE) &&
|
||||
(cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
dc->fw_name = "PowerPC,POWER9";
|
||||
dc->desc = "POWER9";
|
||||
|
@ -6425,7 +6282,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
|
|||
PCR_COMPAT_2_05;
|
||||
pcc->init_proc = init_proc_POWER9;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
cc->has_work = cpu_has_work_POWER9;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
|
@ -6584,71 +6440,10 @@ static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER10(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (cs->halted) {
|
||||
uint64_t psscr = env->spr[SPR_PSSCR];
|
||||
|
||||
if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If EC is clear, just return true on any pending interrupt */
|
||||
if (!(psscr & PSSCR_EC)) {
|
||||
return true;
|
||||
}
|
||||
/* External Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_EEE)) {
|
||||
bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC);
|
||||
if (!heic || !FIELD_EX64_HV(env->msr) ||
|
||||
FIELD_EX64(env->msr, MSR, PR)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* Decrementer Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_DEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Machine Check or Hypervisor Maintenance Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK |
|
||||
1u << PPC_INTERRUPT_HMI)) && (env->spr[SPR_LPCR] & LPCR_OEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Privileged Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_PDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor Doorbell Exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HDEE)) {
|
||||
return true;
|
||||
}
|
||||
/* Hypervisor virtualization exception */
|
||||
if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) &&
|
||||
(env->spr[SPR_LPCR] & LPCR_HVEE)) {
|
||||
return true;
|
||||
}
|
||||
if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return FIELD_EX64(env->msr, MSR, EE) &&
|
||||
(cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
|
||||
dc->fw_name = "PowerPC,POWER10";
|
||||
dc->desc = "POWER10";
|
||||
|
@ -6659,7 +6454,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
|
|||
PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
pcc->init_proc = init_proc_POWER10;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
cc->has_work = cpu_has_work_POWER10;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
|
@ -7232,11 +7026,7 @@ static void ppc_restore_state_to_opc(CPUState *cs,
|
|||
|
||||
static bool ppc_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
return FIELD_EX64(env->msr, MSR, EE) &&
|
||||
(cs->interrupt_request & CPU_INTERRUPT_HARD);
|
||||
return cs->interrupt_request & CPU_INTERRUPT_HARD;
|
||||
}
|
||||
|
||||
static void ppc_cpu_reset(DeviceState *dev)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3241,93 +3241,82 @@ void helper_XVXSIGSP(ppc_vsr_t *xt, ppc_vsr_t *xb)
|
|||
*xt = t;
|
||||
}
|
||||
|
||||
/*
|
||||
* VSX_TEST_DC - VSX floating point test data class
|
||||
* op - instruction mnemonic
|
||||
* nels - number of elements (1, 2 or 4)
|
||||
* xbn - VSR register number
|
||||
* tp - type (float32 or float64)
|
||||
* fld - vsr_t field (VsrD(*) or VsrW(*))
|
||||
* tfld - target vsr_t field (VsrD(*) or VsrW(*))
|
||||
* fld_max - target field max
|
||||
* scrf - set result in CR and FPCC
|
||||
*/
|
||||
#define VSX_TEST_DC(op, nels, xbn, tp, fld, tfld, fld_max, scrf) \
|
||||
void helper_##op(CPUPPCState *env, uint32_t opcode) \
|
||||
#define VSX_TSTDC(tp) \
|
||||
static int32_t tp##_tstdc(tp b, uint32_t dcmx) \
|
||||
{ \
|
||||
ppc_vsr_t *xt = &env->vsr[xT(opcode)]; \
|
||||
ppc_vsr_t *xb = &env->vsr[xbn]; \
|
||||
ppc_vsr_t t = { }; \
|
||||
uint32_t i, sign, dcmx; \
|
||||
uint32_t cc, match = 0; \
|
||||
\
|
||||
if (!scrf) { \
|
||||
dcmx = DCMX_XV(opcode); \
|
||||
} else { \
|
||||
t = *xt; \
|
||||
dcmx = DCMX(opcode); \
|
||||
} \
|
||||
\
|
||||
for (i = 0; i < nels; i++) { \
|
||||
sign = tp##_is_neg(xb->fld); \
|
||||
if (tp##_is_any_nan(xb->fld)) { \
|
||||
match = extract32(dcmx, 6, 1); \
|
||||
} else if (tp##_is_infinity(xb->fld)) { \
|
||||
match = extract32(dcmx, 4 + !sign, 1); \
|
||||
} else if (tp##_is_zero(xb->fld)) { \
|
||||
match = extract32(dcmx, 2 + !sign, 1); \
|
||||
} else if (tp##_is_zero_or_denormal(xb->fld)) { \
|
||||
match = extract32(dcmx, 0 + !sign, 1); \
|
||||
} \
|
||||
\
|
||||
if (scrf) { \
|
||||
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \
|
||||
env->fpscr &= ~FP_FPCC; \
|
||||
env->fpscr |= cc << FPSCR_FPCC; \
|
||||
env->crf[BF(opcode)] = cc; \
|
||||
} else { \
|
||||
t.tfld = match ? fld_max : 0; \
|
||||
} \
|
||||
match = 0; \
|
||||
} \
|
||||
if (!scrf) { \
|
||||
*xt = t; \
|
||||
uint32_t match = 0; \
|
||||
uint32_t sign = tp##_is_neg(b); \
|
||||
if (tp##_is_any_nan(b)) { \
|
||||
match = extract32(dcmx, 6, 1); \
|
||||
} else if (tp##_is_infinity(b)) { \
|
||||
match = extract32(dcmx, 4 + !sign, 1); \
|
||||
} else if (tp##_is_zero(b)) { \
|
||||
match = extract32(dcmx, 2 + !sign, 1); \
|
||||
} else if (tp##_is_zero_or_denormal(b)) { \
|
||||
match = extract32(dcmx, 0 + !sign, 1); \
|
||||
} \
|
||||
return (match != 0); \
|
||||
}
|
||||
|
||||
VSX_TEST_DC(xvtstdcdp, 2, xB(opcode), float64, VsrD(i), VsrD(i), UINT64_MAX, 0)
|
||||
VSX_TEST_DC(xvtstdcsp, 4, xB(opcode), float32, VsrW(i), VsrW(i), UINT32_MAX, 0)
|
||||
VSX_TEST_DC(xststdcdp, 1, xB(opcode), float64, VsrD(0), VsrD(0), 0, 1)
|
||||
VSX_TEST_DC(xststdcqp, 1, (rB(opcode) + 32), float128, f128, VsrD(0), 0, 1)
|
||||
VSX_TSTDC(float32)
|
||||
VSX_TSTDC(float64)
|
||||
VSX_TSTDC(float128)
|
||||
#undef VSX_TSTDC
|
||||
|
||||
void helper_xststdcsp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xb)
|
||||
void helper_XVTSTDCDP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v)
|
||||
{
|
||||
uint32_t dcmx, sign, exp;
|
||||
uint32_t cc, match = 0, not_sp = 0;
|
||||
float64 arg = xb->VsrD(0);
|
||||
float64 arg_sp;
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
t->s64[i] = (int64_t)-float64_tstdc(b->f64[i], dcmx);
|
||||
}
|
||||
}
|
||||
|
||||
dcmx = DCMX(opcode);
|
||||
exp = (arg >> 52) & 0x7FF;
|
||||
sign = float64_is_neg(arg);
|
||||
void helper_XVTSTDCSP(ppc_vsr_t *t, ppc_vsr_t *b, uint64_t dcmx, uint32_t v)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
t->s32[i] = (int32_t)-float32_tstdc(b->f32[i], dcmx);
|
||||
}
|
||||
}
|
||||
|
||||
if (float64_is_any_nan(arg)) {
|
||||
match = extract32(dcmx, 6, 1);
|
||||
} else if (float64_is_infinity(arg)) {
|
||||
match = extract32(dcmx, 4 + !sign, 1);
|
||||
} else if (float64_is_zero(arg)) {
|
||||
match = extract32(dcmx, 2 + !sign, 1);
|
||||
} else if (float64_is_zero_or_denormal(arg) || (exp > 0 && exp < 0x381)) {
|
||||
match = extract32(dcmx, 0 + !sign, 1);
|
||||
static bool not_SP_value(float64 val)
|
||||
{
|
||||
return val != helper_todouble(helper_tosingle(val));
|
||||
}
|
||||
|
||||
/*
|
||||
* VSX_XS_TSTDC - VSX Scalar Test Data Class
|
||||
* NAME - instruction name
|
||||
* FLD - vsr_t field (VsrD(0) or f128)
|
||||
* TP - type (float64 or float128)
|
||||
*/
|
||||
#define VSX_XS_TSTDC(NAME, FLD, TP) \
|
||||
void helper_##NAME(CPUPPCState *env, uint32_t bf, \
|
||||
uint32_t dcmx, ppc_vsr_t *b) \
|
||||
{ \
|
||||
uint32_t cc, match, sign = TP##_is_neg(b->FLD); \
|
||||
match = TP##_tstdc(b->FLD, dcmx); \
|
||||
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT; \
|
||||
env->fpscr &= ~FP_FPCC; \
|
||||
env->fpscr |= cc << FPSCR_FPCC; \
|
||||
env->crf[bf] = cc; \
|
||||
}
|
||||
|
||||
arg_sp = helper_todouble(helper_tosingle(arg));
|
||||
not_sp = arg != arg_sp;
|
||||
VSX_XS_TSTDC(XSTSTDCDP, VsrD(0), float64)
|
||||
VSX_XS_TSTDC(XSTSTDCQP, f128, float128)
|
||||
#undef VSX_XS_TSTDC
|
||||
|
||||
void helper_XSTSTDCSP(CPUPPCState *env, uint32_t bf,
|
||||
uint32_t dcmx, ppc_vsr_t *b)
|
||||
{
|
||||
uint32_t cc, match, sign = float64_is_neg(b->VsrD(0));
|
||||
uint32_t exp = (b->VsrD(0) >> 52) & 0x7FF;
|
||||
int not_sp = (int)not_SP_value(b->VsrD(0));
|
||||
match = float64_tstdc(b->VsrD(0), dcmx) || (exp > 0 && exp < 0x381);
|
||||
cc = sign << CRF_LT_BIT | match << CRF_EQ_BIT | not_sp << CRF_SO_BIT;
|
||||
env->fpscr &= ~FP_FPCC;
|
||||
env->fpscr |= cc << FPSCR_FPCC;
|
||||
env->crf[BF(opcode)] = cc;
|
||||
env->crf[bf] = cc;
|
||||
}
|
||||
|
||||
void helper_xsrqpi(CPUPPCState *env, uint32_t opcode,
|
||||
|
|
|
@ -10,6 +10,7 @@ DEF_HELPER_4(HASHSTP, void, env, tl, tl, tl)
|
|||
DEF_HELPER_4(HASHCHKP, void, env, tl, tl, tl)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
DEF_HELPER_2(store_msr, void, env, tl)
|
||||
DEF_HELPER_1(ppc_maybe_interrupt, void, env)
|
||||
DEF_HELPER_1(rfi, void, env)
|
||||
DEF_HELPER_1(40x_rfci, void, env)
|
||||
DEF_HELPER_1(rfci, void, env)
|
||||
|
@ -29,6 +30,7 @@ DEF_HELPER_2(store_mmcr1, void, env, tl)
|
|||
DEF_HELPER_3(store_pmc, void, env, i32, i64)
|
||||
DEF_HELPER_2(read_pmc, tl, env, i32)
|
||||
DEF_HELPER_2(insns_inc, void, env, i32)
|
||||
DEF_HELPER_1(handle_pmc5_overflow, void, env)
|
||||
#endif
|
||||
DEF_HELPER_1(check_tlb_flush_local, void, env)
|
||||
DEF_HELPER_1(check_tlb_flush_global, void, env)
|
||||
|
@ -143,15 +145,15 @@ DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64)
|
|||
#define dh_ctype_acc ppc_acc_t *
|
||||
#define dh_typecode_acc dh_typecode_ptr
|
||||
|
||||
DEF_HELPER_FLAGS_3(vavgub, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vavguh, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vavguw, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vabsdub, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vabsduh, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vabsduw, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vavgsb, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vavgsh, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vavgsw, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_4(VAVGUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VAVGUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VAVGUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VABSDUB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VABSDUH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VABSDUW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VAVGSB, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VAVGSH, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_4(VAVGSW, TCG_CALL_NO_RWG, void, avr, avr, avr, i32)
|
||||
DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr)
|
||||
|
@ -193,11 +195,7 @@ DEF_HELPER_FLAGS_3(vslo, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
|||
DEF_HELPER_FLAGS_3(vsro, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vsrv, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vslv, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vaddcuw, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vprtybw, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vprtybd, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vprtybq, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vsubcuw, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(VPRTYBQ, TCG_CALL_NO_RWG, void, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
|
||||
|
@ -231,8 +229,6 @@ DEF_HELPER_FLAGS_2(VSTRIBL, TCG_CALL_NO_RWG, i32, avr, avr)
|
|||
DEF_HELPER_FLAGS_2(VSTRIBR, TCG_CALL_NO_RWG, i32, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(VSTRIHL, TCG_CALL_NO_RWG, i32, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(VSTRIHR, TCG_CALL_NO_RWG, i32, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vnegw, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vnegd, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vupkhpx, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vupklpx, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
DEF_HELPER_FLAGS_2(vupkhsb, TCG_CALL_NO_RWG, void, avr, avr)
|
||||
|
@ -258,13 +254,13 @@ DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr)
|
|||
DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkudum, void, env, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_3(vpkpx, TCG_CALL_NO_RWG, void, avr, avr, avr)
|
||||
DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(VMHADDSHS, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(VMHRADDSHS, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_4(VMSUMUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(VMSUMUHS, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_4(VMSUMSHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(VMSUMSHS, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_4(vmladduhm, TCG_CALL_NO_RWG, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_FLAGS_5(VMLADDUHM, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32)
|
||||
DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32)
|
||||
DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env)
|
||||
DEF_HELPER_3(lvebx, void, env, avr, tl)
|
||||
|
@ -423,9 +419,9 @@ DEF_HELPER_3(xscvuxdsp, void, env, vsr, vsr)
|
|||
DEF_HELPER_3(xscvsxdsp, void, env, vsr, vsr)
|
||||
DEF_HELPER_4(xscvudqp, void, env, i32, vsr, vsr)
|
||||
DEF_HELPER_3(xscvuxddp, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xststdcsp, void, env, i32, vsr)
|
||||
DEF_HELPER_2(xststdcdp, void, env, i32)
|
||||
DEF_HELPER_2(xststdcqp, void, env, i32)
|
||||
DEF_HELPER_4(XSTSTDCSP, void, env, i32, i32, vsr)
|
||||
DEF_HELPER_4(XSTSTDCDP, void, env, i32, i32, vsr)
|
||||
DEF_HELPER_4(XSTSTDCQP, void, env, i32, i32, vsr)
|
||||
DEF_HELPER_3(xsrdpi, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xsrdpic, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xsrdpim, void, env, vsr, vsr)
|
||||
|
@ -523,8 +519,8 @@ DEF_HELPER_3(xvcvsxdsp, void, env, vsr, vsr)
|
|||
DEF_HELPER_3(xvcvuxdsp, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xvcvsxwsp, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xvcvuxwsp, void, env, vsr, vsr)
|
||||
DEF_HELPER_2(xvtstdcsp, void, env, i32)
|
||||
DEF_HELPER_2(xvtstdcdp, void, env, i32)
|
||||
DEF_HELPER_FLAGS_4(XVTSTDCSP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32)
|
||||
DEF_HELPER_FLAGS_4(XVTSTDCDP, TCG_CALL_NO_RWG, void, vsr, vsr, i64, i32)
|
||||
DEF_HELPER_3(xvrspi, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xvrspic, void, env, vsr, vsr)
|
||||
DEF_HELPER_3(xvrspim, void, env, vsr, vsr)
|
||||
|
|
|
@ -109,6 +109,9 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
|||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) {
|
||||
hflags |= 1 << HFLAGS_PMCC1;
|
||||
}
|
||||
if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) {
|
||||
hflags |= 1 << HFLAGS_PMCJCE;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
|
||||
|
@ -119,6 +122,9 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
|||
if (env->pmc_ins_cnt) {
|
||||
hflags |= 1 << HFLAGS_INSN_CNT;
|
||||
}
|
||||
if (env->pmc_ins_cnt & 0x1e) {
|
||||
hflags |= 1 << HFLAGS_PMC_OTHER;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -260,6 +266,8 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
|
|||
env->msr = value;
|
||||
hreg_compute_hflags(env);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
ppc_maybe_interrupt(env);
|
||||
|
||||
if (unlikely(FIELD_EX64(env->msr, MSR, POW))) {
|
||||
if (!env->pending_interrupts && (*env->check_pow)(env)) {
|
||||
cs->halted = 1;
|
||||
|
|
|
@ -199,6 +199,12 @@
|
|||
|
||||
@XX2_uim4 ...... ..... . uim:4 ..... ......... .. &XX2_uim xt=%xx_xt xb=%xx_xb
|
||||
|
||||
%xx_uim7 6:1 2:1 16:5
|
||||
@XX2_uim7 ...... ..... ..... ..... .... . ... . .. &XX2_uim xt=%xx_xt xb=%xx_xb uim=%xx_uim7
|
||||
|
||||
&XX2_bf_uim bf xb uim
|
||||
@XX2_bf_uim ...... bf:3 uim:7 ..... ......... . . &XX2_bf_uim
|
||||
|
||||
&XX2_bf_xb bf xb
|
||||
@XX2_bf_xb ...... bf:3 .. ..... ..... ......... . . &XX2_bf_xb xb=%xx_xb
|
||||
|
||||
|
@ -519,6 +525,21 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC
|
|||
VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf
|
||||
VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf
|
||||
|
||||
## Vector Integer Average Instructions
|
||||
|
||||
VAVGSB 000100 ..... ..... ..... 10100000010 @VX
|
||||
VAVGSH 000100 ..... ..... ..... 10101000010 @VX
|
||||
VAVGSW 000100 ..... ..... ..... 10110000010 @VX
|
||||
VAVGUB 000100 ..... ..... ..... 10000000010 @VX
|
||||
VAVGUH 000100 ..... ..... ..... 10001000010 @VX
|
||||
VAVGUW 000100 ..... ..... ..... 10010000010 @VX
|
||||
|
||||
## Vector Integer Absolute Difference Instructions
|
||||
|
||||
VABSDUB 000100 ..... ..... ..... 10000000011 @VX
|
||||
VABSDUH 000100 ..... ..... ..... 10001000011 @VX
|
||||
VABSDUW 000100 ..... ..... ..... 10010000011 @VX
|
||||
|
||||
## Vector Bit Manipulation Instruction
|
||||
|
||||
VGNB 000100 ..... -- ... ..... 10011001100 @VX_n
|
||||
|
@ -529,6 +550,10 @@ VCTZDM 000100 ..... ..... ..... 11111000100 @VX
|
|||
VPDEPD 000100 ..... ..... ..... 10111001101 @VX
|
||||
VPEXTD 000100 ..... ..... ..... 10110001101 @VX
|
||||
|
||||
VPRTYBD 000100 ..... 01001 ..... 11000000010 @VX_tb
|
||||
VPRTYBQ 000100 ..... 01010 ..... 11000000010 @VX_tb
|
||||
VPRTYBW 000100 ..... 01000 ..... 11000000010 @VX_tb
|
||||
|
||||
## Vector Permute and Formatting Instruction
|
||||
|
||||
VEXTDUBVLX 000100 ..... ..... ..... ..... 011000 @VA
|
||||
|
@ -608,12 +633,14 @@ VRLQNM 000100 ..... ..... ..... 00101000101 @VX
|
|||
|
||||
## Vector Integer Arithmetic Instructions
|
||||
|
||||
VADDCUW 000100 ..... ..... ..... 00110000000 @VX
|
||||
VADDCUQ 000100 ..... ..... ..... 00101000000 @VX
|
||||
VADDUQM 000100 ..... ..... ..... 00100000000 @VX
|
||||
|
||||
VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA
|
||||
VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA
|
||||
|
||||
VSUBCUW 000100 ..... ..... ..... 10110000000 @VX
|
||||
VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX
|
||||
VSUBUQM 000100 ..... ..... ..... 10100000000 @VX
|
||||
|
||||
|
@ -627,6 +654,9 @@ VEXTSH2D 000100 ..... 11001 ..... 11000000010 @VX_tb
|
|||
VEXTSW2D 000100 ..... 11010 ..... 11000000010 @VX_tb
|
||||
VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb
|
||||
|
||||
VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb
|
||||
VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb
|
||||
|
||||
## Vector Mask Manipulation Instructions
|
||||
|
||||
MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb
|
||||
|
@ -693,6 +723,10 @@ VMSUMUHS 000100 ..... ..... ..... ..... 100111 @VA
|
|||
VMSUMCUD 000100 ..... ..... ..... ..... 010111 @VA
|
||||
VMSUMUDM 000100 ..... ..... ..... ..... 100011 @VA
|
||||
|
||||
VMLADDUHM 000100 ..... ..... ..... ..... 100010 @VA
|
||||
VMHADDSHS 000100 ..... ..... ..... ..... 100000 @VA
|
||||
VMHRADDSHS 000100 ..... ..... ..... ..... 100001 @VA
|
||||
|
||||
## Vector String Instructions
|
||||
|
||||
VSTRIBL 000100 ..... 00000 ..... . 0000001101 @VX_tb_rc
|
||||
|
@ -726,6 +760,17 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX
|
|||
STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX
|
||||
STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX
|
||||
|
||||
## VSX Vector Binary Floating-Point Sign Manipulation Instructions
|
||||
|
||||
XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2
|
||||
XVABSSP 111100 ..... 00000 ..... 110011001 .. @XX2
|
||||
XVNABSDP 111100 ..... 00000 ..... 111101001 .. @XX2
|
||||
XVNABSSP 111100 ..... 00000 ..... 110101001 .. @XX2
|
||||
XVNEGDP 111100 ..... 00000 ..... 111111001 .. @XX2
|
||||
XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2
|
||||
XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3
|
||||
XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3
|
||||
|
||||
## VSX Scalar Multiply-Add Instructions
|
||||
|
||||
XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3
|
||||
|
@ -809,6 +854,11 @@ XSCVSPDPN 111100 ..... ----- ..... 101001011 .. @XX2
|
|||
## VSX Binary Floating-Point Math Support Instructions
|
||||
|
||||
XVXSIGSP 111100 ..... 01001 ..... 111011011 .. @XX2
|
||||
XVTSTDCDP 111100 ..... ..... ..... 1111 . 101 ... @XX2_uim7
|
||||
XVTSTDCSP 111100 ..... ..... ..... 1101 . 101 ... @XX2_uim7
|
||||
XSTSTDCSP 111100 ... ....... ..... 100101010 . - @XX2_bf_uim xb=%xx_xb
|
||||
XSTSTDCDP 111100 ... ....... ..... 101101010 . - @XX2_bf_uim xb=%xx_xb
|
||||
XSTSTDCQP 111111 ... ....... xb:5 1011000100 - @XX2_bf_uim
|
||||
|
||||
## VSX Vector Test Least-Significant Bit by Byte Instruction
|
||||
|
||||
|
@ -908,3 +958,11 @@ SLBSYNC 011111 ----- ----- ----- 0101010010 -
|
|||
|
||||
TLBIE 011111 ..... - .. . . ..... 0100110010 - @X_tlbie
|
||||
TLBIEL 011111 ..... - .. . . ..... 0100010010 - @X_tlbie
|
||||
|
||||
# Processor Control Instructions
|
||||
|
||||
MSGCLR 011111 ----- ----- ..... 0011101110 - @X_rb
|
||||
MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb
|
||||
MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb
|
||||
MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb
|
||||
MSGSYNC 011111 ----- ----- ----- 1101110110 -
|
||||
|
|
|
@ -492,40 +492,8 @@ static inline void set_vscr_sat(CPUPPCState *env)
|
|||
env->vscr_sat.u32[0] = 1;
|
||||
}
|
||||
|
||||
void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
|
||||
r->u32[i] = ~a->u32[i] < b->u32[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* vprtybw */
|
||||
void helper_vprtybw(ppc_avr_t *r, ppc_avr_t *b)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
|
||||
uint64_t res = b->u32[i] ^ (b->u32[i] >> 16);
|
||||
res ^= res >> 8;
|
||||
r->u32[i] = res & 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* vprtybd */
|
||||
void helper_vprtybd(ppc_avr_t *r, ppc_avr_t *b)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
|
||||
uint64_t res = b->u64[i] ^ (b->u64[i] >> 32);
|
||||
res ^= res >> 16;
|
||||
res ^= res >> 8;
|
||||
r->u64[i] = res & 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* vprtybq */
|
||||
void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b)
|
||||
void helper_VPRTYBQ(ppc_avr_t *r, ppc_avr_t *b, uint32_t v)
|
||||
{
|
||||
uint64_t res = b->u64[0] ^ b->u64[1];
|
||||
res ^= res >> 32;
|
||||
|
@ -602,29 +570,27 @@ VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw)
|
|||
#undef VARITHSAT_SIGNED
|
||||
#undef VARITHSAT_UNSIGNED
|
||||
|
||||
#define VAVG_DO(name, element, etype) \
|
||||
void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
|
||||
{ \
|
||||
int i; \
|
||||
\
|
||||
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
|
||||
etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \
|
||||
r->element[i] = x >> 1; \
|
||||
} \
|
||||
#define VAVG(name, element, etype) \
|
||||
void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\
|
||||
{ \
|
||||
int i; \
|
||||
\
|
||||
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
|
||||
etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \
|
||||
r->element[i] = x >> 1; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define VAVG(type, signed_element, signed_type, unsigned_element, \
|
||||
unsigned_type) \
|
||||
VAVG_DO(avgs##type, signed_element, signed_type) \
|
||||
VAVG_DO(avgu##type, unsigned_element, unsigned_type)
|
||||
VAVG(b, s8, int16_t, u8, uint16_t)
|
||||
VAVG(h, s16, int32_t, u16, uint32_t)
|
||||
VAVG(w, s32, int64_t, u32, uint64_t)
|
||||
#undef VAVG_DO
|
||||
VAVG(VAVGSB, s8, int16_t)
|
||||
VAVG(VAVGUB, u8, uint16_t)
|
||||
VAVG(VAVGSH, s16, int32_t)
|
||||
VAVG(VAVGUH, u16, uint32_t)
|
||||
VAVG(VAVGSW, s32, int64_t)
|
||||
VAVG(VAVGUW, u32, uint64_t)
|
||||
#undef VAVG
|
||||
|
||||
#define VABSDU_DO(name, element) \
|
||||
void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
|
||||
#define VABSDU(name, element) \
|
||||
void helper_##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t v)\
|
||||
{ \
|
||||
int i; \
|
||||
\
|
||||
|
@ -640,12 +606,9 @@ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
|
|||
* name - instruction mnemonic suffix (b: byte, h: halfword, w: word)
|
||||
* element - element type to access from vector
|
||||
*/
|
||||
#define VABSDU(type, element) \
|
||||
VABSDU_DO(absdu##type, element)
|
||||
VABSDU(b, u8)
|
||||
VABSDU(h, u16)
|
||||
VABSDU(w, u32)
|
||||
#undef VABSDU_DO
|
||||
VABSDU(VABSDUB, u8)
|
||||
VABSDU(VABSDUH, u16)
|
||||
VABSDU(VABSDUW, u32)
|
||||
#undef VABSDU
|
||||
|
||||
#define VCF(suffix, cvt, element) \
|
||||
|
@ -939,7 +902,7 @@ target_ulong helper_vctzlsbb(ppc_avr_t *r)
|
|||
return count;
|
||||
}
|
||||
|
||||
void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
||||
void helper_VMHADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
||||
ppc_avr_t *b, ppc_avr_t *c)
|
||||
{
|
||||
int sat = 0;
|
||||
|
@ -957,7 +920,7 @@ void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
|||
}
|
||||
}
|
||||
|
||||
void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
||||
void helper_VMHRADDSHS(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
||||
ppc_avr_t *b, ppc_avr_t *c)
|
||||
{
|
||||
int sat = 0;
|
||||
|
@ -974,7 +937,8 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a,
|
|||
}
|
||||
}
|
||||
|
||||
void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
|
||||
void helper_VMLADDUHM(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c,
|
||||
uint32_t v)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -1936,18 +1900,6 @@ XXBLEND(W, 32)
|
|||
XXBLEND(D, 64)
|
||||
#undef XXBLEND
|
||||
|
||||
#define VNEG(name, element) \
|
||||
void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \
|
||||
{ \
|
||||
int i; \
|
||||
for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
|
||||
r->element[i] = -b->element[i]; \
|
||||
} \
|
||||
}
|
||||
VNEG(vnegw, s32)
|
||||
VNEG(vnegd, s64)
|
||||
#undef VNEG
|
||||
|
||||
void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||
{
|
||||
int sh = (b->VsrB(0xf) >> 3) & 0xf;
|
||||
|
@ -1961,15 +1913,6 @@ void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
|||
#endif
|
||||
}
|
||||
|
||||
void helper_vsubcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
|
||||
r->u32[i] = a->u32[i] >= b->u32[i];
|
||||
}
|
||||
}
|
||||
|
||||
void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
|
||||
{
|
||||
int64_t t;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
|
||||
#include "helper_regs.h"
|
||||
|
||||
|
@ -163,7 +164,7 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
|
|||
helper_hfscr_facility_check(env, HFSCR_MSGP, "load DPDES", HFSCR_IC_MSGP);
|
||||
|
||||
/* TODO: TCG supports only one thread */
|
||||
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
||||
if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) {
|
||||
dpdes = 1;
|
||||
}
|
||||
|
||||
|
@ -173,7 +174,6 @@ target_ulong helper_load_dpdes(CPUPPCState *env)
|
|||
void helper_store_dpdes(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = env_archcpu(env);
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP);
|
||||
|
||||
|
@ -184,12 +184,7 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val)
|
|||
return;
|
||||
}
|
||||
|
||||
if (val & 0x1) {
|
||||
env->pending_interrupts |= 1 << PPC_INTERRUPT_DOORBELL;
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
||||
}
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1);
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
||||
|
|
|
@ -238,6 +238,8 @@ static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type,
|
|||
|
||||
static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
/*
|
||||
* Check if this is a valid level, according to POWER9 and POWER10
|
||||
* Processor User's Manuals, sections 4.10.4.1 and 5.10.6.1, respectively:
|
||||
|
@ -249,16 +251,25 @@ static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls)
|
|||
*/
|
||||
switch (level) {
|
||||
case 0: /* Root Page Dir */
|
||||
return psize == 52 && nls == 13;
|
||||
ret = psize == 52 && nls == 13;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
return nls == 9;
|
||||
ret = nls == 9;
|
||||
break;
|
||||
case 3:
|
||||
return nls == 9 || nls == 5;
|
||||
ret = nls == 9 || nls == 5;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid radix level: %d\n", level);
|
||||
return false;
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (unlikely(!ret)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid radix configuration: "
|
||||
"level %d size %d nls %"PRIu64"\n",
|
||||
level, psize, nls);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr,
|
||||
|
@ -519,11 +530,13 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu,
|
|||
|
||||
if (!ppc_radix64_is_valid_level(level++, *g_page_size, nls)) {
|
||||
fault_cause |= DSISR_R_BADCONFIG;
|
||||
return 1;
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK,
|
||||
&h_raddr, &nls, g_page_size,
|
||||
&pte, &fault_cause);
|
||||
}
|
||||
|
||||
ret = ppc_radix64_next_level(cs->as, eaddr & R_EADDR_MASK, &h_raddr,
|
||||
&nls, g_page_size, &pte, &fault_cause);
|
||||
if (ret) {
|
||||
/* No valid pte */
|
||||
if (guest_visible) {
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
|
||||
|
||||
static bool pmc_has_overflow_enabled(CPUPPCState *env, int sprn)
|
||||
{
|
||||
if (sprn == SPR_POWER_PMC1) {
|
||||
|
@ -88,49 +86,47 @@ static bool pmu_increment_insns(CPUPPCState *env, uint32_t num_insns)
|
|||
bool overflow_triggered = false;
|
||||
target_ulong tmp;
|
||||
|
||||
if (unlikely(ins_cnt & 0x1e)) {
|
||||
if (ins_cnt & (1 << 1)) {
|
||||
tmp = env->spr[SPR_POWER_PMC1];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC1] = tmp;
|
||||
if (ins_cnt & (1 << 1)) {
|
||||
tmp = env->spr[SPR_POWER_PMC1];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMC1CE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC1] = tmp;
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 2)) {
|
||||
tmp = env->spr[SPR_POWER_PMC2];
|
||||
if (ins_cnt & (1 << 2)) {
|
||||
tmp = env->spr[SPR_POWER_PMC2];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC2] = tmp;
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 3)) {
|
||||
tmp = env->spr[SPR_POWER_PMC3];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC3] = tmp;
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 4)) {
|
||||
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
|
||||
int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
|
||||
tmp = env->spr[SPR_POWER_PMC4];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC2] = tmp;
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 3)) {
|
||||
tmp = env->spr[SPR_POWER_PMC3];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC3] = tmp;
|
||||
}
|
||||
|
||||
if (ins_cnt & (1 << 4)) {
|
||||
target_ulong mmcr1 = env->spr[SPR_POWER_MMCR1];
|
||||
int sel = extract64(mmcr1, MMCR1_PMC4EVT_EXTR, MMCR1_EVT_SIZE);
|
||||
if (sel == 0x02 || (env->spr[SPR_CTRL] & CTRL_RUN)) {
|
||||
tmp = env->spr[SPR_POWER_PMC4];
|
||||
tmp += num_insns;
|
||||
if (tmp >= PMC_COUNTER_NEGATIVE_VAL && (mmcr0 & MMCR0_PMCjCE)) {
|
||||
tmp = PMC_COUNTER_NEGATIVE_VAL;
|
||||
overflow_triggered = true;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC4] = tmp;
|
||||
}
|
||||
env->spr[SPR_POWER_PMC4] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -310,6 +306,12 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu)
|
|||
raise_ebb_perfm_exception(env);
|
||||
}
|
||||
|
||||
void helper_handle_pmc5_overflow(CPUPPCState *env)
|
||||
{
|
||||
env->spr[SPR_POWER_PMC5] = PMC_COUNTER_NEGATIVE_VAL;
|
||||
fire_PMC_interrupt(env_archcpu(env));
|
||||
}
|
||||
|
||||
/* This helper assumes that the PMC is running. */
|
||||
void helper_insns_inc(CPUPPCState *env, uint32_t num_insns)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#define POWER8_PMU_H
|
||||
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
|
||||
#define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL
|
||||
|
||||
void cpu_ppc_pmu_init(CPUPPCState *env);
|
||||
void pmu_update_summaries(CPUPPCState *env);
|
||||
#else
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "exec/log.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "spr_common.h"
|
||||
#include "power8-pmu.h"
|
||||
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qapi/error.h"
|
||||
|
@ -177,6 +178,8 @@ struct DisasContext {
|
|||
bool hr;
|
||||
bool mmcr0_pmcc0;
|
||||
bool mmcr0_pmcc1;
|
||||
bool mmcr0_pmcjce;
|
||||
bool pmc_other;
|
||||
bool pmu_insn_cnt;
|
||||
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
|
||||
int singlestep_enabled;
|
||||
|
@ -305,6 +308,14 @@ static void gen_icount_io_start(DisasContext *ctx)
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static void gen_ppc_maybe_interrupt(DisasContext *ctx)
|
||||
{
|
||||
gen_icount_io_start(ctx);
|
||||
gen_helper_ppc_maybe_interrupt(cpu_env);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tells the caller what is the appropriate exception to generate and prepares
|
||||
* SPR registers for this exception.
|
||||
|
@ -4261,6 +4272,9 @@ static void pmu_count_insns(DisasContext *ctx)
|
|||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
TCGLabel *l;
|
||||
TCGv t0;
|
||||
|
||||
/*
|
||||
* The PMU insns_inc() helper stops the internal PMU timer if a
|
||||
* counter overflows happens. In that case, if the guest is
|
||||
|
@ -4269,8 +4283,26 @@ static void pmu_count_insns(DisasContext *ctx)
|
|||
*/
|
||||
gen_icount_io_start(ctx);
|
||||
|
||||
gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
|
||||
#else
|
||||
/* Avoid helper calls when only PMC5-6 are enabled. */
|
||||
if (!ctx->pmc_other) {
|
||||
l = gen_new_label();
|
||||
t0 = tcg_temp_new();
|
||||
|
||||
gen_load_spr(t0, SPR_POWER_PMC5);
|
||||
tcg_gen_addi_tl(t0, t0, ctx->base.num_insns);
|
||||
gen_store_spr(SPR_POWER_PMC5, t0);
|
||||
/* Check for overflow, if it's enabled */
|
||||
if (ctx->mmcr0_pmcjce) {
|
||||
tcg_gen_brcondi_tl(TCG_COND_LT, t0, PMC_COUNTER_NEGATIVE_VAL, l);
|
||||
gen_helper_handle_pmc5_overflow(cpu_env);
|
||||
}
|
||||
|
||||
gen_set_label(l);
|
||||
tcg_temp_free(t0);
|
||||
} else {
|
||||
gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns));
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* User mode can read (but not write) PMC5 and start/stop
|
||||
* the PMU via MMCR0_FC. In this case just increment
|
||||
|
@ -4283,7 +4315,7 @@ static void pmu_count_insns(DisasContext *ctx)
|
|||
gen_store_spr(SPR_POWER_PMC5, t0);
|
||||
|
||||
tcg_temp_free(t0);
|
||||
#endif /* #if !defined(CONFIG_USER_ONLY) */
|
||||
#endif /* #if !defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
#else
|
||||
static void pmu_count_insns(DisasContext *ctx)
|
||||
|
@ -6161,7 +6193,6 @@ static void gen_tlbilx_booke206(DisasContext *ctx)
|
|||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
|
||||
/* wrtee */
|
||||
static void gen_wrtee(DisasContext *ctx)
|
||||
{
|
||||
|
@ -6175,6 +6206,7 @@ static void gen_wrtee(DisasContext *ctx)
|
|||
tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE));
|
||||
tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
|
||||
tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
|
||||
gen_ppc_maybe_interrupt(ctx);
|
||||
tcg_temp_free(t0);
|
||||
/*
|
||||
* Stop translation to have a chance to raise an exception if we
|
||||
|
@ -6193,6 +6225,7 @@ static void gen_wrteei(DisasContext *ctx)
|
|||
CHK_SV(ctx);
|
||||
if (ctx->opcode & 0x00008000) {
|
||||
tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE));
|
||||
gen_ppc_maybe_interrupt(ctx);
|
||||
/* Stop translation to have a chance to raise an exception */
|
||||
ctx->base.is_jmp = DISAS_EXIT_UPDATE;
|
||||
} else {
|
||||
|
@ -6239,68 +6272,6 @@ static void gen_icbt_440(DisasContext *ctx)
|
|||
*/
|
||||
}
|
||||
|
||||
/* Embedded.Processor Control */
|
||||
|
||||
static void gen_msgclr(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV(ctx);
|
||||
#else
|
||||
CHK_HV(ctx);
|
||||
if (is_book3s_arch2x(ctx)) {
|
||||
gen_helper_book3s_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
} else {
|
||||
gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
}
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
static void gen_msgsnd(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV(ctx);
|
||||
#else
|
||||
CHK_HV(ctx);
|
||||
if (is_book3s_arch2x(ctx)) {
|
||||
gen_helper_book3s_msgsnd(cpu_gpr[rB(ctx->opcode)]);
|
||||
} else {
|
||||
gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]);
|
||||
}
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static void gen_msgclrp(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV(ctx);
|
||||
#else
|
||||
CHK_SV(ctx);
|
||||
gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
|
||||
static void gen_msgsndp(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV(ctx);
|
||||
#else
|
||||
CHK_SV(ctx);
|
||||
gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[rB(ctx->opcode)]);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gen_msgsync(DisasContext *ctx)
|
||||
{
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
GEN_PRIV(ctx);
|
||||
#else
|
||||
CHK_HV(ctx);
|
||||
#endif /* defined(CONFIG_USER_ONLY) */
|
||||
/* interpreted as no-op */
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
static void gen_maddld(DisasContext *ctx)
|
||||
{
|
||||
|
@ -6545,12 +6516,12 @@ static int64_t dw_compose_ea(DisasContext *ctx, int x)
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_HV(CTX) \
|
||||
do { \
|
||||
if (unlikely((CTX)->pr || !(CTX)->hv)) \
|
||||
gen_priv_opc(CTX); \
|
||||
return true; \
|
||||
} \
|
||||
#define REQUIRE_HV(CTX) \
|
||||
do { \
|
||||
if (unlikely((CTX)->pr || !(CTX)->hv)) { \
|
||||
gen_priv_opc(CTX); \
|
||||
return true; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define REQUIRE_SV(CTX) do { gen_priv_opc(CTX); return true; } while (0)
|
||||
|
@ -6628,6 +6599,8 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a)
|
|||
|
||||
#include "translate/branch-impl.c.inc"
|
||||
|
||||
#include "translate/processor-ctrl-impl.c.inc"
|
||||
|
||||
#include "translate/storage-ctrl-impl.c.inc"
|
||||
|
||||
/* Handles lfdp */
|
||||
|
@ -6901,12 +6874,6 @@ GEN_HANDLER2_E(tlbivax_booke206, "tlbivax", 0x1F, 0x12, 0x18, 0x00000001,
|
|||
PPC_NONE, PPC2_BOOKE206),
|
||||
GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001,
|
||||
PPC_NONE, PPC2_BOOKE206),
|
||||
GEN_HANDLER2_E(msgsnd, "msgsnd", 0x1F, 0x0E, 0x06, 0x03ff0001,
|
||||
PPC_NONE, PPC2_PRCNTL),
|
||||
GEN_HANDLER2_E(msgclr, "msgclr", 0x1F, 0x0E, 0x07, 0x03ff0001,
|
||||
PPC_NONE, PPC2_PRCNTL),
|
||||
GEN_HANDLER2_E(msgsync, "msgsync", 0x1F, 0x16, 0x1B, 0x00000000,
|
||||
PPC_NONE, PPC2_PRCNTL),
|
||||
GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE),
|
||||
GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE),
|
||||
GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC),
|
||||
|
@ -6921,15 +6888,10 @@ GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
|
|||
GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
|
||||
GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
|
||||
GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC),
|
||||
GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC),
|
||||
#if defined(TARGET_PPC64)
|
||||
GEN_HANDLER_E(maddhd_maddhdu, 0x04, 0x18, 0xFF, 0x00000000, PPC_NONE,
|
||||
PPC2_ISA300),
|
||||
GEN_HANDLER_E(maddld, 0x04, 0x19, 0xFF, 0x00000000, PPC_NONE, PPC2_ISA300),
|
||||
GEN_HANDLER2_E(msgsndp, "msgsndp", 0x1F, 0x0E, 0x04, 0x03ff0001,
|
||||
PPC_NONE, PPC2_ISA207S),
|
||||
GEN_HANDLER2_E(msgclrp, "msgclrp", 0x1F, 0x0E, 0x05, 0x03ff0001,
|
||||
PPC_NONE, PPC2_ISA207S),
|
||||
#endif
|
||||
|
||||
#undef GEN_INT_ARITH_ADD
|
||||
|
@ -7574,6 +7536,8 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
|||
ctx->hr = (hflags >> HFLAGS_HR) & 1;
|
||||
ctx->mmcr0_pmcc0 = (hflags >> HFLAGS_PMCC0) & 1;
|
||||
ctx->mmcr0_pmcc1 = (hflags >> HFLAGS_PMCC1) & 1;
|
||||
ctx->mmcr0_pmcjce = (hflags >> HFLAGS_PMCJCE) & 1;
|
||||
ctx->pmc_other = (hflags >> HFLAGS_PMC_OTHER) & 1;
|
||||
ctx->pmu_insn_cnt = (hflags >> HFLAGS_INSN_CNT) & 1;
|
||||
|
||||
ctx->singlestep_enabled = 0;
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Power ISA decode for Storage Control instructions
|
||||
*
|
||||
* Copyright (c) 2022 Instituto de Pesquisas Eldorado (eldorado.org.br)
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Processor Control Instructions
|
||||
*/
|
||||
|
||||
static bool trans_MSGCLR(DisasContext *ctx, arg_X_rb *a)
|
||||
{
|
||||
if (!(ctx->insns_flags2 & PPC2_ISA207S)) {
|
||||
/*
|
||||
* Before Power ISA 2.07, processor control instructions were only
|
||||
* implemented in the "Embedded.Processor Control" category.
|
||||
*/
|
||||
REQUIRE_INSNS_FLAGS2(ctx, PRCNTL);
|
||||
}
|
||||
|
||||
REQUIRE_HV(ctx);
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (is_book3s_arch2x(ctx)) {
|
||||
gen_helper_book3s_msgclr(cpu_env, cpu_gpr[a->rb]);
|
||||
} else {
|
||||
gen_helper_msgclr(cpu_env, cpu_gpr[a->rb]);
|
||||
}
|
||||
#else
|
||||
qemu_build_not_reached();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a)
|
||||
{
|
||||
if (!(ctx->insns_flags2 & PPC2_ISA207S)) {
|
||||
/*
|
||||
* Before Power ISA 2.07, processor control instructions were only
|
||||
* implemented in the "Embedded.Processor Control" category.
|
||||
*/
|
||||
REQUIRE_INSNS_FLAGS2(ctx, PRCNTL);
|
||||
}
|
||||
|
||||
REQUIRE_HV(ctx);
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (is_book3s_arch2x(ctx)) {
|
||||
gen_helper_book3s_msgsnd(cpu_gpr[a->rb]);
|
||||
} else {
|
||||
gen_helper_msgsnd(cpu_gpr[a->rb]);
|
||||
}
|
||||
#else
|
||||
qemu_build_not_reached();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_MSGCLRP(DisasContext *ctx, arg_X_rb *a)
|
||||
{
|
||||
REQUIRE_64BIT(ctx);
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA207S);
|
||||
REQUIRE_SV(ctx);
|
||||
#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
|
||||
gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[a->rb]);
|
||||
#else
|
||||
qemu_build_not_reached();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_MSGSNDP(DisasContext *ctx, arg_X_rb *a)
|
||||
{
|
||||
REQUIRE_64BIT(ctx);
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA207S);
|
||||
REQUIRE_SV(ctx);
|
||||
#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64)
|
||||
gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[a->rb]);
|
||||
#else
|
||||
qemu_build_not_reached();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_MSGSYNC(DisasContext *ctx, arg_MSGSYNC *a)
|
||||
{
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
||||
REQUIRE_HV(ctx);
|
||||
|
||||
/* interpreted as no-op */
|
||||
return true;
|
||||
}
|
|
@ -431,21 +431,6 @@ GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12);
|
|||
GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13);
|
||||
GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14);
|
||||
GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15);
|
||||
GEN_VXFORM(vavgub, 1, 16);
|
||||
GEN_VXFORM(vabsdub, 1, 16);
|
||||
GEN_VXFORM_DUAL(vavgub, PPC_ALTIVEC, PPC_NONE, \
|
||||
vabsdub, PPC_NONE, PPC2_ISA300)
|
||||
GEN_VXFORM(vavguh, 1, 17);
|
||||
GEN_VXFORM(vabsduh, 1, 17);
|
||||
GEN_VXFORM_DUAL(vavguh, PPC_ALTIVEC, PPC_NONE, \
|
||||
vabsduh, PPC_NONE, PPC2_ISA300)
|
||||
GEN_VXFORM(vavguw, 1, 18);
|
||||
GEN_VXFORM(vabsduw, 1, 18);
|
||||
GEN_VXFORM_DUAL(vavguw, PPC_ALTIVEC, PPC_NONE, \
|
||||
vabsduw, PPC_NONE, PPC2_ISA300)
|
||||
GEN_VXFORM(vavgsb, 1, 20);
|
||||
GEN_VXFORM(vavgsh, 1, 21);
|
||||
GEN_VXFORM(vavgsw, 1, 22);
|
||||
GEN_VXFORM(vmrghb, 6, 0);
|
||||
GEN_VXFORM(vmrghh, 6, 1);
|
||||
GEN_VXFORM(vmrghw, 6, 2);
|
||||
|
@ -803,8 +788,6 @@ GEN_VXFORM(vsrv, 2, 28);
|
|||
GEN_VXFORM(vslv, 2, 29);
|
||||
GEN_VXFORM(vslo, 6, 16);
|
||||
GEN_VXFORM(vsro, 6, 17);
|
||||
GEN_VXFORM(vaddcuw, 0, 6);
|
||||
GEN_VXFORM(vsubcuw, 0, 22);
|
||||
|
||||
static bool do_vector_gvec3_VX(DisasContext *ctx, arg_VX *a, int vece,
|
||||
void (*gen_gvec)(unsigned, uint32_t, uint32_t,
|
||||
|
@ -1661,9 +1644,71 @@ GEN_VXFORM_NOA_ENV(vrfim, 5, 11);
|
|||
GEN_VXFORM_NOA_ENV(vrfin, 5, 8);
|
||||
GEN_VXFORM_NOA_ENV(vrfip, 5, 10);
|
||||
GEN_VXFORM_NOA_ENV(vrfiz, 5, 9);
|
||||
GEN_VXFORM_NOA(vprtybw, 1, 24);
|
||||
GEN_VXFORM_NOA(vprtybd, 1, 24);
|
||||
GEN_VXFORM_NOA(vprtybq, 1, 24);
|
||||
|
||||
static void gen_vprtyb_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
|
||||
{
|
||||
int i;
|
||||
TCGv_vec tmp = tcg_temp_new_vec_matching(b);
|
||||
/* MO_32 is 2, so 2 iteractions for MO_32 and 3 for MO_64 */
|
||||
for (i = 0; i < vece; i++) {
|
||||
tcg_gen_shri_vec(vece, tmp, b, (4 << (vece - i)));
|
||||
tcg_gen_xor_vec(vece, b, tmp, b);
|
||||
}
|
||||
tcg_gen_and_vec(vece, t, b, tcg_constant_vec_matching(t, vece, 1));
|
||||
tcg_temp_free_vec(tmp);
|
||||
}
|
||||
|
||||
/* vprtybw */
|
||||
static void gen_vprtyb_i32(TCGv_i32 t, TCGv_i32 b)
|
||||
{
|
||||
tcg_gen_ctpop_i32(t, b);
|
||||
tcg_gen_and_i32(t, t, tcg_constant_i32(1));
|
||||
}
|
||||
|
||||
/* vprtybd */
|
||||
static void gen_vprtyb_i64(TCGv_i64 t, TCGv_i64 b)
|
||||
{
|
||||
tcg_gen_ctpop_i64(t, b);
|
||||
tcg_gen_and_i64(t, t, tcg_constant_i64(1));
|
||||
}
|
||||
|
||||
static bool do_vx_vprtyb(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
INDEX_op_shri_vec, 0
|
||||
};
|
||||
|
||||
static const GVecGen2 op[] = {
|
||||
{
|
||||
.fniv = gen_vprtyb_vec,
|
||||
.fni4 = gen_vprtyb_i32,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_32
|
||||
},
|
||||
{
|
||||
.fniv = gen_vprtyb_vec,
|
||||
.fni8 = gen_vprtyb_i64,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_64
|
||||
},
|
||||
{
|
||||
.fno = gen_helper_VPRTYBQ,
|
||||
.vece = MO_128
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_2(avr_full_offset(a->vrt), avr_full_offset(a->vrb),
|
||||
16, 16, &op[vece - MO_32]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(VPRTYBW, do_vx_vprtyb, MO_32)
|
||||
TRANS(VPRTYBD, do_vx_vprtyb, MO_64)
|
||||
TRANS(VPRTYBQ, do_vx_vprtyb, MO_128)
|
||||
|
||||
static void gen_vsplt(DisasContext *ctx, int vece)
|
||||
{
|
||||
|
@ -2521,25 +2566,7 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
|
|||
tcg_temp_free_ptr(rd); \
|
||||
}
|
||||
|
||||
GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16)
|
||||
|
||||
static void gen_vmladduhm(DisasContext *ctx)
|
||||
{
|
||||
TCGv_ptr ra, rb, rc, rd;
|
||||
if (unlikely(!ctx->altivec_enabled)) {
|
||||
gen_exception(ctx, POWERPC_EXCP_VPU);
|
||||
return;
|
||||
}
|
||||
ra = gen_avr_ptr(rA(ctx->opcode));
|
||||
rb = gen_avr_ptr(rB(ctx->opcode));
|
||||
rc = gen_avr_ptr(rC(ctx->opcode));
|
||||
rd = gen_avr_ptr(rD(ctx->opcode));
|
||||
gen_helper_vmladduhm(rd, ra, rb, rc);
|
||||
tcg_temp_free_ptr(ra);
|
||||
tcg_temp_free_ptr(rb);
|
||||
tcg_temp_free_ptr(rc);
|
||||
tcg_temp_free_ptr(rd);
|
||||
}
|
||||
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
|
||||
|
||||
static bool do_va_helper(DisasContext *ctx, arg_VA *a,
|
||||
void (*gen_helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr))
|
||||
|
@ -2569,6 +2596,36 @@ TRANS_FLAGS2(ALTIVEC_207, VSUBECUQ, do_va_helper, gen_helper_VSUBECUQ)
|
|||
TRANS_FLAGS(ALTIVEC, VPERM, do_va_helper, gen_helper_VPERM)
|
||||
TRANS_FLAGS2(ISA300, VPERMR, do_va_helper, gen_helper_VPERMR)
|
||||
|
||||
static void gen_vmladduhm_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b,
|
||||
TCGv_vec c)
|
||||
{
|
||||
tcg_gen_mul_vec(vece, t, a, b);
|
||||
tcg_gen_add_vec(vece, t, t, c);
|
||||
}
|
||||
|
||||
static bool trans_VMLADDUHM(DisasContext *ctx, arg_VA *a)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
INDEX_op_add_vec, INDEX_op_mul_vec, 0
|
||||
};
|
||||
|
||||
static const GVecGen4 op = {
|
||||
.fno = gen_helper_VMLADDUHM,
|
||||
.fniv = gen_vmladduhm_vec,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_16
|
||||
};
|
||||
|
||||
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_4(avr_full_offset(a->vrt), avr_full_offset(a->vra),
|
||||
avr_full_offset(a->vrb), avr_full_offset(a->rc),
|
||||
16, 16, &op);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_VSEL(DisasContext *ctx, arg_VA *a)
|
||||
{
|
||||
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
|
||||
|
@ -2608,14 +2665,26 @@ static bool do_va_env_helper(DisasContext *ctx, arg_VA *a,
|
|||
TRANS_FLAGS(ALTIVEC, VMSUMUHS, do_va_env_helper, gen_helper_VMSUMUHS)
|
||||
TRANS_FLAGS(ALTIVEC, VMSUMSHS, do_va_env_helper, gen_helper_VMSUMSHS)
|
||||
|
||||
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
|
||||
TRANS_FLAGS(ALTIVEC, VMHADDSHS, do_va_env_helper, gen_helper_VMHADDSHS)
|
||||
TRANS_FLAGS(ALTIVEC, VMHRADDSHS, do_va_env_helper, gen_helper_VMHRADDSHS)
|
||||
|
||||
GEN_VXFORM_NOA(vclzb, 1, 28)
|
||||
GEN_VXFORM_NOA(vclzh, 1, 29)
|
||||
GEN_VXFORM_TRANS(vclzw, 1, 30)
|
||||
GEN_VXFORM_TRANS(vclzd, 1, 31)
|
||||
GEN_VXFORM_NOA_2(vnegw, 1, 24, 6)
|
||||
GEN_VXFORM_NOA_2(vnegd, 1, 24, 7)
|
||||
|
||||
static bool do_vneg(DisasContext *ctx, arg_VX_tb *a, unsigned vece)
|
||||
{
|
||||
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_neg(vece, avr_full_offset(a->vrt), avr_full_offset(a->vrb),
|
||||
16, 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(VNEGW, do_vneg, MO_32)
|
||||
TRANS(VNEGD, do_vneg, MO_64)
|
||||
|
||||
static void gen_vexts_i64(TCGv_i64 t, TCGv_i64 b, int64_t s)
|
||||
{
|
||||
|
@ -2834,8 +2903,6 @@ static void gen_xpnd04_2(DisasContext *ctx)
|
|||
}
|
||||
|
||||
|
||||
GEN_VXFORM_DUAL(vsubcuw, PPC_ALTIVEC, PPC_NONE, \
|
||||
xpnd04_1, PPC_NONE, PPC2_ISA300)
|
||||
GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \
|
||||
xpnd04_2, PPC_NONE, PPC2_ISA300)
|
||||
|
||||
|
@ -3097,6 +3164,63 @@ TRANS_FLAGS2(ALTIVEC_207, VPMSUMD, do_vx_helper, gen_helper_VPMSUMD)
|
|||
TRANS_FLAGS2(ALTIVEC_207, VSUBCUQ, do_vx_helper, gen_helper_VSUBCUQ)
|
||||
TRANS_FLAGS2(ALTIVEC_207, VSUBUQM, do_vx_helper, gen_helper_VSUBUQM)
|
||||
|
||||
static void gen_VADDCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
tcg_gen_not_vec(vece, a, a);
|
||||
tcg_gen_cmp_vec(TCG_COND_LTU, vece, t, a, b);
|
||||
tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1));
|
||||
}
|
||||
|
||||
static void gen_VADDCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b)
|
||||
{
|
||||
tcg_gen_not_i32(a, a);
|
||||
tcg_gen_setcond_i32(TCG_COND_LTU, t, a, b);
|
||||
}
|
||||
|
||||
static void gen_VSUBCUW_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
tcg_gen_cmp_vec(TCG_COND_GEU, vece, t, a, b);
|
||||
tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(t, vece, 1));
|
||||
}
|
||||
|
||||
static void gen_VSUBCUW_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b)
|
||||
{
|
||||
tcg_gen_setcond_i32(TCG_COND_GEU, t, a, b);
|
||||
}
|
||||
|
||||
static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
INDEX_op_cmp_vec, 0
|
||||
};
|
||||
|
||||
static const GVecGen3 op[] = {
|
||||
{
|
||||
.fniv = gen_VSUBCUW_vec,
|
||||
.fni4 = gen_VSUBCUW_i32,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_32
|
||||
},
|
||||
{
|
||||
.fniv = gen_VADDCUW_vec,
|
||||
.fni4 = gen_VADDCUW_i32,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_32
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE_INSNS_FLAGS(ctx, ALTIVEC);
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
|
||||
avr_full_offset(a->vrb), 16, 16, &op[add]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(VSUBCUW, do_vx_vaddsubcuw, 0)
|
||||
TRANS(VADDCUW, do_vx_vaddsubcuw, 1)
|
||||
|
||||
static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even,
|
||||
void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64))
|
||||
{
|
||||
|
@ -3234,6 +3358,146 @@ TRANS(VMULHSD, do_vx_mulh, true , do_vx_vmulhd_i64)
|
|||
TRANS(VMULHUW, do_vx_mulh, false, do_vx_vmulhw_i64)
|
||||
TRANS(VMULHUD, do_vx_mulh, false, do_vx_vmulhd_i64)
|
||||
|
||||
static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b,
|
||||
void (*gen_shr_vec)(unsigned, TCGv_vec, TCGv_vec, int64_t))
|
||||
{
|
||||
TCGv_vec tmp = tcg_temp_new_vec_matching(t);
|
||||
tcg_gen_or_vec(vece, tmp, a, b);
|
||||
tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1));
|
||||
gen_shr_vec(vece, a, a, 1);
|
||||
gen_shr_vec(vece, b, b, 1);
|
||||
tcg_gen_add_vec(vece, t, a, b);
|
||||
tcg_gen_add_vec(vece, t, t, tmp);
|
||||
tcg_temp_free_vec(tmp);
|
||||
}
|
||||
|
||||
QEMU_FLATTEN
|
||||
static void gen_vavgu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
do_vavg(vece, t, a, b, tcg_gen_shri_vec);
|
||||
}
|
||||
|
||||
QEMU_FLATTEN
|
||||
static void gen_vavgs(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
do_vavg(vece, t, a, b, tcg_gen_sari_vec);
|
||||
}
|
||||
|
||||
static bool do_vx_vavg(DisasContext *ctx, arg_VX *a, int sign, int vece)
|
||||
{
|
||||
static const TCGOpcode vecop_list_s[] = {
|
||||
INDEX_op_add_vec, INDEX_op_sari_vec, 0
|
||||
};
|
||||
static const TCGOpcode vecop_list_u[] = {
|
||||
INDEX_op_add_vec, INDEX_op_shri_vec, 0
|
||||
};
|
||||
|
||||
static const GVecGen3 op[2][3] = {
|
||||
{
|
||||
{
|
||||
.fniv = gen_vavgu,
|
||||
.fno = gen_helper_VAVGUB,
|
||||
.opt_opc = vecop_list_u,
|
||||
.vece = MO_8
|
||||
},
|
||||
{
|
||||
.fniv = gen_vavgu,
|
||||
.fno = gen_helper_VAVGUH,
|
||||
.opt_opc = vecop_list_u,
|
||||
.vece = MO_16
|
||||
},
|
||||
{
|
||||
.fniv = gen_vavgu,
|
||||
.fno = gen_helper_VAVGUW,
|
||||
.opt_opc = vecop_list_u,
|
||||
.vece = MO_32
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
.fniv = gen_vavgs,
|
||||
.fno = gen_helper_VAVGSB,
|
||||
.opt_opc = vecop_list_s,
|
||||
.vece = MO_8
|
||||
},
|
||||
{
|
||||
.fniv = gen_vavgs,
|
||||
.fno = gen_helper_VAVGSH,
|
||||
.opt_opc = vecop_list_s,
|
||||
.vece = MO_16
|
||||
},
|
||||
{
|
||||
.fniv = gen_vavgs,
|
||||
.fno = gen_helper_VAVGSW,
|
||||
.opt_opc = vecop_list_s,
|
||||
.vece = MO_32
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
|
||||
avr_full_offset(a->vrb), 16, 16, &op[sign][vece]);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TRANS_FLAGS(ALTIVEC, VAVGSB, do_vx_vavg, 1, MO_8)
|
||||
TRANS_FLAGS(ALTIVEC, VAVGSH, do_vx_vavg, 1, MO_16)
|
||||
TRANS_FLAGS(ALTIVEC, VAVGSW, do_vx_vavg, 1, MO_32)
|
||||
TRANS_FLAGS(ALTIVEC, VAVGUB, do_vx_vavg, 0, MO_8)
|
||||
TRANS_FLAGS(ALTIVEC, VAVGUH, do_vx_vavg, 0, MO_16)
|
||||
TRANS_FLAGS(ALTIVEC, VAVGUW, do_vx_vavg, 0, MO_32)
|
||||
|
||||
static void gen_vabsdu(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
tcg_gen_umax_vec(vece, t, a, b);
|
||||
tcg_gen_umin_vec(vece, a, a, b);
|
||||
tcg_gen_sub_vec(vece, t, t, a);
|
||||
}
|
||||
|
||||
static bool do_vabsdu(DisasContext *ctx, arg_VX *a, const int vece)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0
|
||||
};
|
||||
|
||||
static const GVecGen3 op[] = {
|
||||
{
|
||||
.fniv = gen_vabsdu,
|
||||
.fno = gen_helper_VABSDUB,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_8
|
||||
},
|
||||
{
|
||||
.fniv = gen_vabsdu,
|
||||
.fno = gen_helper_VABSDUH,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_16
|
||||
},
|
||||
{
|
||||
.fniv = gen_vabsdu,
|
||||
.fno = gen_helper_VABSDUW,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_32
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE_VECTOR(ctx);
|
||||
|
||||
tcg_gen_gvec_3(avr_full_offset(a->vrt), avr_full_offset(a->vra),
|
||||
avr_full_offset(a->vrb), 16, 16, &op[vece]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS_FLAGS2(ISA300, VABSDUB, do_vabsdu, MO_8)
|
||||
TRANS_FLAGS2(ISA300, VABSDUH, do_vabsdu, MO_16)
|
||||
TRANS_FLAGS2(ISA300, VABSDUW, do_vabsdu, MO_32)
|
||||
|
||||
static bool do_vdiv_vmod(DisasContext *ctx, arg_VX *a, const int vece,
|
||||
void (*func_32)(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b),
|
||||
void (*func_64)(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b))
|
||||
|
|
|
@ -83,12 +83,6 @@ GEN_VXFORM(vminsb, 1, 12),
|
|||
GEN_VXFORM(vminsh, 1, 13),
|
||||
GEN_VXFORM(vminsw, 1, 14),
|
||||
GEN_VXFORM_207(vminsd, 1, 15),
|
||||
GEN_VXFORM_DUAL(vavgub, vabsdub, 1, 16, PPC_ALTIVEC, PPC_NONE),
|
||||
GEN_VXFORM_DUAL(vavguh, vabsduh, 1, 17, PPC_ALTIVEC, PPC_NONE),
|
||||
GEN_VXFORM_DUAL(vavguw, vabsduw, 1, 18, PPC_ALTIVEC, PPC_NONE),
|
||||
GEN_VXFORM(vavgsb, 1, 20),
|
||||
GEN_VXFORM(vavgsh, 1, 21),
|
||||
GEN_VXFORM(vavgsw, 1, 22),
|
||||
GEN_VXFORM(vmrghb, 6, 0),
|
||||
GEN_VXFORM(vmrghh, 6, 1),
|
||||
GEN_VXFORM(vmrghw, 6, 2),
|
||||
|
@ -106,12 +100,8 @@ GEN_VXFORM_300(vsrv, 2, 28),
|
|||
GEN_VXFORM_300(vslv, 2, 29),
|
||||
GEN_VXFORM(vslo, 6, 16),
|
||||
GEN_VXFORM(vsro, 6, 17),
|
||||
GEN_VXFORM(vaddcuw, 0, 6),
|
||||
GEN_HANDLER_E_2(vprtybw, 0x4, 0x1, 0x18, 8, 0, PPC_NONE, PPC2_ISA300),
|
||||
GEN_HANDLER_E_2(vprtybd, 0x4, 0x1, 0x18, 9, 0, PPC_NONE, PPC2_ISA300),
|
||||
GEN_HANDLER_E_2(vprtybq, 0x4, 0x1, 0x18, 10, 0, PPC_NONE, PPC2_ISA300),
|
||||
|
||||
GEN_VXFORM_DUAL(vsubcuw, xpnd04_1, 0, 22, PPC_ALTIVEC, PPC_NONE),
|
||||
GEN_VXFORM(xpnd04_1, 0, 22),
|
||||
GEN_VXFORM_300(bcdsr, 0, 23),
|
||||
GEN_VXFORM_300(bcdsr, 0, 31),
|
||||
GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE),
|
||||
|
@ -182,8 +172,6 @@ GEN_VXFORM_300_EXT(vextractd, 6, 11, 0x100000),
|
|||
GEN_VXFORM(vspltisb, 6, 12),
|
||||
GEN_VXFORM(vspltish, 6, 13),
|
||||
GEN_VXFORM(vspltisw, 6, 14),
|
||||
GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06),
|
||||
GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07),
|
||||
GEN_VXFORM_300_EO(vctzb, 0x01, 0x18, 0x1C),
|
||||
GEN_VXFORM_300_EO(vctzh, 0x01, 0x18, 0x1D),
|
||||
GEN_VXFORM_300_EO(vctzw, 0x01, 0x18, 0x1E),
|
||||
|
@ -219,7 +207,6 @@ GEN_VXFORM_UIMM(vctsxs, 5, 15),
|
|||
|
||||
#define GEN_VAFORM_PAIRED(name0, name1, opc2) \
|
||||
GEN_HANDLER(name0##_##name1, 0x04, opc2, 0xFF, 0x00000000, PPC_ALTIVEC)
|
||||
GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16),
|
||||
GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23),
|
||||
|
||||
GEN_VXFORM_DUAL(vclzb, vpopcntb, 1, 28, PPC_NONE, PPC2_ALTIVEC_207),
|
||||
|
|
|
@ -630,6 +630,10 @@ static void gen_mtvsrws(DisasContext *ctx)
|
|||
#define OP_CPSGN 4
|
||||
#define SGN_MASK_DP 0x8000000000000000ull
|
||||
#define SGN_MASK_SP 0x8000000080000000ull
|
||||
#define EXP_MASK_DP 0x7FF0000000000000ull
|
||||
#define EXP_MASK_SP 0x7F8000007F800000ull
|
||||
#define FRC_MASK_DP (~(SGN_MASK_DP | EXP_MASK_DP))
|
||||
#define FRC_MASK_SP (~(SGN_MASK_SP | EXP_MASK_SP))
|
||||
|
||||
#define VSX_SCALAR_MOVE(name, op, sgn_mask) \
|
||||
static void glue(gen_, name)(DisasContext *ctx) \
|
||||
|
@ -729,67 +733,125 @@ VSX_SCALAR_MOVE_QP(xsnabsqp, OP_NABS, SGN_MASK_DP)
|
|||
VSX_SCALAR_MOVE_QP(xsnegqp, OP_NEG, SGN_MASK_DP)
|
||||
VSX_SCALAR_MOVE_QP(xscpsgnqp, OP_CPSGN, SGN_MASK_DP)
|
||||
|
||||
#define VSX_VECTOR_MOVE(name, op, sgn_mask) \
|
||||
static void glue(gen_, name)(DisasContext *ctx) \
|
||||
{ \
|
||||
TCGv_i64 xbh, xbl, sgm; \
|
||||
if (unlikely(!ctx->vsx_enabled)) { \
|
||||
gen_exception(ctx, POWERPC_EXCP_VSXU); \
|
||||
return; \
|
||||
} \
|
||||
xbh = tcg_temp_new_i64(); \
|
||||
xbl = tcg_temp_new_i64(); \
|
||||
sgm = tcg_temp_new_i64(); \
|
||||
get_cpu_vsr(xbh, xB(ctx->opcode), true); \
|
||||
get_cpu_vsr(xbl, xB(ctx->opcode), false); \
|
||||
tcg_gen_movi_i64(sgm, sgn_mask); \
|
||||
switch (op) { \
|
||||
case OP_ABS: { \
|
||||
tcg_gen_andc_i64(xbh, xbh, sgm); \
|
||||
tcg_gen_andc_i64(xbl, xbl, sgm); \
|
||||
break; \
|
||||
} \
|
||||
case OP_NABS: { \
|
||||
tcg_gen_or_i64(xbh, xbh, sgm); \
|
||||
tcg_gen_or_i64(xbl, xbl, sgm); \
|
||||
break; \
|
||||
} \
|
||||
case OP_NEG: { \
|
||||
tcg_gen_xor_i64(xbh, xbh, sgm); \
|
||||
tcg_gen_xor_i64(xbl, xbl, sgm); \
|
||||
break; \
|
||||
} \
|
||||
case OP_CPSGN: { \
|
||||
TCGv_i64 xah = tcg_temp_new_i64(); \
|
||||
TCGv_i64 xal = tcg_temp_new_i64(); \
|
||||
get_cpu_vsr(xah, xA(ctx->opcode), true); \
|
||||
get_cpu_vsr(xal, xA(ctx->opcode), false); \
|
||||
tcg_gen_and_i64(xah, xah, sgm); \
|
||||
tcg_gen_and_i64(xal, xal, sgm); \
|
||||
tcg_gen_andc_i64(xbh, xbh, sgm); \
|
||||
tcg_gen_andc_i64(xbl, xbl, sgm); \
|
||||
tcg_gen_or_i64(xbh, xbh, xah); \
|
||||
tcg_gen_or_i64(xbl, xbl, xal); \
|
||||
tcg_temp_free_i64(xah); \
|
||||
tcg_temp_free_i64(xal); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
set_cpu_vsr(xT(ctx->opcode), xbh, true); \
|
||||
set_cpu_vsr(xT(ctx->opcode), xbl, false); \
|
||||
tcg_temp_free_i64(xbh); \
|
||||
tcg_temp_free_i64(xbl); \
|
||||
tcg_temp_free_i64(sgm); \
|
||||
#define TCG_OP_IMM_i64(FUNC, OP, IMM) \
|
||||
static void FUNC(TCGv_i64 t, TCGv_i64 b) \
|
||||
{ \
|
||||
OP(t, b, IMM); \
|
||||
}
|
||||
|
||||
VSX_VECTOR_MOVE(xvabsdp, OP_ABS, SGN_MASK_DP)
|
||||
VSX_VECTOR_MOVE(xvnabsdp, OP_NABS, SGN_MASK_DP)
|
||||
VSX_VECTOR_MOVE(xvnegdp, OP_NEG, SGN_MASK_DP)
|
||||
VSX_VECTOR_MOVE(xvcpsgndp, OP_CPSGN, SGN_MASK_DP)
|
||||
VSX_VECTOR_MOVE(xvabssp, OP_ABS, SGN_MASK_SP)
|
||||
VSX_VECTOR_MOVE(xvnabssp, OP_NABS, SGN_MASK_SP)
|
||||
VSX_VECTOR_MOVE(xvnegsp, OP_NEG, SGN_MASK_SP)
|
||||
VSX_VECTOR_MOVE(xvcpsgnsp, OP_CPSGN, SGN_MASK_SP)
|
||||
TCG_OP_IMM_i64(do_xvabssp_i64, tcg_gen_andi_i64, ~SGN_MASK_SP)
|
||||
TCG_OP_IMM_i64(do_xvnabssp_i64, tcg_gen_ori_i64, SGN_MASK_SP)
|
||||
TCG_OP_IMM_i64(do_xvnegsp_i64, tcg_gen_xori_i64, SGN_MASK_SP)
|
||||
TCG_OP_IMM_i64(do_xvabsdp_i64, tcg_gen_andi_i64, ~SGN_MASK_DP)
|
||||
TCG_OP_IMM_i64(do_xvnabsdp_i64, tcg_gen_ori_i64, SGN_MASK_DP)
|
||||
TCG_OP_IMM_i64(do_xvnegdp_i64, tcg_gen_xori_i64, SGN_MASK_DP)
|
||||
#undef TCG_OP_IMM_i64
|
||||
|
||||
static void xv_msb_op1(unsigned vece, TCGv_vec t, TCGv_vec b,
|
||||
void (*tcg_gen_op_vec)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec))
|
||||
{
|
||||
uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_op_vec(vece, t, b, tcg_constant_vec_matching(t, vece, msb));
|
||||
}
|
||||
|
||||
static void do_xvabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
|
||||
{
|
||||
xv_msb_op1(vece, t, b, tcg_gen_andc_vec);
|
||||
}
|
||||
|
||||
static void do_xvnabs_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
|
||||
{
|
||||
xv_msb_op1(vece, t, b, tcg_gen_or_vec);
|
||||
}
|
||||
|
||||
static void do_xvneg_vec(unsigned vece, TCGv_vec t, TCGv_vec b)
|
||||
{
|
||||
xv_msb_op1(vece, t, b, tcg_gen_xor_vec);
|
||||
}
|
||||
|
||||
static bool do_vsx_msb_op(DisasContext *ctx, arg_XX2 *a, unsigned vece,
|
||||
void (*vec)(unsigned, TCGv_vec, TCGv_vec),
|
||||
void (*i64)(TCGv_i64, TCGv_i64))
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
0
|
||||
};
|
||||
|
||||
const GVecGen2 op = {
|
||||
.fni8 = i64,
|
||||
.fniv = vec,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = vece
|
||||
};
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, VSX);
|
||||
REQUIRE_VSX(ctx);
|
||||
|
||||
tcg_gen_gvec_2(vsr_full_offset(a->xt), vsr_full_offset(a->xb),
|
||||
16, 16, &op);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(XVABSDP, do_vsx_msb_op, MO_64, do_xvabs_vec, do_xvabsdp_i64)
|
||||
TRANS(XVNABSDP, do_vsx_msb_op, MO_64, do_xvnabs_vec, do_xvnabsdp_i64)
|
||||
TRANS(XVNEGDP, do_vsx_msb_op, MO_64, do_xvneg_vec, do_xvnegdp_i64)
|
||||
TRANS(XVABSSP, do_vsx_msb_op, MO_32, do_xvabs_vec, do_xvabssp_i64)
|
||||
TRANS(XVNABSSP, do_vsx_msb_op, MO_32, do_xvnabs_vec, do_xvnabssp_i64)
|
||||
TRANS(XVNEGSP, do_vsx_msb_op, MO_32, do_xvneg_vec, do_xvnegsp_i64)
|
||||
|
||||
static void do_xvcpsgndp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)
|
||||
{
|
||||
tcg_gen_andi_i64(a, a, SGN_MASK_DP);
|
||||
tcg_gen_andi_i64(b, b, ~SGN_MASK_DP);
|
||||
tcg_gen_or_i64(t, a, b);
|
||||
}
|
||||
|
||||
static void do_xvcpsgnsp_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b)
|
||||
{
|
||||
tcg_gen_andi_i64(a, a, SGN_MASK_SP);
|
||||
tcg_gen_andi_i64(b, b, ~SGN_MASK_SP);
|
||||
tcg_gen_or_i64(t, a, b);
|
||||
}
|
||||
|
||||
static void do_xvcpsgn_vec(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b)
|
||||
{
|
||||
uint64_t msb = (vece == MO_32) ? SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_bitsel_vec(vece, t, tcg_constant_vec_matching(t, vece, msb), a, b);
|
||||
}
|
||||
|
||||
static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
0
|
||||
};
|
||||
|
||||
static const GVecGen3 op[] = {
|
||||
{
|
||||
.fni8 = do_xvcpsgnsp_i64,
|
||||
.fniv = do_xvcpsgn_vec,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_32
|
||||
},
|
||||
{
|
||||
.fni8 = do_xvcpsgndp_i64,
|
||||
.fniv = do_xvcpsgn_vec,
|
||||
.opt_opc = vecop_list,
|
||||
.vece = MO_64
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE_INSNS_FLAGS2(ctx, VSX);
|
||||
REQUIRE_VSX(ctx);
|
||||
|
||||
tcg_gen_gvec_3(vsr_full_offset(a->xt), vsr_full_offset(a->xa),
|
||||
vsr_full_offset(a->xb), 16, 16, &op[vece - MO_32]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS(XVCPSGNSP, do_xvcpsgn, MO_32)
|
||||
TRANS(XVCPSGNDP, do_xvcpsgn, MO_64)
|
||||
|
||||
#define VSX_CMP(name, op1, op2, inval, type) \
|
||||
static void gen_##name(DisasContext *ctx) \
|
||||
|
@ -1052,6 +1114,192 @@ GEN_VSX_HELPER_X2(xscvhpdp, 0x16, 0x15, 0x10, PPC2_ISA300)
|
|||
GEN_VSX_HELPER_R2(xscvsdqp, 0x04, 0x1A, 0x0A, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_X2(xscvspdp, 0x12, 0x14, 0, PPC2_VSX)
|
||||
|
||||
/* test if +Inf */
|
||||
static void gen_is_pos_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, exp_msk));
|
||||
}
|
||||
|
||||
/* test if -Inf */
|
||||
static void gen_is_neg_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, sgn_msk | exp_msk));
|
||||
}
|
||||
|
||||
/* test if +Inf or -Inf */
|
||||
static void gen_is_any_inf(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, exp_msk));
|
||||
}
|
||||
|
||||
/* test if +0 */
|
||||
static void gen_is_pos_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, 0));
|
||||
}
|
||||
|
||||
/* test if -0 */
|
||||
static void gen_is_neg_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, sgn_msk));
|
||||
}
|
||||
|
||||
/* test if +0 or -0 */
|
||||
static void gen_is_any_zero(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, 0));
|
||||
}
|
||||
|
||||
/* test if +Denormal */
|
||||
static void gen_is_pos_denormal(unsigned vece, TCGv_vec t,
|
||||
TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
|
||||
tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, frc_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b,
|
||||
tcg_constant_vec_matching(t, vece, 0));
|
||||
tcg_gen_and_vec(vece, t, t, b);
|
||||
}
|
||||
|
||||
/* test if -Denormal */
|
||||
static void gen_is_neg_denormal(unsigned vece, TCGv_vec t,
|
||||
TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
|
||||
tcg_gen_cmp_vec(TCG_COND_LEU, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, sgn_msk | frc_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_GTU, vece, b, b,
|
||||
tcg_constant_vec_matching(t, vece, sgn_msk));
|
||||
tcg_gen_and_vec(vece, t, t, b);
|
||||
}
|
||||
|
||||
/* test if +Denormal or -Denormal */
|
||||
static void gen_is_any_denormal(unsigned vece, TCGv_vec t,
|
||||
TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
uint64_t frc_msk = (vece == MO_32) ? (uint32_t)FRC_MASK_SP : FRC_MASK_DP;
|
||||
tcg_gen_andc_vec(vece, b, b, tcg_constant_vec_matching(t, vece, sgn_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_LE, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, frc_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_NE, vece, b, b,
|
||||
tcg_constant_vec_matching(t, vece, 0));
|
||||
tcg_gen_and_vec(vece, t, t, b);
|
||||
}
|
||||
|
||||
/* test if NaN */
|
||||
static void gen_is_nan(unsigned vece, TCGv_vec t, TCGv_vec b, int64_t v)
|
||||
{
|
||||
uint64_t exp_msk = (vece == MO_32) ? (uint32_t)EXP_MASK_SP : EXP_MASK_DP;
|
||||
uint64_t sgn_msk = (vece == MO_32) ? (uint32_t)SGN_MASK_SP : SGN_MASK_DP;
|
||||
tcg_gen_and_vec(vece, b, b, tcg_constant_vec_matching(t, vece, ~sgn_msk));
|
||||
tcg_gen_cmp_vec(TCG_COND_GT, vece, t, b,
|
||||
tcg_constant_vec_matching(t, vece, exp_msk));
|
||||
}
|
||||
|
||||
static bool do_xvtstdc(DisasContext *ctx, arg_XX2_uim *a, unsigned vece)
|
||||
{
|
||||
static const TCGOpcode vecop_list[] = {
|
||||
INDEX_op_cmp_vec, 0
|
||||
};
|
||||
|
||||
GVecGen2i op = {
|
||||
.fnoi = (vece == MO_32) ? gen_helper_XVTSTDCSP : gen_helper_XVTSTDCDP,
|
||||
.vece = vece,
|
||||
.opt_opc = vecop_list
|
||||
};
|
||||
|
||||
REQUIRE_VSX(ctx);
|
||||
|
||||
switch (a->uim) {
|
||||
case 0:
|
||||
set_cpu_vsr(a->xt, tcg_constant_i64(0), true);
|
||||
set_cpu_vsr(a->xt, tcg_constant_i64(0), false);
|
||||
return true;
|
||||
case ((1 << 0) | (1 << 1)):
|
||||
/* test if +Denormal or -Denormal */
|
||||
op.fniv = gen_is_any_denormal;
|
||||
break;
|
||||
case (1 << 0):
|
||||
/* test if -Denormal */
|
||||
op.fniv = gen_is_neg_denormal;
|
||||
break;
|
||||
case (1 << 1):
|
||||
/* test if +Denormal */
|
||||
op.fniv = gen_is_pos_denormal;
|
||||
break;
|
||||
case ((1 << 2) | (1 << 3)):
|
||||
/* test if +0 or -0 */
|
||||
op.fniv = gen_is_any_zero;
|
||||
break;
|
||||
case (1 << 2):
|
||||
/* test if -0 */
|
||||
op.fniv = gen_is_neg_zero;
|
||||
break;
|
||||
case (1 << 3):
|
||||
/* test if +0 */
|
||||
op.fniv = gen_is_pos_zero;
|
||||
break;
|
||||
case ((1 << 4) | (1 << 5)):
|
||||
/* test if +Inf or -Inf */
|
||||
op.fniv = gen_is_any_inf;
|
||||
break;
|
||||
case (1 << 4):
|
||||
/* test if -Inf */
|
||||
op.fniv = gen_is_neg_inf;
|
||||
break;
|
||||
case (1 << 5):
|
||||
/* test if +Inf */
|
||||
op.fniv = gen_is_pos_inf;
|
||||
break;
|
||||
case (1 << 6):
|
||||
/* test if NaN */
|
||||
op.fniv = gen_is_nan;
|
||||
break;
|
||||
}
|
||||
tcg_gen_gvec_2i(vsr_full_offset(a->xt), vsr_full_offset(a->xb),
|
||||
16, 16, a->uim, &op);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS_FLAGS2(VSX, XVTSTDCSP, do_xvtstdc, MO_32)
|
||||
TRANS_FLAGS2(VSX, XVTSTDCDP, do_xvtstdc, MO_64)
|
||||
|
||||
static bool do_XX2_bf_uim(DisasContext *ctx, arg_XX2_bf_uim *a, bool vsr,
|
||||
void (*gen_helper)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_ptr))
|
||||
{
|
||||
TCGv_ptr xb;
|
||||
|
||||
REQUIRE_VSX(ctx);
|
||||
xb = vsr ? gen_vsr_ptr(a->xb) : gen_avr_ptr(a->xb);
|
||||
gen_helper(cpu_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb);
|
||||
tcg_temp_free_ptr(xb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TRANS_FLAGS2(ISA300, XSTSTDCSP, do_XX2_bf_uim, true, gen_helper_XSTSTDCSP)
|
||||
TRANS_FLAGS2(ISA300, XSTSTDCDP, do_XX2_bf_uim, true, gen_helper_XSTSTDCDP)
|
||||
TRANS_FLAGS2(ISA300, XSTSTDCQP, do_XX2_bf_uim, false, gen_helper_XSTSTDCQP)
|
||||
|
||||
bool trans_XSCVSPDPN(DisasContext *ctx, arg_XX2 *a)
|
||||
{
|
||||
TCGv_i64 tmp;
|
||||
|
@ -1098,9 +1346,6 @@ GEN_VSX_HELPER_X2(xssqrtsp, 0x16, 0x00, 0, PPC2_VSX207)
|
|||
GEN_VSX_HELPER_X2(xsrsqrtesp, 0x14, 0x00, 0, PPC2_VSX207)
|
||||
GEN_VSX_HELPER_X2(xscvsxdsp, 0x10, 0x13, 0, PPC2_VSX207)
|
||||
GEN_VSX_HELPER_X2(xscvuxdsp, 0x10, 0x12, 0, PPC2_VSX207)
|
||||
GEN_VSX_HELPER_X1(xststdcsp, 0x14, 0x12, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_2(xststdcdp, 0x14, 0x16, 0, PPC2_ISA300)
|
||||
GEN_VSX_HELPER_2(xststdcqp, 0x04, 0x16, 0, PPC2_ISA300)
|
||||
|
||||
GEN_VSX_HELPER_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX)
|
||||
|
@ -1155,8 +1400,6 @@ GEN_VSX_HELPER_X2(xvrspic, 0x16, 0x0A, 0, PPC2_VSX)
|
|||
GEN_VSX_HELPER_X2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_X2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_X2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_2(xvtstdcsp, 0x14, 0x1A, 0, PPC2_VSX)
|
||||
GEN_VSX_HELPER_2(xvtstdcdp, 0x14, 0x1E, 0, PPC2_VSX)
|
||||
|
||||
static bool trans_XXPERM(DisasContext *ctx, arg_XX3 *a)
|
||||
{
|
||||
|
|
|
@ -147,33 +147,12 @@ GEN_HANDLER_E(xsiexpdp, 0x3C, 0x16, 0x1C, 0, PPC_NONE, PPC2_ISA300),
|
|||
GEN_VSX_XFORM_300(xsiexpqp, 0x4, 0x1B, 0x00000001),
|
||||
#endif
|
||||
|
||||
GEN_XX2FORM(xststdcdp, 0x14, 0x16, PPC2_ISA300),
|
||||
GEN_XX2FORM(xststdcsp, 0x14, 0x12, PPC2_ISA300),
|
||||
GEN_VSX_XFORM_300(xststdcqp, 0x04, 0x16, 0x00000001),
|
||||
|
||||
GEN_XX3FORM(xviexpsp, 0x00, 0x1B, PPC2_ISA300),
|
||||
GEN_XX3FORM(xviexpdp, 0x00, 0x1F, PPC2_ISA300),
|
||||
GEN_XX2FORM_EO(xvxexpdp, 0x16, 0x1D, 0x00, PPC2_ISA300),
|
||||
GEN_XX2FORM_EO(xvxsigdp, 0x16, 0x1D, 0x01, PPC2_ISA300),
|
||||
GEN_XX2FORM_EO(xvxexpsp, 0x16, 0x1D, 0x08, PPC2_ISA300),
|
||||
|
||||
/* DCMX = bit[25] << 6 | bit[29] << 5 | bit[11:15] */
|
||||
#define GEN_XX2FORM_DCMX(name, opc2, opc3, fl2) \
|
||||
GEN_XX3FORM(name, opc2, opc3 | 0, fl2), \
|
||||
GEN_XX3FORM(name, opc2, opc3 | 1, fl2)
|
||||
|
||||
GEN_XX2FORM_DCMX(xvtstdcdp, 0x14, 0x1E, PPC2_ISA300),
|
||||
GEN_XX2FORM_DCMX(xvtstdcsp, 0x14, 0x1A, PPC2_ISA300),
|
||||
|
||||
GEN_XX2FORM(xvabsdp, 0x12, 0x1D, PPC2_VSX),
|
||||
GEN_XX2FORM(xvnabsdp, 0x12, 0x1E, PPC2_VSX),
|
||||
GEN_XX2FORM(xvnegdp, 0x12, 0x1F, PPC2_VSX),
|
||||
GEN_XX3FORM(xvcpsgndp, 0x00, 0x1E, PPC2_VSX),
|
||||
GEN_XX2FORM(xvabssp, 0x12, 0x19, PPC2_VSX),
|
||||
GEN_XX2FORM(xvnabssp, 0x12, 0x1A, PPC2_VSX),
|
||||
GEN_XX2FORM(xvnegsp, 0x12, 0x1B, PPC2_VSX),
|
||||
GEN_XX3FORM(xvcpsgnsp, 0x00, 0x1A, PPC2_VSX),
|
||||
|
||||
GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX),
|
||||
GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0),
|
||||
GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX),
|
||||
|
|
Loading…
Reference in New Issue