mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-ppc-for-10.0-1-20250311' of https://gitlab.com/npiggin/qemu into staging
* Next round of XIVE patches... * tag 'pull-ppc-for-10.0-1-20250311' of https://gitlab.com/npiggin/qemu: (72 commits) docs/system/ppc/amigang.rst: Update for NVRAM emulation ppc/amigaone: Add #defines for memory map constants ppc/amigaone: Add kernel and initrd support ppc/amigaone: Add default environment ppc/amigaone: Implement NVRAM emulation ppc/amigaone: Simplify replacement dummy_fw spapr: Generate random HASHPKEYR for spapr machines target/ppc: Avoid warning message for zero process table entries target/ppc: Wire up BookE ATB registers for e500 family target/ppc: fix timebase register reset state spapr: nested: Add support for reporting Hostwide state counter ppc: spapr: Enable 2nd DAWR on Power10 pSeries machine ppc: Enable 2nd DAWR support on Power10 PowerNV machine hw/ppc/epapr: Do not swap ePAPR magic value hw/ppc/spapr: Convert DIRTY_HPTE() macro as hpte_set_dirty() method hw/ppc/spapr: Convert CLEAN_HPTE() macro as hpte_set_clean() method hw/ppc/spapr: Convert HPTE_DIRTY() macro as hpte_is_dirty() method hw/ppc/spapr: Convert HPTE_VALID() macro as hpte_is_valid() method hw/ppc/spapr: Convert HPTE() macro as hpte_get_ptr() method target/ppc: Restrict ATTN / SCV / PMINSN helpers to TCG ... [Fix __packed macro redefinition on FreeBSD 14 hosts: ../hw/ppc/pnv_occ.c:397:9: error: '__packed' macro redefined [-Werror,-Wmacro-redefined] 397 | #define __packed QEMU_PACKED | ^ /usr/include/sys/cdefs.h:217:9: note: previous definition is here 217 | #define __packed __attribute__((__packed__)) | ^ --Stefan] Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
+1
-6
@@ -1407,12 +1407,6 @@ F: hw/openrisc/openrisc_sim.c
|
||||
|
||||
PowerPC Machines
|
||||
----------------
|
||||
405 (ref405ep)
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/ppc/ppc405*
|
||||
F: tests/functional/test_ppc_405.py
|
||||
|
||||
Bamboo
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Orphan
|
||||
@@ -1543,6 +1537,7 @@ F: include/hw/ppc/pnv*
|
||||
F: include/hw/pci-host/pnv*
|
||||
F: include/hw/ssi/pnv_spi*
|
||||
F: pc-bios/skiboot.lid
|
||||
F: pc-bios/pnv-pnor.bin
|
||||
F: tests/qtest/pnv*
|
||||
F: tests/functional/test_ppc64_powernv.py
|
||||
|
||||
|
||||
@@ -266,6 +266,15 @@ in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and
|
||||
but for consistency these will get removed in a future release, too.
|
||||
Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead.
|
||||
|
||||
PPC 405 CPUs (since 10.0)
|
||||
'''''''''''''''''''''''''
|
||||
|
||||
The PPC 405 CPU has no known users and the ``ref405ep`` machine was
|
||||
removed in QEMU 10.0. Since the IBM POWER [8-11] processors uses an
|
||||
embedded 405 for power management (OCC) and other internal tasks, it
|
||||
is theoretically possible to use QEMU to model them. Let's keep the
|
||||
CPU implementation for a while before removing all support.
|
||||
|
||||
System emulator machines
|
||||
------------------------
|
||||
|
||||
@@ -277,14 +286,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name
|
||||
better reflects the way this property affects all random data within
|
||||
the device tree blob, not just the ``kaslr-seed`` node.
|
||||
|
||||
PPC 405 ``ref405ep`` machine (since 9.1)
|
||||
''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware
|
||||
images are not available, OpenWRT dropped support in 2019, U-Boot in
|
||||
2017, Linux also is dropping support in 2024. It is time to let go of
|
||||
this ancient hardware and focus on newer CPUs and platforms.
|
||||
|
||||
Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
||||
@@ -1064,6 +1064,13 @@ for all machine types using the PXA2xx and OMAP2 SoCs. We are also
|
||||
dropping the ``cheetah`` OMAP1 board, because we don't have any
|
||||
test images for it and don't know of anybody who does.
|
||||
|
||||
ppc ``ref405ep`` machine (removed in 10.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This machine was removed because PPC 405 CPU have no known users,
|
||||
firmware images are not available, OpenWRT dropped support in 2019,
|
||||
U-Boot in 2017, and Linux in 2024.
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ Emulated devices
|
||||
* VIA VT82C686B south bridge
|
||||
* PCI VGA compatible card (guests may need other card instead)
|
||||
* PS/2 keyboard and mouse
|
||||
* 4 KiB NVRAM (use ``-drive if=mtd,format=raw,file=nvram.bin`` to keep contents persistent)
|
||||
|
||||
Firmware
|
||||
--------
|
||||
@@ -54,14 +55,14 @@ To boot the system run:
|
||||
-cdrom "A1 Linux Net Installer.iso" \
|
||||
-device ati-vga,model=rv100,romfile=VGABIOS-lgpl-latest.bin
|
||||
|
||||
From the firmware menu that appears select ``Boot sequence`` →
|
||||
``Amiga Multiboot Options`` and set ``Boot device 1`` to
|
||||
``Onboard VIA IDE CDROM``. Then hit escape until the main screen appears again,
|
||||
hit escape once more and from the exit menu that appears select either
|
||||
``Save settings and exit`` or ``Use settings for this session only``. It may
|
||||
take a long time loading the kernel into memory but eventually it boots and the
|
||||
installer becomes visible. The ``ati-vga`` RV100 emulation is not
|
||||
complete yet so only frame buffer works, DRM and 3D is not available.
|
||||
If a firmware menu appears, select ``Boot sequence`` → ``Amiga Multiboot Options``
|
||||
and set ``Boot device 1`` to ``Onboard VIA IDE CDROM``. Then hit escape until
|
||||
the main screen appears again, hit escape once more and from the exit menu that
|
||||
appears select either ``Save settings and exit`` or ``Use settings for this
|
||||
session only``. It may take a long time loading the kernel into memory but
|
||||
eventually it boots and the installer becomes visible. The ``ati-vga`` RV100
|
||||
emulation is not complete yet so only frame buffer works, DRM and 3D is not
|
||||
available.
|
||||
|
||||
Genesi/bPlan Pegasos II (``pegasos2``)
|
||||
======================================
|
||||
|
||||
@@ -4,6 +4,5 @@ Embedded family boards
|
||||
- ``bamboo`` bamboo
|
||||
- ``mpc8544ds`` mpc8544ds
|
||||
- ``ppce500`` generic paravirt e500 platform
|
||||
- ``ref405ep`` ref405ep
|
||||
- ``sam460ex`` aCube Sam460ex
|
||||
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
|
||||
|
||||
@@ -195,6 +195,13 @@ Use a MTD drive to add a PNOR to the machine, and get a NVRAM :
|
||||
|
||||
-drive file=./witherspoon.pnor,format=raw,if=mtd
|
||||
|
||||
If no mtd drive is provided, the powernv platform will create a default
|
||||
PNOR device using a tiny formatted PNOR in pc-bios/pnv-pnor.bin opened
|
||||
read-only (PNOR changes will be persistent across reboots but not across
|
||||
invocations of QEMU). If no defaults are used, an erased 128MB PNOR is
|
||||
provided (which skiboot will probably not recognize since it is not
|
||||
formatted).
|
||||
|
||||
Maintainer contact information
|
||||
------------------------------
|
||||
|
||||
|
||||
+5
-5
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* QEMU PowerPC XIVE interrupt controller model
|
||||
*
|
||||
* Copyright (c) 2017-2019, IBM Corporation.
|
||||
* Copyright (c) 2017-2024, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@@ -473,7 +472,7 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu)
|
||||
|
||||
static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
bool crowd, bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
PnvXive *xive = PNV_XIVE(xptr);
|
||||
@@ -500,7 +499,8 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
* Check the thread context CAM lines and record matches.
|
||||
*/
|
||||
ring = xive_presenter_tctx_match(xptr, tctx, format, nvt_blk,
|
||||
nvt_idx, cam_ignore, logic_serv);
|
||||
nvt_idx, cam_ignore,
|
||||
logic_serv);
|
||||
/*
|
||||
* Save the context and follow on to catch duplicates, that we
|
||||
* don't support yet.
|
||||
|
||||
+136
-30
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* QEMU PowerPC XIVE2 interrupt controller model (POWER10)
|
||||
*
|
||||
* Copyright (c) 2019-2022, IBM Corporation.
|
||||
* Copyright (c) 2019-2024, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@@ -625,7 +624,7 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu)
|
||||
|
||||
static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
bool crowd, bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(xptr);
|
||||
@@ -656,25 +655,38 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
logic_serv);
|
||||
} else {
|
||||
ring = xive2_presenter_tctx_match(xptr, tctx, format, nvt_blk,
|
||||
nvt_idx, cam_ignore,
|
||||
logic_serv);
|
||||
nvt_idx, crowd, cam_ignore,
|
||||
logic_serv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the context and follow on to catch duplicates,
|
||||
* that we don't support yet.
|
||||
*/
|
||||
if (ring != -1) {
|
||||
if (match->tctx) {
|
||||
/*
|
||||
* For VP-specific match, finding more than one is a
|
||||
* problem. For group notification, it's possible.
|
||||
*/
|
||||
if (!cam_ignore && match->tctx) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a "
|
||||
"thread context NVT %x/%x\n",
|
||||
nvt_blk, nvt_idx);
|
||||
return false;
|
||||
/* Should set a FIR if we ever model it */
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* For a group notification, we need to know if the
|
||||
* match is precluded first by checking the current
|
||||
* thread priority. If the interrupt can be delivered,
|
||||
* we always notify the first match (for now).
|
||||
*/
|
||||
if (cam_ignore &&
|
||||
xive2_tm_irq_precluded(tctx, ring, priority)) {
|
||||
match->precluded = true;
|
||||
} else {
|
||||
if (!match->tctx) {
|
||||
match->ring = ring;
|
||||
match->tctx = tctx;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
match->ring = ring;
|
||||
match->tctx = tctx;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -693,6 +705,47 @@ static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr)
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static int pnv_xive2_broadcast(XivePresenter *xptr,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool crowd, bool ignore, uint8_t priority)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(xptr);
|
||||
PnvChip *chip = xive->chip;
|
||||
int i, j;
|
||||
bool gen1_tima_os =
|
||||
xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS;
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pc = chip->cores[i];
|
||||
CPUCore *cc = CPU_CORE(pc);
|
||||
|
||||
for (j = 0; j < cc->nr_threads; j++) {
|
||||
PowerPCCPU *cpu = pc->threads[j];
|
||||
XiveTCTX *tctx;
|
||||
int ring;
|
||||
|
||||
if (!pnv_xive2_is_cpu_enabled(xive, cpu)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc);
|
||||
|
||||
if (gen1_tima_os) {
|
||||
ring = xive_presenter_tctx_match(xptr, tctx, 0, nvt_blk,
|
||||
nvt_idx, ignore, 0);
|
||||
} else {
|
||||
ring = xive2_presenter_tctx_match(xptr, tctx, 0, nvt_blk,
|
||||
nvt_idx, crowd, ignore, 0);
|
||||
}
|
||||
|
||||
if (ring != -1) {
|
||||
xive2_tm_set_lsmfb(tctx, ring, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t pnv_xive2_get_block_id(Xive2Router *xrtr)
|
||||
{
|
||||
return pnv_xive2_block_id(PNV_XIVE2(xrtr));
|
||||
@@ -2149,21 +2202,40 @@ static const MemoryRegionOps pnv_xive2_tm_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr offset,
|
||||
static uint64_t pnv_xive2_nvc_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(opaque);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(xive);
|
||||
uint32_t page = addr >> xive->nvpg_shift;
|
||||
uint16_t op = addr & 0xFFF;
|
||||
uint8_t blk = pnv_xive2_block_id(xive);
|
||||
|
||||
xive2_error(xive, "NVC: invalid read @%"HWADDR_PRIx, offset);
|
||||
return -1;
|
||||
if (size != 2) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc load size %d\n",
|
||||
size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, 1);
|
||||
}
|
||||
|
||||
static void pnv_xive2_nvc_write(void *opaque, hwaddr offset,
|
||||
static void pnv_xive2_nvc_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(opaque);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(xive);
|
||||
uint32_t page = addr >> xive->nvc_shift;
|
||||
uint16_t op = addr & 0xFFF;
|
||||
uint8_t blk = pnv_xive2_block_id(xive);
|
||||
|
||||
xive2_error(xive, "NVC: invalid write @%"HWADDR_PRIx, offset);
|
||||
if (size != 1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvc write size %d\n",
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
(void)xive2_presenter_nvgc_backlog_op(xptr, true, blk, page, op, val);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_xive2_nvc_ops = {
|
||||
@@ -2171,30 +2243,63 @@ static const MemoryRegionOps pnv_xive2_nvc_ops = {
|
||||
.write = pnv_xive2_nvc_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 8,
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 8,
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr offset,
|
||||
static uint64_t pnv_xive2_nvpg_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(opaque);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(xive);
|
||||
uint32_t page = addr >> xive->nvpg_shift;
|
||||
uint16_t op = addr & 0xFFF;
|
||||
uint32_t index = page >> 1;
|
||||
uint8_t blk = pnv_xive2_block_id(xive);
|
||||
|
||||
xive2_error(xive, "NVPG: invalid read @%"HWADDR_PRIx, offset);
|
||||
return -1;
|
||||
if (size != 2) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg load size %d\n",
|
||||
size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (page % 2) {
|
||||
/* odd page - NVG */
|
||||
return xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, 1);
|
||||
} else {
|
||||
/* even page - NVP */
|
||||
return xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_xive2_nvpg_write(void *opaque, hwaddr offset,
|
||||
static void pnv_xive2_nvpg_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvXive2 *xive = PNV_XIVE2(opaque);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(xive);
|
||||
uint32_t page = addr >> xive->nvpg_shift;
|
||||
uint16_t op = addr & 0xFFF;
|
||||
uint32_t index = page >> 1;
|
||||
uint8_t blk = pnv_xive2_block_id(xive);
|
||||
|
||||
xive2_error(xive, "NVPG: invalid write @%"HWADDR_PRIx, offset);
|
||||
if (size != 1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid nvpg write size %d\n",
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page % 2) {
|
||||
/* odd page - NVG */
|
||||
(void)xive2_presenter_nvgc_backlog_op(xptr, false, blk, index, op, val);
|
||||
} else {
|
||||
/* even page - NVP */
|
||||
(void)xive2_presenter_nvp_backlog_op(xptr, blk, index, op);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_xive2_nvpg_ops = {
|
||||
@@ -2202,11 +2307,11 @@ static const MemoryRegionOps pnv_xive2_nvpg_ops = {
|
||||
.write = pnv_xive2_nvpg_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 8,
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 8,
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
@@ -2432,6 +2537,7 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
xpc->match_nvt = pnv_xive2_match_nvt;
|
||||
xpc->get_config = pnv_xive2_presenter_get_config;
|
||||
xpc->broadcast = pnv_xive2_broadcast;
|
||||
};
|
||||
|
||||
static const TypeInfo pnv_xive2_info = {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/*
|
||||
* QEMU PowerPC sPAPR XIVE interrupt controller model
|
||||
*
|
||||
* Copyright (c) 2017-2018, IBM Corporation.
|
||||
* Copyright (c) 2017-2024, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@@ -431,7 +430,8 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk,
|
||||
|
||||
static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
bool crowd, bool cam_ignore,
|
||||
uint8_t priority,
|
||||
uint32_t logic_serv, XiveTCTXMatch *match)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
||||
@@ -283,9 +283,13 @@ xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "EN
|
||||
xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x"
|
||||
xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64
|
||||
xive_tctx_tm_read(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64
|
||||
xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring) "found NVT 0x%x/0x%x ring=0x%x"
|
||||
xive_presenter_notify(uint8_t nvt_blk, uint32_t nvt_idx, uint8_t ring, uint8_t group_level) "found NVT 0x%x/0x%x ring=0x%x group_level=%d"
|
||||
xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x/0x%x @0x%"PRIx64
|
||||
|
||||
# xive2.c
|
||||
xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d"
|
||||
xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d"
|
||||
|
||||
# pnv_xive.c
|
||||
pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64
|
||||
|
||||
|
||||
+132
-73
@@ -3,8 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2017-2018, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@@ -27,28 +26,6 @@
|
||||
* XIVE Thread Interrupt Management context
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert an Interrupt Pending Buffer (IPB) register to a Pending
|
||||
* Interrupt Priority Register (PIPR), which contains the priority of
|
||||
* the most favored pending notification.
|
||||
*/
|
||||
static uint8_t ipb_to_pipr(uint8_t ibp)
|
||||
{
|
||||
return ibp ? clz32((uint32_t)ibp << 24) : 0xff;
|
||||
}
|
||||
|
||||
static uint8_t exception_mask(uint8_t ring)
|
||||
{
|
||||
switch (ring) {
|
||||
case TM_QW1_OS:
|
||||
return TM_QW1_NSR_EO;
|
||||
case TM_QW3_HV_PHYS:
|
||||
return TM_QW3_NSR_HE;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring)
|
||||
{
|
||||
switch (ring) {
|
||||
@@ -68,11 +45,10 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
|
||||
{
|
||||
uint8_t *regs = &tctx->regs[ring];
|
||||
uint8_t nsr = regs[TM_NSR];
|
||||
uint8_t mask = exception_mask(ring);
|
||||
|
||||
qemu_irq_lower(xive_tctx_output(tctx, ring));
|
||||
|
||||
if (regs[TM_NSR] & mask) {
|
||||
if (regs[TM_NSR] != 0) {
|
||||
uint8_t cppr = regs[TM_PIPR];
|
||||
uint8_t alt_ring;
|
||||
uint8_t *alt_regs;
|
||||
@@ -87,11 +63,18 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
|
||||
|
||||
regs[TM_CPPR] = cppr;
|
||||
|
||||
/* Reset the pending buffer bit */
|
||||
alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
|
||||
/*
|
||||
* If the interrupt was for a specific VP, reset the pending
|
||||
* buffer bit, otherwise clear the logical server indicator
|
||||
*/
|
||||
if (regs[TM_NSR] & TM_NSR_GRP_LVL) {
|
||||
regs[TM_NSR] &= ~TM_NSR_GRP_LVL;
|
||||
} else {
|
||||
alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr);
|
||||
}
|
||||
|
||||
/* Drop Exception bit */
|
||||
regs[TM_NSR] &= ~mask;
|
||||
/* Drop the exception bit and any group/crowd */
|
||||
regs[TM_NSR] = 0;
|
||||
|
||||
trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring,
|
||||
alt_regs[TM_IPB], regs[TM_PIPR],
|
||||
@@ -101,7 +84,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring)
|
||||
return ((uint64_t)nsr << 8) | regs[TM_CPPR];
|
||||
}
|
||||
|
||||
static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring)
|
||||
void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level)
|
||||
{
|
||||
/* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
|
||||
uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
|
||||
@@ -111,13 +94,13 @@ static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring)
|
||||
if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) {
|
||||
switch (ring) {
|
||||
case TM_QW1_OS:
|
||||
regs[TM_NSR] |= TM_QW1_NSR_EO;
|
||||
regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F);
|
||||
break;
|
||||
case TM_QW2_HV_POOL:
|
||||
alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6);
|
||||
alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F);
|
||||
break;
|
||||
case TM_QW3_HV_PHYS:
|
||||
regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6);
|
||||
regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
@@ -159,7 +142,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
|
||||
* Recompute the PIPR based on local pending interrupts. The PHYS
|
||||
* ring must take the minimum of both the PHYS and POOL PIPR values.
|
||||
*/
|
||||
pipr_min = ipb_to_pipr(regs[TM_IPB]);
|
||||
pipr_min = xive_ipb_to_pipr(regs[TM_IPB]);
|
||||
ring_min = ring;
|
||||
|
||||
/* PHYS updates also depend on POOL values */
|
||||
@@ -169,7 +152,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
|
||||
/* POOL values only matter if POOL ctx is valid */
|
||||
if (pool_regs[TM_WORD2] & 0x80) {
|
||||
|
||||
uint8_t pool_pipr = ipb_to_pipr(pool_regs[TM_IPB]);
|
||||
uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]);
|
||||
|
||||
/*
|
||||
* Determine highest priority interrupt and
|
||||
@@ -185,17 +168,27 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr)
|
||||
regs[TM_PIPR] = pipr_min;
|
||||
|
||||
/* CPPR has changed, check if we need to raise a pending exception */
|
||||
xive_tctx_notify(tctx, ring_min);
|
||||
xive_tctx_notify(tctx, ring_min, 0);
|
||||
}
|
||||
|
||||
void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb)
|
||||
{
|
||||
void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority,
|
||||
uint8_t group_level)
|
||||
{
|
||||
/* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */
|
||||
uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring;
|
||||
uint8_t *alt_regs = &tctx->regs[alt_ring];
|
||||
uint8_t *regs = &tctx->regs[ring];
|
||||
|
||||
regs[TM_IPB] |= ipb;
|
||||
regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]);
|
||||
xive_tctx_notify(tctx, ring);
|
||||
}
|
||||
if (group_level == 0) {
|
||||
/* VP-specific */
|
||||
regs[TM_IPB] |= xive_priority_to_ipb(priority);
|
||||
alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]);
|
||||
} else {
|
||||
/* VP-group */
|
||||
alt_regs[TM_PIPR] = xive_priority_to_pipr(priority);
|
||||
}
|
||||
xive_tctx_notify(tctx, ring, group_level);
|
||||
}
|
||||
|
||||
/*
|
||||
* XIVE Thread Interrupt Management Area (TIMA)
|
||||
@@ -411,13 +404,13 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the IPB to allow a CPU to process event queues of other
|
||||
* Adjust the PIPR to allow a CPU to process event queues of other
|
||||
* priorities during one physical interrupt cycle.
|
||||
*/
|
||||
static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
xive_tctx_ipb_update(tctx, TM_QW1_OS, xive_priority_to_ipb(value & 0xff));
|
||||
xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0);
|
||||
}
|
||||
|
||||
static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk,
|
||||
@@ -495,16 +488,20 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx,
|
||||
/* Reset the NVT value */
|
||||
nvt.w4 = xive_set_field32(NVT_W4_IPB, nvt.w4, 0);
|
||||
xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4);
|
||||
|
||||
uint8_t *regs = &tctx->regs[TM_QW1_OS];
|
||||
regs[TM_IPB] |= ipb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Always call xive_tctx_ipb_update(). Even if there were no
|
||||
* Always call xive_tctx_pipr_update(). Even if there were no
|
||||
* escalation triggered, there could be a pending interrupt which
|
||||
* was saved when the context was pulled and that we need to take
|
||||
* into account by recalculating the PIPR (which is not
|
||||
* saved/restored).
|
||||
* It will also raise the External interrupt signal if needed.
|
||||
*/
|
||||
xive_tctx_ipb_update(tctx, TM_QW1_OS, ipb);
|
||||
xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -592,7 +589,7 @@ static const XiveTmOp xive2_tm_operations[] = {
|
||||
* MMIOs below 2K : raw values and special operations without side
|
||||
* effects
|
||||
*/
|
||||
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr,
|
||||
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr,
|
||||
NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx,
|
||||
NULL },
|
||||
@@ -600,7 +597,7 @@ static const XiveTmOp xive2_tm_operations[] = {
|
||||
NULL },
|
||||
{ XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs,
|
||||
NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr,
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr,
|
||||
NULL },
|
||||
{ XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push,
|
||||
NULL },
|
||||
@@ -841,9 +838,9 @@ void xive_tctx_reset(XiveTCTX *tctx)
|
||||
* CPPR is first set.
|
||||
*/
|
||||
tctx->regs[TM_QW1_OS + TM_PIPR] =
|
||||
ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]);
|
||||
xive_ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]);
|
||||
tctx->regs[TM_QW3_HV_PHYS + TM_PIPR] =
|
||||
ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]);
|
||||
xive_ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]);
|
||||
}
|
||||
|
||||
static void xive_tctx_realize(DeviceState *dev, Error **errp)
|
||||
@@ -1658,6 +1655,54 @@ static uint32_t xive_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx)
|
||||
return xive_nvt_cam_line(blk, 1 << 7 | (pir & 0x7f));
|
||||
}
|
||||
|
||||
uint32_t xive_get_vpgroup_size(uint32_t nvp_index)
|
||||
{
|
||||
/*
|
||||
* Group size is a power of 2. The position of the first 0
|
||||
* (starting with the least significant bits) in the NVP index
|
||||
* gives the size of the group.
|
||||
*/
|
||||
return 1 << (ctz32(~nvp_index) + 1);
|
||||
}
|
||||
|
||||
static uint8_t xive_get_group_level(bool crowd, bool ignore,
|
||||
uint32_t nvp_blk, uint32_t nvp_index)
|
||||
{
|
||||
uint8_t level;
|
||||
|
||||
if (!ignore) {
|
||||
g_assert(!crowd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
level = (ctz32(~nvp_index) + 1) & 0b1111;
|
||||
if (crowd) {
|
||||
uint32_t blk;
|
||||
|
||||
/* crowd level is bit position of first 0 from the right in nvp_blk */
|
||||
blk = ctz32(~nvp_blk) + 1;
|
||||
|
||||
/*
|
||||
* Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported.
|
||||
* HW will encode level 4 as the value 3. See xive2_pgofnext().
|
||||
*/
|
||||
switch (level) {
|
||||
case 1:
|
||||
case 2:
|
||||
break;
|
||||
case 4:
|
||||
blk = 3;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Crowd level bits reside in upper 2 bits of the 6 bit group level */
|
||||
level |= blk << 4;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
/*
|
||||
* The thread context register words are in big-endian format.
|
||||
*/
|
||||
@@ -1724,31 +1769,41 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx,
|
||||
/*
|
||||
* This is our simple Xive Presenter Engine model. It is merged in the
|
||||
* Router as it does not require an extra object.
|
||||
*
|
||||
* It receives notification requests sent by the IVRE to find one
|
||||
* matching NVT (or more) dispatched on the processor threads. In case
|
||||
* of a single NVT notification, the process is abbreviated and the
|
||||
* thread is signaled if a match is found. In case of a logical server
|
||||
* notification (bits ignored at the end of the NVT identifier), the
|
||||
* IVPE and IVRE select a winning thread using different filters. This
|
||||
* involves 2 or 3 exchanges on the PowerBus that the model does not
|
||||
* support.
|
||||
*
|
||||
* The parameters represent what is sent on the PowerBus
|
||||
*/
|
||||
bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv)
|
||||
bool crowd, bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv, bool *precluded)
|
||||
{
|
||||
XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb);
|
||||
XiveTCTXMatch match = { .tctx = NULL, .ring = 0 };
|
||||
XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false };
|
||||
uint8_t group_level;
|
||||
int count;
|
||||
|
||||
/*
|
||||
* Ask the machine to scan the interrupt controllers for a match
|
||||
* Ask the machine to scan the interrupt controllers for a match.
|
||||
*
|
||||
* For VP-specific notification, we expect at most one match and
|
||||
* one call to the presenters is all we need (abbreviated notify
|
||||
* sequence documented by the architecture).
|
||||
*
|
||||
* For VP-group notification, match_nvt() is the equivalent of the
|
||||
* "histogram" and "poll" commands sent to the power bus to the
|
||||
* presenters. 'count' could be more than one, but we always
|
||||
* select the first match for now. 'precluded' tells if (at least)
|
||||
* one thread matches but can't take the interrupt now because
|
||||
* it's running at a more favored priority. We return the
|
||||
* information to the router so that it can take appropriate
|
||||
* actions (backlog, escalation, broadcast, etc...)
|
||||
*
|
||||
* If we were to implement a better way of dispatching the
|
||||
* interrupt in case of multiple matches (instead of the first
|
||||
* match), we would need a heuristic to elect a thread (for
|
||||
* example, the hardware keeps track of an 'age' in the TIMA) and
|
||||
* a new command to the presenters (the equivalent of the "assign"
|
||||
* power bus command in the documented full notify sequence.
|
||||
*/
|
||||
count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore,
|
||||
priority, logic_serv, &match);
|
||||
if (count < 0) {
|
||||
return false;
|
||||
@@ -1756,9 +1811,11 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format,
|
||||
|
||||
/* handle CPU exception delivery */
|
||||
if (count) {
|
||||
trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring);
|
||||
xive_tctx_ipb_update(match.tctx, match.ring,
|
||||
xive_priority_to_ipb(priority));
|
||||
group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx);
|
||||
trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level);
|
||||
xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level);
|
||||
} else {
|
||||
*precluded = match.precluded;
|
||||
}
|
||||
|
||||
return !!count;
|
||||
@@ -1798,7 +1855,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
|
||||
uint8_t nvt_blk;
|
||||
uint32_t nvt_idx;
|
||||
XiveNVT nvt;
|
||||
bool found;
|
||||
bool found, precluded;
|
||||
|
||||
uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
|
||||
uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
|
||||
@@ -1879,10 +1936,12 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
|
||||
}
|
||||
|
||||
found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx,
|
||||
false /* crowd */,
|
||||
xive_get_field32(END_W7_F0_IGNORE, end.w7),
|
||||
priority,
|
||||
xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7));
|
||||
|
||||
xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7),
|
||||
&precluded);
|
||||
/* we don't support VP-group notification on P9, so precluded is not used */
|
||||
/* TODO: Auto EOI. */
|
||||
|
||||
if (found) {
|
||||
|
||||
+619
-71
File diff suppressed because it is too large
Load Diff
@@ -197,6 +197,9 @@ static PnvPHB *pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
|
||||
return phb;
|
||||
}
|
||||
|
||||
#define XPEC_P9_PCI_LANE_CFG PPC_BITMASK(10, 11)
|
||||
#define XPEC_P10_PCI_LANE_CFG PPC_BITMASK(0, 1)
|
||||
|
||||
static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPhb4PecState *pec = PNV_PHB4_PEC(dev);
|
||||
@@ -211,6 +214,43 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
pec->num_phbs = pecc->num_phbs[pec->index];
|
||||
|
||||
/* Pervasive chiplet */
|
||||
object_initialize_child(OBJECT(pec), "nest-pervasive-common",
|
||||
&pec->nest_pervasive,
|
||||
TYPE_PNV_NEST_CHIPLET_PERVASIVE);
|
||||
if (!qdev_realize(DEVICE(&pec->nest_pervasive), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set up pervasive chiplet registers */
|
||||
/*
|
||||
* Most registers are not set up, this just sets the PCI CONF1 link-width
|
||||
* field because skiboot probes it.
|
||||
*/
|
||||
if (pecc->version == PNV_PHB4_VERSION) {
|
||||
/*
|
||||
* On P9, PEC2 has configurable 1/2/3-furcation).
|
||||
* Make it trifurcated (x8, x4, x4) to match pnv_pec_num_phbs.
|
||||
*/
|
||||
if (pec->index == 2) {
|
||||
pec->nest_pervasive.control_regs.cplt_cfg1 =
|
||||
SETFIELD(XPEC_P9_PCI_LANE_CFG,
|
||||
pec->nest_pervasive.control_regs.cplt_cfg1,
|
||||
0b10);
|
||||
}
|
||||
} else if (pecc->version == PNV_PHB5_VERSION) {
|
||||
/*
|
||||
* On P10, both PECs are configurable 1/2/3-furcation).
|
||||
* Both are trifurcated to match pnv_phb5_pec_num_stacks.
|
||||
*/
|
||||
pec->nest_pervasive.control_regs.cplt_cfg1 =
|
||||
SETFIELD(XPEC_P10_PCI_LANE_CFG,
|
||||
pec->nest_pervasive.control_regs.cplt_cfg1,
|
||||
0b10);
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Create PHBs if running with defaults */
|
||||
if (defaults_enabled()) {
|
||||
g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC);
|
||||
@@ -290,9 +330,16 @@ static const Property pnv_pec_properties[] = {
|
||||
PnvChip *),
|
||||
};
|
||||
|
||||
#define XPEC_PCI_CPLT_OFFSET 0x1000000ULL
|
||||
|
||||
static uint32_t pnv_pec_xscom_cplt_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
return PNV9_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
|
||||
}
|
||||
|
||||
static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index;
|
||||
return PNV9_XSCOM_PEC_PCI_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
|
||||
}
|
||||
|
||||
static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec)
|
||||
@@ -321,6 +368,7 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
||||
device_class_set_props(dc, pnv_pec_properties);
|
||||
dc->user_creatable = false;
|
||||
|
||||
pecc->xscom_cplt_base = pnv_pec_xscom_cplt_base;
|
||||
pecc->xscom_nest_base = pnv_pec_xscom_nest_base;
|
||||
pecc->xscom_pci_base = pnv_pec_xscom_pci_base;
|
||||
pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE;
|
||||
@@ -349,6 +397,10 @@ static const TypeInfo pnv_pec_type_info = {
|
||||
/*
|
||||
* POWER10 definitions
|
||||
*/
|
||||
static uint32_t pnv_phb5_pec_xscom_cplt_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
return PNV10_XSCOM_PEC_NEST_CPLT_BASE + XPEC_PCI_CPLT_OFFSET * pec->index;
|
||||
}
|
||||
|
||||
static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec)
|
||||
{
|
||||
@@ -373,6 +425,7 @@ static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data)
|
||||
static const char compat[] = "ibm,power10-pbcq";
|
||||
static const char stk_compat[] = "ibm,power10-phb-stack";
|
||||
|
||||
pecc->xscom_cplt_base = pnv_phb5_pec_xscom_cplt_base;
|
||||
pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base;
|
||||
pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base;
|
||||
pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE;
|
||||
|
||||
@@ -44,15 +44,6 @@ config POWERNV
|
||||
select SSI_M25P80
|
||||
select PNV_SPI
|
||||
|
||||
config PPC405
|
||||
bool
|
||||
default y
|
||||
depends on PPC
|
||||
select M48T59
|
||||
select PFLASH_CFI02
|
||||
select PPC4XX
|
||||
select SERIAL_MM
|
||||
|
||||
config PPC440
|
||||
bool
|
||||
default y
|
||||
|
||||
+271
-13
@@ -21,12 +21,26 @@
|
||||
#include "hw/ide/pci.h"
|
||||
#include "hw/i2c/smbus_eeprom.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "system/block-backend.h"
|
||||
#include "system/qtest.h"
|
||||
#include "system/reset.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "elf.h"
|
||||
|
||||
#include <zlib.h> /* for crc32 */
|
||||
|
||||
#define BUS_FREQ_HZ 100000000
|
||||
|
||||
#define INITRD_MIN_ADDR 0x600000
|
||||
#define INIT_RAM_ADDR 0x40000000
|
||||
|
||||
#define PCI_HIGH_ADDR 0x80000000
|
||||
#define PCI_HIGH_SIZE 0x7d000000
|
||||
#define PCI_LOW_ADDR 0xfd000000
|
||||
#define PCI_LOW_SIZE 0xe0000
|
||||
|
||||
#define ARTICIA_ADDR 0xfe000000
|
||||
|
||||
/*
|
||||
* Firmware binary available at
|
||||
* https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28
|
||||
@@ -41,20 +55,202 @@
|
||||
|
||||
/* AmigaOS calls this routine from ROM, use this if no firmware loaded */
|
||||
static const char dummy_fw[] = {
|
||||
0x38, 0x00, 0x00, 0x08, /* li r0,8 */
|
||||
0x7c, 0x09, 0x03, 0xa6, /* mtctr r0 */
|
||||
0x54, 0x63, 0xf8, 0x7e, /* srwi r3,r3,1 */
|
||||
0x42, 0x00, 0xff, 0xfc, /* bdnz 0x8 */
|
||||
0x54, 0x63, 0xc2, 0x3e, /* srwi r3,r3,8 */
|
||||
0x7c, 0x63, 0x18, 0xf8, /* not r3,r3 */
|
||||
0x4e, 0x80, 0x00, 0x20, /* blr */
|
||||
};
|
||||
|
||||
#define NVRAM_ADDR 0xfd0e0000
|
||||
#define NVRAM_SIZE (4 * KiB)
|
||||
|
||||
static char default_env[] =
|
||||
"baudrate=115200\0"
|
||||
"stdout=vga\0"
|
||||
"stdin=ps2kbd\0"
|
||||
"bootcmd=boota; menu; run menuboot_cmd\0"
|
||||
"boot1=ide\0"
|
||||
"boot2=cdrom\0"
|
||||
"boota_timeout=3\0"
|
||||
"ide_doreset=on\0"
|
||||
"pci_irqa=9\0"
|
||||
"pci_irqa_select=level\0"
|
||||
"pci_irqb=10\0"
|
||||
"pci_irqb_select=level\0"
|
||||
"pci_irqc=11\0"
|
||||
"pci_irqc_select=level\0"
|
||||
"pci_irqd=7\0"
|
||||
"pci_irqd_select=level\0"
|
||||
"a1ide_irq=1111\0"
|
||||
"a1ide_xfer=FFFF\0";
|
||||
#define CRC32_DEFAULT_ENV 0xb5548481
|
||||
#define CRC32_ALL_ZEROS 0x603b0489
|
||||
|
||||
#define TYPE_A1_NVRAM "a1-nvram"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM)
|
||||
|
||||
struct A1NVRAMState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion mr;
|
||||
BlockBackend *blk;
|
||||
};
|
||||
|
||||
static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
/* read callback not used because of romd mode */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void nvram_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned int size)
|
||||
{
|
||||
A1NVRAMState *s = opaque;
|
||||
uint8_t *p = memory_region_get_ram_ptr(&s->mr);
|
||||
|
||||
p[addr] = val;
|
||||
if (s->blk) {
|
||||
blk_pwrite(s->blk, addr, 1, &val, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nvram_ops = {
|
||||
.read = nvram_read,
|
||||
.write = nvram_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void nvram_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
A1NVRAMState *s = A1_NVRAM(dev);
|
||||
void *p;
|
||||
uint32_t crc, *c;
|
||||
|
||||
memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram",
|
||||
NVRAM_SIZE, &error_fatal);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
|
||||
c = p = memory_region_get_ram_ptr(&s->mr);
|
||||
if (s->blk) {
|
||||
if (blk_getlength(s->blk) != NVRAM_SIZE) {
|
||||
error_setg(errp, "NVRAM backing file size must be %" PRId64 "bytes",
|
||||
NVRAM_SIZE);
|
||||
return;
|
||||
}
|
||||
blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
|
||||
BLK_PERM_ALL, &error_fatal);
|
||||
if (blk_pread(s->blk, 0, NVRAM_SIZE, p, 0) < 0) {
|
||||
error_setg(errp, "Cannot read NVRAM contents from backing file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
crc = crc32(0, p + 4, NVRAM_SIZE - 4);
|
||||
if (crc == CRC32_ALL_ZEROS) { /* If env is uninitialized set default */
|
||||
*c = cpu_to_be32(CRC32_DEFAULT_ENV);
|
||||
/* Also copies terminating \0 as env is terminated by \0\0 */
|
||||
memcpy(p + 4, default_env, sizeof(default_env));
|
||||
if (s->blk) {
|
||||
blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (*c == 0) {
|
||||
*c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4));
|
||||
if (s->blk) {
|
||||
blk_pwrite(s->blk, 0, 4, p, 0);
|
||||
}
|
||||
}
|
||||
if (be32_to_cpu(*c) != crc) {
|
||||
warn_report("NVRAM checksum mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
static const Property nvram_properties[] = {
|
||||
DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk),
|
||||
};
|
||||
|
||||
static void nvram_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = nvram_realize;
|
||||
device_class_set_props(dc, nvram_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo nvram_types[] = {
|
||||
{
|
||||
.name = TYPE_A1_NVRAM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(A1NVRAMState),
|
||||
.class_init = nvram_class_init,
|
||||
},
|
||||
};
|
||||
DEFINE_TYPES(nvram_types)
|
||||
|
||||
struct boot_info {
|
||||
hwaddr entry;
|
||||
hwaddr stack;
|
||||
hwaddr bd_info;
|
||||
hwaddr initrd_start;
|
||||
hwaddr initrd_end;
|
||||
hwaddr cmdline_start;
|
||||
hwaddr cmdline_end;
|
||||
};
|
||||
|
||||
/* Board info struct from U-Boot */
|
||||
struct bd_info {
|
||||
uint32_t bi_memstart;
|
||||
uint32_t bi_memsize;
|
||||
uint32_t bi_flashstart;
|
||||
uint32_t bi_flashsize;
|
||||
uint32_t bi_flashoffset;
|
||||
uint32_t bi_sramstart;
|
||||
uint32_t bi_sramsize;
|
||||
uint32_t bi_bootflags;
|
||||
uint32_t bi_ip_addr;
|
||||
uint8_t bi_enetaddr[6];
|
||||
uint16_t bi_ethspeed;
|
||||
uint32_t bi_intfreq;
|
||||
uint32_t bi_busfreq;
|
||||
uint32_t bi_baudrate;
|
||||
} QEMU_PACKED;
|
||||
|
||||
static void create_bd_info(hwaddr addr, ram_addr_t ram_size)
|
||||
{
|
||||
struct bd_info *bd = g_new0(struct bd_info, 1);
|
||||
|
||||
bd->bi_memsize = cpu_to_be32(ram_size);
|
||||
bd->bi_flashstart = cpu_to_be32(PROM_ADDR);
|
||||
bd->bi_flashsize = cpu_to_be32(1); /* match what U-Boot detects */
|
||||
bd->bi_bootflags = cpu_to_be32(1);
|
||||
bd->bi_intfreq = cpu_to_be32(11.5 * BUS_FREQ_HZ);
|
||||
bd->bi_busfreq = cpu_to_be32(BUS_FREQ_HZ);
|
||||
bd->bi_baudrate = cpu_to_be32(115200);
|
||||
|
||||
cpu_physical_memory_write(addr, bd, sizeof(*bd));
|
||||
}
|
||||
|
||||
static void amigaone_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
cpu_ppc_tb_reset(&cpu->env);
|
||||
if (env->load_info) {
|
||||
struct boot_info *bi = env->load_info;
|
||||
|
||||
env->gpr[1] = bi->stack;
|
||||
env->gpr[2] = 1024;
|
||||
env->gpr[3] = bi->bd_info;
|
||||
env->gpr[4] = bi->initrd_start;
|
||||
env->gpr[5] = bi->initrd_end;
|
||||
env->gpr[6] = bi->cmdline_start;
|
||||
env->gpr[7] = bi->cmdline_end;
|
||||
env->nip = bi->entry;
|
||||
}
|
||||
cpu_ppc_tb_reset(env);
|
||||
}
|
||||
|
||||
static void fix_spd_data(uint8_t *spd)
|
||||
@@ -75,7 +271,9 @@ static void amigaone_init(MachineState *machine)
|
||||
DeviceState *dev;
|
||||
I2CBus *i2c_bus;
|
||||
uint8_t *spd_data;
|
||||
int i;
|
||||
DriveInfo *di;
|
||||
hwaddr loadaddr;
|
||||
struct boot_info *bi = NULL;
|
||||
|
||||
/* init CPU */
|
||||
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
|
||||
@@ -97,9 +295,19 @@ static void amigaone_init(MachineState *machine)
|
||||
/* Firmware uses this area for startup */
|
||||
mr = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal);
|
||||
memory_region_add_subregion(get_system_memory(), 0x40000000, mr);
|
||||
memory_region_add_subregion(get_system_memory(), INIT_RAM_ADDR, mr);
|
||||
}
|
||||
|
||||
/* nvram */
|
||||
dev = qdev_new(TYPE_A1_NVRAM);
|
||||
di = drive_get(IF_MTD, 0, 0);
|
||||
if (di) {
|
||||
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di));
|
||||
}
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
memory_region_add_subregion(get_system_memory(), NVRAM_ADDR,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
|
||||
|
||||
/* allocate and load firmware */
|
||||
rom = g_new(MemoryRegion, 1);
|
||||
memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal);
|
||||
@@ -122,7 +330,7 @@ static void amigaone_init(MachineState *machine)
|
||||
}
|
||||
|
||||
/* Articia S */
|
||||
dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL);
|
||||
dev = sysbus_create_simple(TYPE_ARTICIA, ARTICIA_ADDR, NULL);
|
||||
|
||||
i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus"));
|
||||
if (machine->ram_size > 512 * MiB) {
|
||||
@@ -139,12 +347,12 @@ static void amigaone_init(MachineState *machine)
|
||||
pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
|
||||
mr = g_new(MemoryRegion, 1);
|
||||
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem,
|
||||
0, 0x1000000);
|
||||
memory_region_add_subregion(get_system_memory(), 0xfd000000, mr);
|
||||
0, PCI_LOW_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), PCI_LOW_ADDR, mr);
|
||||
mr = g_new(MemoryRegion, 1);
|
||||
memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem,
|
||||
0x80000000, 0x7d000000);
|
||||
memory_region_add_subregion(get_system_memory(), 0x80000000, mr);
|
||||
PCI_HIGH_ADDR, PCI_HIGH_SIZE);
|
||||
memory_region_add_subregion(get_system_memory(), PCI_HIGH_ADDR, mr);
|
||||
pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
|
||||
|
||||
/* VIA VT82c686B South Bridge (multifunction PCI device) */
|
||||
@@ -156,12 +364,62 @@ static void amigaone_init(MachineState *machine)
|
||||
qdev_connect_gpio_out_named(DEVICE(via), "intr", 0,
|
||||
qdev_get_gpio_in(DEVICE(cpu),
|
||||
PPC6xx_INPUT_INT));
|
||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||
for (int i = 0; i < PCI_NUM_PINS; i++) {
|
||||
qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via),
|
||||
"pirq", i));
|
||||
}
|
||||
pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide")));
|
||||
pci_vga_init(pci_bus);
|
||||
|
||||
if (!machine->kernel_filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle -kernel, -initrd, -append options and emulate U-Boot */
|
||||
bi = g_new0(struct boot_info, 1);
|
||||
cpu->env.load_info = bi;
|
||||
|
||||
loadaddr = MIN(machine->ram_size, 256 * MiB);
|
||||
bi->bd_info = loadaddr - 8 * MiB;
|
||||
create_bd_info(bi->bd_info, machine->ram_size);
|
||||
bi->stack = bi->bd_info - 64 * KiB - 8;
|
||||
|
||||
if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
|
||||
size_t len = strlen(machine->kernel_cmdline);
|
||||
|
||||
loadaddr = bi->bd_info + 1 * MiB;
|
||||
cpu_physical_memory_write(loadaddr, machine->kernel_cmdline, len + 1);
|
||||
bi->cmdline_start = loadaddr;
|
||||
bi->cmdline_end = loadaddr + len + 1; /* including terminating '\0' */
|
||||
}
|
||||
|
||||
sz = load_elf(machine->kernel_filename, NULL, NULL, NULL,
|
||||
&bi->entry, &loadaddr, NULL, NULL,
|
||||
ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0);
|
||||
if (sz <= 0) {
|
||||
sz = load_uimage(machine->kernel_filename, &bi->entry, &loadaddr,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
if (sz <= 0) {
|
||||
error_report("Could not load kernel '%s'",
|
||||
machine->kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
loadaddr += sz;
|
||||
|
||||
if (machine->initrd_filename) {
|
||||
loadaddr = ROUND_UP(loadaddr + 4 * MiB, 4 * KiB);
|
||||
loadaddr = MAX(loadaddr, INITRD_MIN_ADDR);
|
||||
sz = load_image_targphys(machine->initrd_filename, loadaddr,
|
||||
bi->bd_info - loadaddr);
|
||||
if (sz <= 0) {
|
||||
error_report("Could not load initrd '%s'",
|
||||
machine->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
bi->initrd_start = loadaddr;
|
||||
bi->initrd_end = loadaddr + sz;
|
||||
}
|
||||
}
|
||||
|
||||
static void amigaone_machine_init(MachineClass *mc)
|
||||
|
||||
@@ -57,9 +57,6 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
|
||||
'pnv_n1_chiplet.c',
|
||||
))
|
||||
# PowerPC 4xx boards
|
||||
ppc_ss.add(when: 'CONFIG_PPC405', if_true: files(
|
||||
'ppc405_boards.c',
|
||||
'ppc405_uc.c'))
|
||||
ppc_ss.add(when: 'CONFIG_PPC440', if_true: files(
|
||||
'ppc440_bamboo.c',
|
||||
'ppc440_uc.c'))
|
||||
|
||||
+99
-51
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
* QEMU PowerPC PowerNV machine model
|
||||
*
|
||||
* Copyright (c) 2016, IBM Corporation.
|
||||
* Copyright (c) 2016-2024, IBM Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -64,6 +66,8 @@
|
||||
#define FW_LOAD_ADDR 0x0
|
||||
#define FW_MAX_SIZE (16 * MiB)
|
||||
|
||||
#define PNOR_FILE_NAME "pnv-pnor.bin"
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x20000000
|
||||
#define KERNEL_MAX_SIZE (128 * MiB)
|
||||
#define INITRD_LOAD_ADDR 0x28000000
|
||||
@@ -941,7 +945,7 @@ static void pnv_init(MachineState *machine)
|
||||
uint64_t chip_ram_start = 0;
|
||||
int i;
|
||||
char *chip_typename;
|
||||
DriveInfo *pnor = drive_get(IF_MTD, 0, 0);
|
||||
DriveInfo *pnor;
|
||||
DeviceState *dev;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
@@ -971,6 +975,18 @@ static void pnv_init(MachineState *machine)
|
||||
* Create our simple PNOR device
|
||||
*/
|
||||
dev = qdev_new(TYPE_PNV_PNOR);
|
||||
pnor = drive_get(IF_MTD, 0, 0);
|
||||
if (!pnor && defaults_enabled()) {
|
||||
fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, PNOR_FILE_NAME);
|
||||
if (!fw_filename) {
|
||||
warn_report("Could not find PNOR '%s'", PNOR_FILE_NAME);
|
||||
} else {
|
||||
QemuOpts *opts;
|
||||
opts = drive_add(IF_MTD, -1, fw_filename, "format=raw,readonly=on");
|
||||
pnor = drive_new(opts, IF_MTD, &error_fatal);
|
||||
g_free(fw_filename);
|
||||
}
|
||||
}
|
||||
if (pnor) {
|
||||
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor));
|
||||
}
|
||||
@@ -1555,7 +1571,21 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* HOMER (must be created before OCC) */
|
||||
object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs);
|
||||
/* Homer RAM region */
|
||||
memory_region_add_subregion(get_system_memory(), chip8->homer.base,
|
||||
&chip8->homer.mem);
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
object_property_set_link(OBJECT(&chip8->occ), "homer",
|
||||
OBJECT(&chip8->homer), &error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip8->occ), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
@@ -1567,19 +1597,6 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip),
|
||||
&chip8->occ.sram_regs);
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip8->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip8->homer), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs);
|
||||
|
||||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
|
||||
&chip8->homer.regs);
|
||||
|
||||
/* PHB controllers */
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
PnvPHB *phb = chip8->phbs[i];
|
||||
@@ -1753,6 +1770,7 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
PnvPhb4PecState *pec = &chip9->pecs[i];
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
uint32_t pec_cplt_base;
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
|
||||
@@ -1765,9 +1783,12 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
pec_cplt_base = pecc->xscom_cplt_base(pec);
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
pnv_xscom_add_subregion(chip, pec_cplt_base,
|
||||
&pec->nest_pervasive.xscom_ctrl_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
|
||||
}
|
||||
@@ -1859,18 +1880,6 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE,
|
||||
&chip9->chiptod.xscom_regs);
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs);
|
||||
qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in(
|
||||
DEVICE(psi9), PSIHB9_IRQ_OCC));
|
||||
|
||||
/* OCC SRAM model */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
|
||||
&chip9->occ.sram_regs);
|
||||
|
||||
/* SBE */
|
||||
if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) {
|
||||
return;
|
||||
@@ -1882,7 +1891,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
qdev_connect_gpio_out(DEVICE(&chip9->sbe), 0, qdev_get_gpio_in(
|
||||
DEVICE(psi9), PSIHB9_IRQ_PSU));
|
||||
|
||||
/* HOMER */
|
||||
/* HOMER (must be created before OCC) */
|
||||
object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip9->homer), NULL, errp)) {
|
||||
@@ -1890,10 +1899,23 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs);
|
||||
/* Homer RAM region */
|
||||
memory_region_add_subregion(get_system_memory(), chip9->homer.base,
|
||||
&chip9->homer.mem);
|
||||
|
||||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip),
|
||||
&chip9->homer.regs);
|
||||
/* Create the simplified OCC model */
|
||||
object_property_set_link(OBJECT(&chip9->occ), "homer",
|
||||
OBJECT(&chip9->homer), &error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs);
|
||||
qdev_connect_gpio_out(DEVICE(&chip9->occ), 0, qdev_get_gpio_in(
|
||||
DEVICE(psi9), PSIHB9_IRQ_OCC));
|
||||
|
||||
/* OCC SRAM model */
|
||||
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
|
||||
&chip9->occ.sram_regs);
|
||||
|
||||
/* PEC PHBs */
|
||||
pnv_chip_power9_pec_realize(chip, &local_err);
|
||||
@@ -2027,6 +2049,7 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp)
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
PnvPhb4PecState *pec = &chip10->pecs[i];
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
uint32_t pec_cplt_base;
|
||||
uint32_t pec_nest_base;
|
||||
uint32_t pec_pci_base;
|
||||
|
||||
@@ -2039,9 +2062,12 @@ static void pnv_chip_power10_phb_realize(PnvChip *chip, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
pec_cplt_base = pecc->xscom_cplt_base(pec);
|
||||
pec_nest_base = pecc->xscom_nest_base(pec);
|
||||
pec_pci_base = pecc->xscom_pci_base(pec);
|
||||
|
||||
pnv_xscom_add_subregion(chip, pec_cplt_base,
|
||||
&pec->nest_pervasive.xscom_ctrl_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr);
|
||||
pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr);
|
||||
}
|
||||
@@ -2136,7 +2162,22 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE,
|
||||
&chip10->chiptod.xscom_regs);
|
||||
|
||||
/* HOMER (must be created before OCC) */
|
||||
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE,
|
||||
&chip10->homer.pba_regs);
|
||||
/* Homer RAM region */
|
||||
memory_region_add_subregion(get_system_memory(), chip10->homer.base,
|
||||
&chip10->homer.mem);
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
object_property_set_link(OBJECT(&chip10->occ), "homer",
|
||||
OBJECT(&chip10->homer), &error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
@@ -2161,20 +2202,6 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in(
|
||||
DEVICE(&chip10->psi), PSIHB9_IRQ_PSU));
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip10->homer), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
/* Homer Xscom region */
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_PBA_BASE,
|
||||
&chip10->homer.pba_regs);
|
||||
|
||||
/* Homer mmio region */
|
||||
memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip),
|
||||
&chip10->homer.regs);
|
||||
|
||||
/* N1 chiplet */
|
||||
if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) {
|
||||
return;
|
||||
@@ -2225,6 +2252,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
/* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */
|
||||
object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len",
|
||||
(i == 2) ? 1 : 4, &error_fatal);
|
||||
object_property_set_int(OBJECT(&chip10->pib_spic[i]), "chip-id",
|
||||
chip->chip_id, &error_fatal);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT
|
||||
(&chip10->pib_spic[i])), errp)) {
|
||||
return;
|
||||
@@ -2581,7 +2610,7 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf)
|
||||
|
||||
static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
bool crowd, bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv,
|
||||
XiveTCTXMatch *match)
|
||||
{
|
||||
@@ -2595,8 +2624,8 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
|
||||
int count;
|
||||
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, match);
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
|
||||
cam_ignore, priority, logic_serv, match);
|
||||
|
||||
if (count < 0) {
|
||||
return count;
|
||||
@@ -2610,7 +2639,7 @@ static int pnv_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
|
||||
static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool cam_ignore, uint8_t priority,
|
||||
bool crowd, bool cam_ignore, uint8_t priority,
|
||||
uint32_t logic_serv,
|
||||
XiveTCTXMatch *match)
|
||||
{
|
||||
@@ -2624,8 +2653,8 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
|
||||
int count;
|
||||
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore,
|
||||
priority, logic_serv, match);
|
||||
count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd,
|
||||
cam_ignore, priority, logic_serv, match);
|
||||
|
||||
if (count < 0) {
|
||||
return count;
|
||||
@@ -2637,6 +2666,24 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format,
|
||||
return total_count;
|
||||
}
|
||||
|
||||
static int pnv10_xive_broadcast(XiveFabric *xfb,
|
||||
uint8_t nvt_blk, uint32_t nvt_idx,
|
||||
bool crowd, bool cam_ignore,
|
||||
uint8_t priority)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(xfb);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
|
||||
XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive);
|
||||
XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr);
|
||||
|
||||
xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pnv_machine_get_big_core(Object *obj, Error **errp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(obj);
|
||||
@@ -2770,6 +2817,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
|
||||
xfc->match_nvt = pnv10_xive_match_nvt;
|
||||
xfc->broadcast = pnv10_xive_broadcast;
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
@@ -251,10 +251,38 @@ static const IPMINetfn hiomap_netfn = {
|
||||
|
||||
void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor)
|
||||
{
|
||||
uint32_t pnor_size = pnor->size;
|
||||
uint32_t pnor_addr = PNOR_SPI_OFFSET;
|
||||
|
||||
if (!pnv_bmc_is_simulator(bmc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The HIOMAP protocol uses block units and 16-bit addressing.
|
||||
* Prevent overflow or misalign.
|
||||
*/
|
||||
if (pnor_addr >= 1U << (BLOCK_SHIFT + 16)) {
|
||||
warn_report("PNOR address is larger than 2^%d, disabling PNOR",
|
||||
BLOCK_SHIFT + 16);
|
||||
return;
|
||||
}
|
||||
if (pnor_addr & ((1U << BLOCK_SHIFT) - 1)) {
|
||||
warn_report("PNOR address is not aligned to 2^%d, disabling PNOR",
|
||||
BLOCK_SHIFT);
|
||||
return;
|
||||
}
|
||||
if (pnor_size > 1U << (BLOCK_SHIFT + 16)) {
|
||||
warn_report("PNOR size is larger than 2^%d, disabling PNOR",
|
||||
BLOCK_SHIFT + 16);
|
||||
return;
|
||||
}
|
||||
if (pnor_size & ((1U << BLOCK_SHIFT) - 1)) {
|
||||
warn_report("PNOR size is not aligned to 2^%d, disabling PNOR",
|
||||
BLOCK_SHIFT);
|
||||
return;
|
||||
}
|
||||
|
||||
object_ref(OBJECT(pnor));
|
||||
object_property_add_const_link(OBJECT(bmc), "pnor", OBJECT(pnor));
|
||||
|
||||
|
||||
+40
-190
@@ -29,94 +29,6 @@
|
||||
#include "hw/ppc/pnv_homer.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
|
||||
|
||||
static bool core_max_array(PnvHomer *homer, hwaddr addr)
|
||||
{
|
||||
int i;
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
|
||||
for (i = 0; i <= homer->chip->nr_cores; i++) {
|
||||
if (addr == (hmrc->core_max_base + i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* P8 Pstate table */
|
||||
|
||||
#define PNV8_OCC_PSTATE_VERSION 0x1f8001
|
||||
#define PNV8_OCC_PSTATE_MIN 0x1f8003
|
||||
#define PNV8_OCC_PSTATE_VALID 0x1f8000
|
||||
#define PNV8_OCC_PSTATE_THROTTLE 0x1f8002
|
||||
#define PNV8_OCC_PSTATE_NOM 0x1f8004
|
||||
#define PNV8_OCC_PSTATE_TURBO 0x1f8005
|
||||
#define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006
|
||||
#define PNV8_OCC_PSTATE_DATA 0x1f8008
|
||||
#define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010
|
||||
#define PNV8_OCC_PSTATE_ID_ONE 0x1f8018
|
||||
#define PNV8_OCC_PSTATE_ID_TWO 0x1f8020
|
||||
#define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012
|
||||
#define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013
|
||||
#define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014
|
||||
#define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c
|
||||
#define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024
|
||||
#define PNV8_CORE_MAX_BASE 0x1f8810
|
||||
|
||||
|
||||
static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
|
||||
switch (addr) {
|
||||
case PNV8_OCC_PSTATE_VERSION:
|
||||
case PNV8_OCC_PSTATE_MIN:
|
||||
case PNV8_OCC_PSTATE_ID_ZERO:
|
||||
return 0;
|
||||
case PNV8_OCC_PSTATE_VALID:
|
||||
case PNV8_OCC_PSTATE_THROTTLE:
|
||||
case PNV8_OCC_PSTATE_NOM:
|
||||
case PNV8_OCC_PSTATE_TURBO:
|
||||
case PNV8_OCC_PSTATE_ID_ONE:
|
||||
case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER:
|
||||
case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER:
|
||||
return 1;
|
||||
case PNV8_OCC_PSTATE_ULTRA_TURBO:
|
||||
case PNV8_OCC_PSTATE_ID_TWO:
|
||||
return 2;
|
||||
case PNV8_OCC_PSTATE_DATA:
|
||||
return 0x1000000000000000;
|
||||
/* P8 frequency for 0, 1, and 2 pstates */
|
||||
case PNV8_OCC_PSTATE_ZERO_FREQUENCY:
|
||||
case PNV8_OCC_PSTATE_ONE_FREQUENCY:
|
||||
case PNV8_OCC_PSTATE_TWO_FREQUENCY:
|
||||
return 3000;
|
||||
}
|
||||
/* pstate table core max array */
|
||||
if (core_max_array(homer, addr)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_power8_homer_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
/* callback function defined to homer write */
|
||||
return;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_power8_homer_ops = {
|
||||
.read = pnv_power8_homer_read,
|
||||
.write = pnv_power8_homer_write,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
/* P8 PBA BARs */
|
||||
#define PBA_BAR0 0x00
|
||||
#define PBA_BAR1 0x01
|
||||
@@ -131,16 +43,16 @@ static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
PnvChip *chip = homer->chip;
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
uint32_t reg = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
case PBA_BAR0:
|
||||
val = PNV_HOMER_BASE(chip);
|
||||
val = homer->base;
|
||||
break;
|
||||
case PBA_BARMASK0: /* P8 homer region mask */
|
||||
val = (PNV_HOMER_SIZE - 1) & 0x300000;
|
||||
val = (hmrc->size - 1) & 0x300000;
|
||||
break;
|
||||
case PBA_BAR3: /* P8 occ common area */
|
||||
val = PNV_OCC_COMMON_AREA_BASE;
|
||||
@@ -172,15 +84,19 @@ static const MemoryRegionOps pnv_homer_power8_pba_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static hwaddr pnv_homer_power8_get_base(PnvChip *chip)
|
||||
{
|
||||
return PNV_HOMER_BASE(chip);
|
||||
}
|
||||
|
||||
static void pnv_homer_power8_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
|
||||
|
||||
homer->get_base = pnv_homer_power8_get_base;
|
||||
homer->size = PNV_HOMER_SIZE;
|
||||
homer->pba_size = PNV_XSCOM_PBA_SIZE;
|
||||
homer->pba_ops = &pnv_homer_power8_pba_ops;
|
||||
homer->homer_size = PNV_HOMER_SIZE;
|
||||
homer->homer_ops = &pnv_power8_homer_ops;
|
||||
homer->core_max_base = PNV8_CORE_MAX_BASE;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_homer_power8_type_info = {
|
||||
@@ -190,100 +106,20 @@ static const TypeInfo pnv_homer_power8_type_info = {
|
||||
.class_init = pnv_homer_power8_class_init,
|
||||
};
|
||||
|
||||
/* P9 Pstate table */
|
||||
|
||||
#define PNV9_OCC_PSTATE_ID_ZERO 0xe2018
|
||||
#define PNV9_OCC_PSTATE_ID_ONE 0xe2020
|
||||
#define PNV9_OCC_PSTATE_ID_TWO 0xe2028
|
||||
#define PNV9_OCC_PSTATE_DATA 0xe2000
|
||||
#define PNV9_OCC_PSTATE_DATA_AREA 0xe2008
|
||||
#define PNV9_OCC_PSTATE_MIN 0xe2003
|
||||
#define PNV9_OCC_PSTATE_NOM 0xe2004
|
||||
#define PNV9_OCC_PSTATE_TURBO 0xe2005
|
||||
#define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818
|
||||
#define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006
|
||||
#define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001
|
||||
#define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85
|
||||
#define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008
|
||||
#define PNV9_CHIP_HOMER_BASE 0x0
|
||||
#define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c
|
||||
#define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024
|
||||
#define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c
|
||||
#define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002
|
||||
#define PNV9_CORE_MAX_BASE 0xe2819
|
||||
|
||||
|
||||
static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
|
||||
switch (addr) {
|
||||
case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO:
|
||||
case PNV9_OCC_PSTATE_ID_ZERO:
|
||||
return 0;
|
||||
case PNV9_OCC_PSTATE_DATA:
|
||||
case PNV9_OCC_ROLE_MASTER_OR_SLAVE:
|
||||
case PNV9_OCC_PSTATE_NOM:
|
||||
case PNV9_OCC_PSTATE_TURBO:
|
||||
case PNV9_OCC_PSTATE_ID_ONE:
|
||||
case PNV9_OCC_PSTATE_ULTRA_TURBO:
|
||||
case PNV9_OCC_OPAL_RUNTIME_DATA:
|
||||
return 1;
|
||||
case PNV9_OCC_PSTATE_MIN:
|
||||
case PNV9_OCC_PSTATE_ID_TWO:
|
||||
return 2;
|
||||
|
||||
/* 3000 khz frequency for 0, 1, and 2 pstates */
|
||||
case PNV9_OCC_PSTATE_ZERO_FREQUENCY:
|
||||
case PNV9_OCC_PSTATE_ONE_FREQUENCY:
|
||||
case PNV9_OCC_PSTATE_TWO_FREQUENCY:
|
||||
return 3000;
|
||||
case PNV9_OCC_PSTATE_MAJOR_VERSION:
|
||||
return 0x90;
|
||||
case PNV9_CHIP_HOMER_BASE:
|
||||
case PNV9_OCC_PSTATE_DATA_AREA:
|
||||
case PNV9_CHIP_HOMER_IMAGE_POINTER:
|
||||
return 0x1000000000000000;
|
||||
}
|
||||
/* pstate table core max array */
|
||||
if (core_max_array(homer, addr)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_power9_homer_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
/* callback function defined to homer write */
|
||||
return;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_power9_homer_ops = {
|
||||
.read = pnv_power9_homer_read,
|
||||
.write = pnv_power9_homer_write,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
PnvChip *chip = homer->chip;
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
uint32_t reg = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
case PBA_BAR0:
|
||||
val = PNV9_HOMER_BASE(chip);
|
||||
val = homer->base;
|
||||
break;
|
||||
case PBA_BARMASK0: /* P9 homer region mask */
|
||||
val = (PNV9_HOMER_SIZE - 1) & 0x300000;
|
||||
val = (hmrc->size - 1) & 0x300000;
|
||||
break;
|
||||
case PBA_BAR2: /* P9 occ common area */
|
||||
val = PNV9_OCC_COMMON_AREA_BASE;
|
||||
@@ -315,15 +151,19 @@ static const MemoryRegionOps pnv_homer_power9_pba_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static hwaddr pnv_homer_power9_get_base(PnvChip *chip)
|
||||
{
|
||||
return PNV9_HOMER_BASE(chip);
|
||||
}
|
||||
|
||||
static void pnv_homer_power9_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
|
||||
|
||||
homer->get_base = pnv_homer_power9_get_base;
|
||||
homer->size = PNV_HOMER_SIZE;
|
||||
homer->pba_size = PNV9_XSCOM_PBA_SIZE;
|
||||
homer->pba_ops = &pnv_homer_power9_pba_ops;
|
||||
homer->homer_size = PNV9_HOMER_SIZE;
|
||||
homer->homer_ops = &pnv_power9_homer_ops;
|
||||
homer->core_max_base = PNV9_CORE_MAX_BASE;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_homer_power9_type_info = {
|
||||
@@ -337,16 +177,16 @@ static uint64_t pnv_homer_power10_pba_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(opaque);
|
||||
PnvChip *chip = homer->chip;
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
uint32_t reg = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (reg) {
|
||||
case PBA_BAR0:
|
||||
val = PNV10_HOMER_BASE(chip);
|
||||
val = homer->base;
|
||||
break;
|
||||
case PBA_BARMASK0: /* P10 homer region mask */
|
||||
val = (PNV10_HOMER_SIZE - 1) & 0x300000;
|
||||
val = (hmrc->size - 1) & 0x300000;
|
||||
break;
|
||||
case PBA_BAR2: /* P10 occ common area */
|
||||
val = PNV10_OCC_COMMON_AREA_BASE;
|
||||
@@ -378,15 +218,19 @@ static const MemoryRegionOps pnv_homer_power10_pba_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static hwaddr pnv_homer_power10_get_base(PnvChip *chip)
|
||||
{
|
||||
return PNV10_HOMER_BASE(chip);
|
||||
}
|
||||
|
||||
static void pnv_homer_power10_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvHomerClass *homer = PNV_HOMER_CLASS(klass);
|
||||
|
||||
homer->get_base = pnv_homer_power10_get_base;
|
||||
homer->size = PNV_HOMER_SIZE;
|
||||
homer->pba_size = PNV10_XSCOM_PBA_SIZE;
|
||||
homer->pba_ops = &pnv_homer_power10_pba_ops;
|
||||
homer->homer_size = PNV10_HOMER_SIZE;
|
||||
homer->homer_ops = &pnv_power9_homer_ops; /* TODO */
|
||||
homer->core_max_base = PNV9_CORE_MAX_BASE;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_homer_power10_type_info = {
|
||||
@@ -400,16 +244,22 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvHomer *homer = PNV_HOMER(dev);
|
||||
PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer);
|
||||
char homer_str[32];
|
||||
|
||||
assert(homer->chip);
|
||||
|
||||
pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops,
|
||||
homer, "xscom-pba", hmrc->pba_size);
|
||||
|
||||
/* homer region */
|
||||
memory_region_init_io(&homer->regs, OBJECT(dev),
|
||||
hmrc->homer_ops, homer, "homer-main-memory",
|
||||
hmrc->homer_size);
|
||||
/* Homer RAM region */
|
||||
homer->base = hmrc->get_base(homer->chip);
|
||||
|
||||
snprintf(homer_str, sizeof(homer_str), "homer-chip%d-memory",
|
||||
homer->chip->chip_id);
|
||||
if (!memory_region_init_ram(&homer->mem, OBJECT(homer),
|
||||
homer_str, hmrc->size, errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const Property pnv_homer_properties[] = {
|
||||
|
||||
+50
-39
@@ -85,7 +85,7 @@ enum {
|
||||
|
||||
#define ISA_IO_SIZE 0x00010000
|
||||
#define ISA_MEM_SIZE 0x10000000
|
||||
#define ISA_FW_SIZE 0x10000000
|
||||
#define ISA_FW_SIZE 0x100000000
|
||||
#define LPC_IO_OPB_ADDR 0xd0010000
|
||||
#define LPC_IO_OPB_SIZE 0x00010000
|
||||
#define LPC_MEM_OPB_ADDR 0xe0000000
|
||||
@@ -353,6 +353,8 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_lpc_opb_noresponse(PnvLpcController *lpc);
|
||||
|
||||
static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
PnvLpcController *lpc = PNV_LPC(opaque);
|
||||
@@ -376,6 +378,7 @@ static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
}
|
||||
|
||||
if (result != MEMTX_OK) {
|
||||
pnv_lpc_opb_noresponse(lpc);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
|
||||
HWADDR_PRIx "\n", addr);
|
||||
}
|
||||
@@ -406,6 +409,7 @@ static void pnv_lpc_mmio_write(void *opaque, hwaddr addr,
|
||||
}
|
||||
|
||||
if (result != MEMTX_OK) {
|
||||
pnv_lpc_opb_noresponse(lpc);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
|
||||
HWADDR_PRIx "\n", addr);
|
||||
}
|
||||
@@ -456,46 +460,18 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
|
||||
{
|
||||
uint32_t active_irqs = 0;
|
||||
|
||||
if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) {
|
||||
qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: "
|
||||
"0x%08"PRIx32"\n", lpc->lpc_hc_irqstat);
|
||||
}
|
||||
|
||||
if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
|
||||
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
|
||||
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
|
||||
if (!(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN)) {
|
||||
active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
|
||||
}
|
||||
|
||||
/* Reflect the interrupt */
|
||||
if (!lpc->psi_has_serirq) {
|
||||
if (lpc->psi_has_serirq) {
|
||||
/*
|
||||
* POWER8 ORs all irqs together (also with LPCHC internal interrupt
|
||||
* sources) and outputs a single line that raises the PSI LPCHC irq
|
||||
* which then latches an OPB IRQ status register that sends the irq
|
||||
* to PSI.
|
||||
*
|
||||
* We don't honor the polarity register, it's pointless and unused
|
||||
* anyway
|
||||
*/
|
||||
if (active_irqs) {
|
||||
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
|
||||
} else {
|
||||
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
|
||||
}
|
||||
|
||||
/* Update OPB internal latch */
|
||||
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
|
||||
|
||||
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
|
||||
} else {
|
||||
/*
|
||||
* POWER9 and POWER10 have routing fields in OPB master registers that
|
||||
* POWER9 and later have routing fields in OPB master registers that
|
||||
* send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs.
|
||||
* These don't appear to get latched into an OPB register like the
|
||||
* LPCHC irqs.
|
||||
*
|
||||
* POWER9 LPC controller internal irqs still go via the OPB
|
||||
* and LPCHC PSI irqs like P8, but we have no such internal sources
|
||||
* modelled yet.
|
||||
*/
|
||||
bool serirq_out[4] = { false, false, false, false };
|
||||
int irq;
|
||||
@@ -510,7 +486,39 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
|
||||
qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]);
|
||||
qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]);
|
||||
qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]);
|
||||
|
||||
/*
|
||||
* POWER9 and later LPC controller internal irqs still go via the OPB
|
||||
* and LPCHC PSI irqs like P8, so take the SERIRQs out and continue.
|
||||
*/
|
||||
active_irqs &= ~LPC_HC_IRQ_SERIRQ_ALL;
|
||||
}
|
||||
|
||||
/*
|
||||
* POWER8 ORs all irqs together (also with LPCHC internal interrupt
|
||||
* sources) and outputs a single line that raises the PSI LPCHC irq
|
||||
* which then latches an OPB IRQ status register that sends the irq
|
||||
* to PSI.
|
||||
*
|
||||
* We don't honor the polarity register, it's pointless and unused
|
||||
* anyway
|
||||
*/
|
||||
if (active_irqs) {
|
||||
lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC;
|
||||
} else {
|
||||
lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC;
|
||||
}
|
||||
|
||||
/* Update OPB internal latch */
|
||||
lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
|
||||
|
||||
qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0);
|
||||
}
|
||||
|
||||
static void pnv_lpc_opb_noresponse(PnvLpcController *lpc)
|
||||
{
|
||||
lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SYNC_NORESP_ERR;
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
}
|
||||
|
||||
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
@@ -553,10 +561,13 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
|
||||
switch (addr) {
|
||||
case LPC_HC_FW_SEG_IDSEL:
|
||||
/* XXX Actually figure out how that works as this impact
|
||||
* memory regions/aliases
|
||||
/*
|
||||
* ISA FW "devices" are modeled as 16x256MB windows into a
|
||||
* 4GB LPC FW address space.
|
||||
*/
|
||||
val &= 0xf; /* Selects device 0-15 */
|
||||
lpc->lpc_hc_fw_seg_idsel = val;
|
||||
memory_region_set_alias_offset(&lpc->opb_isa_fw, val * LPC_FW_OPB_SIZE);
|
||||
break;
|
||||
case LPC_HC_FW_RD_ACC_SIZE:
|
||||
lpc->lpc_hc_fw_rd_acc_size = val;
|
||||
@@ -790,9 +801,9 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull);
|
||||
address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb");
|
||||
|
||||
/* Create ISA IO and Mem space regions which are the root of
|
||||
* the ISA bus (ie, ISA address spaces). We don't create a
|
||||
* separate one for FW which we alias to memory.
|
||||
/*
|
||||
* Create ISA IO, Mem, and FW space regions which are the root of
|
||||
* the ISA bus (ie, ISA address spaces).
|
||||
*/
|
||||
memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE);
|
||||
memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user