mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-ppc-for-9.1-2-20240726-1' of https://gitlab.com/npiggin/qemu into staging
fixes # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEETkN92lZhb0MpsKeVZ7MCdqhiHK4FAmai5TsACgkQZ7MCdqhi # HK4rgA//eh0ax3JnBGma1rVEDL5n5cdEYV+ATFYGc529CUZFUar3IMqSw3in8bJy # uvQ6Cr/7IuusNEtoiYtdN1yNasqsm3fZB/hZ/Ekz32TsbpBRdkJW3ucavAu2rGM/ # EKRo7Y8gciy/Mj9y2JlIZqsDqYe+gribfGQvIg27DX+caAW/lKQdAdt4oJMTSdmr # XR8JjtMdhUazKrI+bc/4EG6tIQyUdp+S1/z1q6Wthqt58dNRElTjkD9op4AsUWMu # CE4a8ALCZoj3P3m+xf7xi7fT2JC2xgmNRCi3KbbhVEHdbFB6ViNYNuEYRS6GmpdC # C6J/ZR6QXs6KB1KO7EyB+vsuxLX4Eb8aeCFxwMlzJ9Fo4g8JudABXOFzYTKX1xBn # DUIGX91YACV43M2MvP/KuEU4zWpREO+U8MbQs/6s6fYsnCO2eKVJt/0Aaf1hmk37 # gY5Ak2DRx5TBvxlFy87zgHxHWTh/dGZodpN3IvCIDzVLnHGFlfluJbFRaoZSOecb # 1vxDHORjIruLcAxNVEGkJ/6MxOrnjjoUzSPUQcbgJ5BpFZOdeGLiMAULu/HBLBd9 # 7dvVw+PeNEPJttYumljOD6nYc/jENhLQsvkc3++bwGNc/rpi4YngtB4jhT1HV2Cl # oLool2ooKZgV4qx6IzeYo9feElvWVNK5XPzqDpSDlt9MaI+yTYM= # =FxPm # -----END PGP SIGNATURE----- # gpg: Signature made Fri 26 Jul 2024 09:52:27 AM AEST # gpg: using RSA key 4E437DDA56616F4329B0A79567B30276A8621CAE # gpg: Good signature from "Nicholas Piggin <npiggin@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: 4E43 7DDA 5661 6F43 29B0 A795 67B3 0276 A862 1CAE * tag 'pull-ppc-for-9.1-2-20240726-1' of https://gitlab.com/npiggin/qemu: (96 commits) target/ppc: Remove includes from mmu-book3s-v3.h target/ppc/mmu-radix64: Remove externally unused parts from header target/ppc: Unexport some functions from mmu-book3s-v3.h target/ppc/mmu-hash32.c: Move get_pteg_offset32() to the header target/ppc/mmu-hash32.c: Inline and remove ppc_hash32_pte_raddr() target/ppc/mmu_common.c: Remove mmu_ctx_t target/ppc/mmu_common.c: Stop using ctx in get_bat_6xx_tlb() target/ppc: Remove bat_size_prot() target/ppc/mmu_common.c: Use defines instead of numeric constants target/ppc/mmu_common.c: Rename function parameter target/ppc/mmu_common.c: Stop using ctx in ppc6xx_tlb_check() target/ppc/mmu_common.c: Remove key field from mmu_ctx_t target/ppc/mmu_common.c: Init variable in function that relies on it target/ppc/mmu-hash32.c: Inline and remove ppc_hash32_pte_prot() target/ppc: Add function to get protection key for hash32 MMU target/ppc/mmu_common.c: Remove ptem field from mmu_ctx_t target/ppc/mmu_common.c: Inline and remove ppc6xx_tlb_pte_check() target/ppc/mmu_common.c: Simplify a switch statement target/ppc/mmu_common.c: Remove single use local variable target/ppc/mmu_common.c: Convert local variable to bool ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -398,6 +398,18 @@ int kvm_create_vcpu(CPUState *cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_create_and_park_vcpu(CPUState *cpu)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = kvm_create_vcpu(cpu);
|
||||
if (!ret) {
|
||||
kvm_park_vcpu(cpu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
|
||||
+4
-3
@@ -57,14 +57,12 @@ void cpu_list_unlock(void)
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
static bool cpu_index_auto_assigned;
|
||||
|
||||
static int cpu_get_free_index(void)
|
||||
int cpu_get_free_index(void)
|
||||
{
|
||||
CPUState *some_cpu;
|
||||
int max_cpu_index = 0;
|
||||
|
||||
cpu_index_auto_assigned = true;
|
||||
CPU_FOREACH(some_cpu) {
|
||||
if (some_cpu->cpu_index >= max_cpu_index) {
|
||||
max_cpu_index = some_cpu->cpu_index + 1;
|
||||
@@ -83,8 +81,11 @@ unsigned int cpu_list_generation_id_get(void)
|
||||
|
||||
void cpu_list_add(CPUState *cpu)
|
||||
{
|
||||
static bool cpu_index_auto_assigned;
|
||||
|
||||
QEMU_LOCK_GUARD(&qemu_cpu_list_lock);
|
||||
if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) {
|
||||
cpu_index_auto_assigned = true;
|
||||
cpu->cpu_index = cpu_get_free_index();
|
||||
assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX);
|
||||
} else {
|
||||
|
||||
@@ -357,6 +357,9 @@ static const FlashPartInfo known_devices[] = {
|
||||
.sfdp_read = m25p80_sfdp_w25q512jv },
|
||||
{ INFO("w25q01jvq", 0xef4021, 0, 64 << 10, 2048, ER_4K),
|
||||
.sfdp_read = m25p80_sfdp_w25q01jvq },
|
||||
|
||||
/* Microchip */
|
||||
{ INFO("25csm04", 0x29cc00, 0x100, 64 << 10, 8, 0) },
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
||||
+475
-91
File diff suppressed because it is too large
Load Diff
@@ -232,6 +232,10 @@
|
||||
#define VC_ESBC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35)
|
||||
#define VC_ESBC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */
|
||||
|
||||
/* ESBC cache flush inject register */
|
||||
#define X_VC_ESBC_FLUSH_INJECT 0x142
|
||||
#define VC_ESBC_FLUSH_INJECT 0x210
|
||||
|
||||
/* ESBC configuration */
|
||||
#define X_VC_ESBC_CFG 0x148
|
||||
#define VC_ESBC_CFG 0x240
|
||||
@@ -250,6 +254,10 @@
|
||||
#define VC_EASC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35)
|
||||
#define VC_EASC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */
|
||||
|
||||
/* EASC flush inject register */
|
||||
#define X_VC_EASC_FLUSH_INJECT 0x162
|
||||
#define VC_EASC_FLUSH_INJECT 0x310
|
||||
|
||||
/*
|
||||
* VC2
|
||||
*/
|
||||
@@ -270,6 +278,10 @@
|
||||
#define VC_ENDC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39)
|
||||
#define VC_ENDC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */
|
||||
|
||||
/* ENDC flush inject register */
|
||||
#define X_VC_ENDC_FLUSH_INJECT 0x182
|
||||
#define VC_ENDC_FLUSH_INJECT 0x410
|
||||
|
||||
/* ENDC Sync done */
|
||||
#define X_VC_ENDC_SYNC_DONE 0x184
|
||||
#define VC_ENDC_SYNC_DONE 0x420
|
||||
@@ -283,6 +295,15 @@
|
||||
#define VC_ENDC_SYNC_QUEUE_HARD PPC_BIT(6)
|
||||
#define VC_QUEUE_COUNT 7
|
||||
|
||||
/* ENDC cache watch assign */
|
||||
#define X_VC_ENDC_WATCH_ASSIGN 0x186
|
||||
#define VC_ENDC_WATCH_ASSIGN 0x430
|
||||
|
||||
/* ENDC configuration register */
|
||||
#define X_VC_ENDC_CFG 0x188
|
||||
#define VC_ENDC_CFG 0x440
|
||||
#define VC_ENDC_CFG_CACHE_WATCH_ASSIGN PPC_BITMASK(32, 35)
|
||||
|
||||
/* ENDC cache watch specification 0 */
|
||||
#define X_VC_ENDC_WATCH0_SPEC 0x1A0
|
||||
#define VC_ENDC_WATCH0_SPEC 0x500
|
||||
@@ -302,6 +323,42 @@
|
||||
#define VC_ENDC_WATCH0_DATA2 0x530
|
||||
#define VC_ENDC_WATCH0_DATA3 0x538
|
||||
|
||||
/* ENDC cache watch 1 */
|
||||
#define X_VC_ENDC_WATCH1_SPEC 0x1A8
|
||||
#define VC_ENDC_WATCH1_SPEC 0x540
|
||||
#define X_VC_ENDC_WATCH1_DATA0 0x1AC
|
||||
#define X_VC_ENDC_WATCH1_DATA1 0x1AD
|
||||
#define X_VC_ENDC_WATCH1_DATA2 0x1AE
|
||||
#define X_VC_ENDC_WATCH1_DATA3 0x1AF
|
||||
#define VC_ENDC_WATCH1_DATA0 0x560
|
||||
#define VC_ENDC_WATCH1_DATA1 0x568
|
||||
#define VC_ENDC_WATCH1_DATA2 0x570
|
||||
#define VC_ENDC_WATCH1_DATA3 0x578
|
||||
|
||||
/* ENDC cache watch 2 */
|
||||
#define X_VC_ENDC_WATCH2_SPEC 0x1B0
|
||||
#define VC_ENDC_WATCH2_SPEC 0x580
|
||||
#define X_VC_ENDC_WATCH2_DATA0 0x1B4
|
||||
#define X_VC_ENDC_WATCH2_DATA1 0x1B5
|
||||
#define X_VC_ENDC_WATCH2_DATA2 0x1B6
|
||||
#define X_VC_ENDC_WATCH2_DATA3 0x1B7
|
||||
#define VC_ENDC_WATCH2_DATA0 0x5A0
|
||||
#define VC_ENDC_WATCH2_DATA1 0x5A8
|
||||
#define VC_ENDC_WATCH2_DATA2 0x5B0
|
||||
#define VC_ENDC_WATCH2_DATA3 0x5B8
|
||||
|
||||
/* ENDC cache watch 3 */
|
||||
#define X_VC_ENDC_WATCH3_SPEC 0x1B8
|
||||
#define VC_ENDC_WATCH3_SPEC 0x5C0
|
||||
#define X_VC_ENDC_WATCH3_DATA0 0x1BC
|
||||
#define X_VC_ENDC_WATCH3_DATA1 0x1BD
|
||||
#define X_VC_ENDC_WATCH3_DATA2 0x1BE
|
||||
#define X_VC_ENDC_WATCH3_DATA3 0x1BF
|
||||
#define VC_ENDC_WATCH3_DATA0 0x5E0
|
||||
#define VC_ENDC_WATCH3_DATA1 0x5E8
|
||||
#define VC_ENDC_WATCH3_DATA2 0x5F0
|
||||
#define VC_ENDC_WATCH3_DATA3 0x5F8
|
||||
|
||||
/*
|
||||
* PC LSB1
|
||||
*/
|
||||
@@ -358,6 +415,21 @@
|
||||
#define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39)
|
||||
#define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */
|
||||
|
||||
/* NxC Cache flush inject */
|
||||
#define X_PC_NXC_FLUSH_INJECT 0x282
|
||||
#define PC_NXC_FLUSH_INJECT 0x410
|
||||
|
||||
/* NxC Cache watch assign */
|
||||
#define X_PC_NXC_WATCH_ASSIGN 0x286
|
||||
#define PC_NXC_WATCH_ASSIGN 0x430
|
||||
|
||||
/* NxC Proc config */
|
||||
#define X_PC_NXC_PROC_CONFIG 0x28A
|
||||
#define PC_NXC_PROC_CONFIG 0x450
|
||||
#define PC_NXC_PROC_CONFIG_WATCH_ASSIGN PPC_BITMASK(0, 3)
|
||||
#define PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS PPC_BITMASK(32, 35)
|
||||
#define PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS PPC_BITMASK(36, 39)
|
||||
|
||||
/* NxC Cache Watch 0 Specification */
|
||||
#define X_PC_NXC_WATCH0_SPEC 0x2A0
|
||||
#define PC_NXC_WATCH0_SPEC 0x500
|
||||
@@ -381,6 +453,42 @@
|
||||
#define PC_NXC_WATCH0_DATA2 0x530
|
||||
#define PC_NXC_WATCH0_DATA3 0x538
|
||||
|
||||
/* NxC Cache Watch 1 */
|
||||
#define X_PC_NXC_WATCH1_SPEC 0x2A8
|
||||
#define PC_NXC_WATCH1_SPEC 0x540
|
||||
#define X_PC_NXC_WATCH1_DATA0 0x2AC
|
||||
#define X_PC_NXC_WATCH1_DATA1 0x2AD
|
||||
#define X_PC_NXC_WATCH1_DATA2 0x2AE
|
||||
#define X_PC_NXC_WATCH1_DATA3 0x2AF
|
||||
#define PC_NXC_WATCH1_DATA0 0x560
|
||||
#define PC_NXC_WATCH1_DATA1 0x568
|
||||
#define PC_NXC_WATCH1_DATA2 0x570
|
||||
#define PC_NXC_WATCH1_DATA3 0x578
|
||||
|
||||
/* NxC Cache Watch 2 */
|
||||
#define X_PC_NXC_WATCH2_SPEC 0x2B0
|
||||
#define PC_NXC_WATCH2_SPEC 0x580
|
||||
#define X_PC_NXC_WATCH2_DATA0 0x2B4
|
||||
#define X_PC_NXC_WATCH2_DATA1 0x2B5
|
||||
#define X_PC_NXC_WATCH2_DATA2 0x2B6
|
||||
#define X_PC_NXC_WATCH2_DATA3 0x2B7
|
||||
#define PC_NXC_WATCH2_DATA0 0x5A0
|
||||
#define PC_NXC_WATCH2_DATA1 0x5A8
|
||||
#define PC_NXC_WATCH2_DATA2 0x5B0
|
||||
#define PC_NXC_WATCH2_DATA3 0x5B8
|
||||
|
||||
/* NxC Cache Watch 3 */
|
||||
#define X_PC_NXC_WATCH3_SPEC 0x2B8
|
||||
#define PC_NXC_WATCH3_SPEC 0x5C0
|
||||
#define X_PC_NXC_WATCH3_DATA0 0x2BC
|
||||
#define X_PC_NXC_WATCH3_DATA1 0x2BD
|
||||
#define X_PC_NXC_WATCH3_DATA2 0x2BE
|
||||
#define X_PC_NXC_WATCH3_DATA3 0x2BF
|
||||
#define PC_NXC_WATCH3_DATA0 0x5E0
|
||||
#define PC_NXC_WATCH3_DATA1 0x5E8
|
||||
#define PC_NXC_WATCH3_DATA2 0x5F0
|
||||
#define PC_NXC_WATCH3_DATA3 0x5F8
|
||||
|
||||
/*
|
||||
* TCTXT Registers
|
||||
*/
|
||||
|
||||
+9
-3
@@ -692,9 +692,15 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf)
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, "CPU[%04x]: "
|
||||
"QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR W2\n",
|
||||
cpu_index);
|
||||
if (xive_presenter_get_config(tctx->xptr) & XIVE_PRESENTER_GEN1_TIMA_OS) {
|
||||
g_string_append_printf(buf, "CPU[%04x]: "
|
||||
"QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR"
|
||||
" W2\n", cpu_index);
|
||||
} else {
|
||||
g_string_append_printf(buf, "CPU[%04x]: "
|
||||
"QW NSR CPPR IPB LSMFB - LGS T PIPR"
|
||||
" W2\n", cpu_index);
|
||||
}
|
||||
|
||||
for (i = 0; i < XIVE_TM_RING_COUNT; i++) {
|
||||
char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]);
|
||||
|
||||
+31
-2
@@ -89,7 +89,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf)
|
||||
pq = xive_get_field32(END2_W1_ESn, end->w1);
|
||||
|
||||
g_string_append_printf(buf,
|
||||
" %08x %c%c %c%c%c%c%c%c%c%c%c%c "
|
||||
" %08x %c%c %c%c%c%c%c%c%c%c%c%c%c %c%c "
|
||||
"prio:%d nvp:%02x/%04x",
|
||||
end_idx,
|
||||
pq & XIVE_ESB_VAL_P ? 'P' : '-',
|
||||
@@ -98,12 +98,15 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf)
|
||||
xive2_end_is_enqueue(end) ? 'q' : '-',
|
||||
xive2_end_is_notify(end) ? 'n' : '-',
|
||||
xive2_end_is_backlog(end) ? 'b' : '-',
|
||||
xive2_end_is_precluded_escalation(end) ? 'p' : '-',
|
||||
xive2_end_is_escalate(end) ? 'e' : '-',
|
||||
xive2_end_is_escalate_end(end) ? 'N' : '-',
|
||||
xive2_end_is_uncond_escalation(end) ? 'u' : '-',
|
||||
xive2_end_is_silent_escalation(end) ? 's' : '-',
|
||||
xive2_end_is_firmware1(end) ? 'f' : '-',
|
||||
xive2_end_is_firmware2(end) ? 'F' : '-',
|
||||
xive2_end_is_ignore(end) ? 'i' : '-',
|
||||
xive2_end_is_crowd(end) ? 'c' : '-',
|
||||
priority, nvp_blk, nvp_idx);
|
||||
|
||||
if (qaddr_base) {
|
||||
@@ -137,6 +140,32 @@ void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx,
|
||||
(uint32_t) xive_get_field64(EAS2_END_DATA, eas->w));
|
||||
}
|
||||
|
||||
void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf)
|
||||
{
|
||||
uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5);
|
||||
uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5);
|
||||
|
||||
if (!xive2_nvp_is_valid(nvp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x",
|
||||
nvp_idx, eq_blk, eq_idx,
|
||||
xive_get_field32(NVP2_W2_IPB, nvp->w2));
|
||||
/*
|
||||
* When the NVP is HW controlled, more fields are updated
|
||||
*/
|
||||
if (xive2_nvp_is_hw(nvp)) {
|
||||
g_string_append_printf(buf, " CPPR:%02x",
|
||||
xive_get_field32(NVP2_W2_CPPR, nvp->w2));
|
||||
if (xive2_nvp_is_co(nvp)) {
|
||||
g_string_append_printf(buf, " CO:%04x",
|
||||
xive_get_field32(NVP2_W1_CO_THRID, nvp->w1));
|
||||
}
|
||||
}
|
||||
g_string_append_c(buf, '\n');
|
||||
}
|
||||
|
||||
static void xive2_end_enqueue(Xive2End *end, uint32_t data)
|
||||
{
|
||||
uint64_t qaddr_base = xive2_end_qaddr(end);
|
||||
@@ -650,7 +679,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk,
|
||||
}
|
||||
|
||||
found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx,
|
||||
xive_get_field32(END2_W6_IGNORE, end.w7),
|
||||
xive2_end_is_ignore(&end),
|
||||
priority,
|
||||
xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7));
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ config POWERNV
|
||||
select PCI_POWERNV
|
||||
select PCA9552
|
||||
select PCA9554
|
||||
select SSI
|
||||
select SSI_M25P80
|
||||
select PNV_SPI
|
||||
|
||||
config PPC405
|
||||
bool
|
||||
|
||||
@@ -42,6 +42,7 @@ endif
|
||||
ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
|
||||
'pnv.c',
|
||||
'pnv_xscom.c',
|
||||
'pnv_adu.c',
|
||||
'pnv_core.c',
|
||||
'pnv_i2c.c',
|
||||
'pnv_lpc.c',
|
||||
|
||||
+322
-63
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* QEMU PowerPC PowerNV ADU unit
|
||||
*
|
||||
* The ADU unit actually implements XSCOM, which is the bridge between MMIO
|
||||
* and PIB. However it also includes control and status registers and other
|
||||
* functions that are exposed as PIB (xscom) registers.
|
||||
*
|
||||
* To keep things simple, pnv_xscom.c remains the XSCOM bridge
|
||||
* implementation, and pnv_adu.c implements the ADU registers and other
|
||||
* functions.
|
||||
*
|
||||
* Copyright (c) 2024, IBM Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_adu.h"
|
||||
#include "hw/ppc/pnv_chip.h"
|
||||
#include "hw/ppc/pnv_lpc.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define ADU_LPC_BASE_REG 0x40
|
||||
#define ADU_LPC_CMD_REG 0x41
|
||||
#define ADU_LPC_DATA_REG 0x42
|
||||
#define ADU_LPC_STATUS_REG 0x43
|
||||
|
||||
static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width)
|
||||
{
|
||||
PnvADU *adu = PNV_ADU(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (offset) {
|
||||
case 0x18: /* Receive status reg */
|
||||
case 0x12: /* log register */
|
||||
case 0x13: /* error register */
|
||||
break;
|
||||
case ADU_LPC_BASE_REG:
|
||||
/*
|
||||
* LPC Address Map in Pervasive ADU Workbook
|
||||
*
|
||||
* return PNV10_LPCM_BASE(chip) & PPC_BITMASK(8, 31);
|
||||
* XXX: implement as class property, or get from LPC?
|
||||
*/
|
||||
qemu_log_mask(LOG_UNIMP, "ADU: LPC_BASE_REG is not implemented\n");
|
||||
break;
|
||||
case ADU_LPC_CMD_REG:
|
||||
val = adu->lpc_cmd_reg;
|
||||
break;
|
||||
case ADU_LPC_DATA_REG:
|
||||
val = adu->lpc_data_reg;
|
||||
break;
|
||||
case ADU_LPC_STATUS_REG:
|
||||
val = PPC_BIT(0); /* ack / done */
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "ADU Unimplemented read register: Ox%08x\n",
|
||||
offset);
|
||||
}
|
||||
|
||||
trace_pnv_adu_xscom_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static bool lpc_cmd_read(PnvADU *adu)
|
||||
{
|
||||
return !!(adu->lpc_cmd_reg & PPC_BIT(0));
|
||||
}
|
||||
|
||||
static bool lpc_cmd_write(PnvADU *adu)
|
||||
{
|
||||
return !lpc_cmd_read(adu);
|
||||
}
|
||||
|
||||
static uint32_t lpc_cmd_addr(PnvADU *adu)
|
||||
{
|
||||
return (adu->lpc_cmd_reg & PPC_BITMASK(32, 63)) >> PPC_BIT_NR(63);
|
||||
}
|
||||
|
||||
static uint32_t lpc_cmd_size(PnvADU *adu)
|
||||
{
|
||||
return (adu->lpc_cmd_reg & PPC_BITMASK(5, 11)) >> PPC_BIT_NR(11);
|
||||
}
|
||||
|
||||
static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned width)
|
||||
{
|
||||
PnvADU *adu = PNV_ADU(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
trace_pnv_adu_xscom_write(addr, val);
|
||||
|
||||
switch (offset) {
|
||||
case 0x18: /* Receive status reg */
|
||||
case 0x12: /* log register */
|
||||
case 0x13: /* error register */
|
||||
break;
|
||||
|
||||
case ADU_LPC_BASE_REG:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"ADU: Changing LPC_BASE_REG is not implemented\n");
|
||||
break;
|
||||
|
||||
case ADU_LPC_CMD_REG:
|
||||
adu->lpc_cmd_reg = val;
|
||||
if (lpc_cmd_read(adu)) {
|
||||
uint32_t lpc_addr = lpc_cmd_addr(adu);
|
||||
uint32_t lpc_size = lpc_cmd_size(adu);
|
||||
uint64_t data = 0;
|
||||
|
||||
pnv_lpc_opb_read(adu->lpc, lpc_addr, (void *)&data, lpc_size);
|
||||
|
||||
/*
|
||||
* ADU access is performed within 8-byte aligned sectors. Smaller
|
||||
* access sizes don't get formatted to the least significant byte,
|
||||
* but rather appear in the data reg at the same offset as the
|
||||
* address in memory. This shifts them into that position.
|
||||
*/
|
||||
adu->lpc_data_reg = be64_to_cpu(data) >> ((lpc_addr & 7) * 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case ADU_LPC_DATA_REG:
|
||||
adu->lpc_data_reg = val;
|
||||
if (lpc_cmd_write(adu)) {
|
||||
uint32_t lpc_addr = lpc_cmd_addr(adu);
|
||||
uint32_t lpc_size = lpc_cmd_size(adu);
|
||||
uint64_t data;
|
||||
|
||||
data = cpu_to_be64(val) >> ((lpc_addr & 7) * 8); /* See above */
|
||||
pnv_lpc_opb_write(adu->lpc, lpc_addr, (void *)&data, lpc_size);
|
||||
}
|
||||
break;
|
||||
|
||||
case ADU_LPC_STATUS_REG:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"ADU: Changing LPC_STATUS_REG is not implemented\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "ADU Unimplemented write register: Ox%08x\n",
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
const MemoryRegionOps pnv_adu_xscom_ops = {
|
||||
.read = pnv_adu_xscom_read,
|
||||
.write = pnv_adu_xscom_write,
|
||||
.valid.min_access_size = 8,
|
||||
.valid.max_access_size = 8,
|
||||
.impl.min_access_size = 8,
|
||||
.impl.max_access_size = 8,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void pnv_adu_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvADU *adu = PNV_ADU(dev);
|
||||
|
||||
assert(adu->lpc);
|
||||
|
||||
/* XScom regions for ADU registers */
|
||||
pnv_xscom_region_init(&adu->xscom_regs, OBJECT(dev),
|
||||
&pnv_adu_xscom_ops, adu, "xscom-adu",
|
||||
PNV9_XSCOM_ADU_SIZE);
|
||||
}
|
||||
|
||||
static Property pnv_adu_properties[] = {
|
||||
DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_adu_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_adu_realize;
|
||||
dc->desc = "PowerNV ADU";
|
||||
device_class_set_props(dc, pnv_adu_properties);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_adu_type_info = {
|
||||
.name = TYPE_PNV_ADU,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvADU),
|
||||
.class_init = pnv_adu_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_PNV_XSCOM_INTERFACE },
|
||||
{ } },
|
||||
};
|
||||
|
||||
static void pnv_adu_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_adu_type_info);
|
||||
}
|
||||
|
||||
type_init(pnv_adu_register_types);
|
||||
@@ -364,8 +364,7 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr,
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
|
||||
" TOD_MOVE_TOD_TO_TB_REG with no slave target\n");
|
||||
} else {
|
||||
PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0];
|
||||
CPUPPCState *env = &cpu->env;
|
||||
PnvCore *pc = chiptod->slave_pc_target;
|
||||
|
||||
/*
|
||||
* Moving TOD to TB will set the TB of all threads in a
|
||||
@@ -377,8 +376,8 @@ static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr,
|
||||
* thread 0.
|
||||
*/
|
||||
|
||||
if (env->pnv_tod_tbst.tb_ready_for_tod) {
|
||||
env->pnv_tod_tbst.tod_sent_to_tb = 1;
|
||||
if (pc->tod_state.tb_ready_for_tod) {
|
||||
pc->tod_state.tod_sent_to_tb = 1;
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
|
||||
" TOD_MOVE_TOD_TO_TB_REG with TB not ready to"
|
||||
|
||||
+118
-9
@@ -58,6 +58,10 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu)
|
||||
env->nip = 0x10;
|
||||
env->msr |= MSR_HVB; /* Hypervisor mode */
|
||||
env->spr[SPR_HRMOR] = pc->hrmor;
|
||||
if (pc->big_core) {
|
||||
/* Clear "small core" bit on Power9/10 (this is set in default PVR) */
|
||||
env->spr[SPR_PVR] &= ~PPC_BIT(51);
|
||||
}
|
||||
hreg_compute_hflags(env);
|
||||
ppc_maybe_interrupt(env);
|
||||
|
||||
@@ -181,16 +185,43 @@ static const MemoryRegionOps pnv_core_power9_xscom_ops = {
|
||||
*/
|
||||
|
||||
#define PNV10_XSCOM_EC_CORE_THREAD_STATE 0x412
|
||||
#define PNV10_XSCOM_EC_CORE_THREAD_INFO 0x413
|
||||
#define PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS 0x449
|
||||
#define PNV10_XSCOM_EC_CORE_RAS_STATUS 0x454
|
||||
|
||||
static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned int width)
|
||||
{
|
||||
PnvCore *pc = PNV_CORE(opaque);
|
||||
int nr_threads = CPU_CORE(pc)->nr_threads;
|
||||
int i;
|
||||
uint32_t offset = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (offset) {
|
||||
case PNV10_XSCOM_EC_CORE_THREAD_STATE:
|
||||
val = 0;
|
||||
for (i = 0; i < nr_threads; i++) {
|
||||
PowerPCCPU *cpu = pc->threads[i];
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
if (cs->halted) {
|
||||
val |= PPC_BIT(56 + i);
|
||||
}
|
||||
}
|
||||
if (pc->lpar_per_core) {
|
||||
val |= PPC_BIT(62);
|
||||
}
|
||||
break;
|
||||
case PNV10_XSCOM_EC_CORE_THREAD_INFO:
|
||||
break;
|
||||
case PNV10_XSCOM_EC_CORE_RAS_STATUS:
|
||||
for (i = 0; i < nr_threads; i++) {
|
||||
PowerPCCPU *cpu = pc->threads[i];
|
||||
CPUState *cs = CPU(cpu);
|
||||
if (cs->stopped) {
|
||||
val |= PPC_BIT(0 + 8 * i) | PPC_BIT(1 + 8 * i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
|
||||
@@ -203,9 +234,46 @@ static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr,
|
||||
static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned int width)
|
||||
{
|
||||
PnvCore *pc = PNV_CORE(opaque);
|
||||
int nr_threads = CPU_CORE(pc)->nr_threads;
|
||||
int i;
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
switch (offset) {
|
||||
case PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS:
|
||||
for (i = 0; i < nr_threads; i++) {
|
||||
PowerPCCPU *cpu = pc->threads[i];
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
||||
if (val & PPC_BIT(7 + 8 * i)) { /* stop */
|
||||
val &= ~PPC_BIT(7 + 8 * i);
|
||||
cpu_pause(cs);
|
||||
}
|
||||
if (val & PPC_BIT(6 + 8 * i)) { /* start */
|
||||
val &= ~PPC_BIT(6 + 8 * i);
|
||||
cpu_resume(cs);
|
||||
}
|
||||
if (val & PPC_BIT(4 + 8 * i)) { /* sreset */
|
||||
val &= ~PPC_BIT(4 + 8 * i);
|
||||
pnv_cpu_do_nmi_resume(cs);
|
||||
}
|
||||
if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */
|
||||
/*
|
||||
* Hardware has very particular cases for where clear maint
|
||||
* must be used and where start must be used to resume a
|
||||
* thread. These are not modelled exactly, just treat
|
||||
* this and start the same.
|
||||
*/
|
||||
val &= ~PPC_BIT(3 + 8 * i);
|
||||
cpu_resume(cs);
|
||||
}
|
||||
}
|
||||
if (val) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimp bits in DIRECT_CONTROLS "
|
||||
"0x%016" PRIx64 "\n", __func__, val);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
|
||||
offset);
|
||||
@@ -227,8 +295,9 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp,
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int core_hwid;
|
||||
ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
|
||||
ppc_spr_t *tir = &env->spr_cb[SPR_TIR];
|
||||
ppc_spr_t *pir_spr = &env->spr_cb[SPR_PIR];
|
||||
ppc_spr_t *tir_spr = &env->spr_cb[SPR_TIR];
|
||||
uint32_t pir, tir;
|
||||
Error *local_err = NULL;
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip);
|
||||
|
||||
@@ -244,8 +313,20 @@ static void pnv_core_cpu_realize(PnvCore *pc, PowerPCCPU *cpu, Error **errp,
|
||||
|
||||
core_hwid = object_property_get_uint(OBJECT(pc), "hwid", &error_abort);
|
||||
|
||||
tir->default_value = thread_index;
|
||||
pir->default_value = pcc->chip_pir(pc->chip, core_hwid, thread_index);
|
||||
pcc->get_pir_tir(pc->chip, core_hwid, thread_index, &pir, &tir);
|
||||
pir_spr->default_value = pir;
|
||||
tir_spr->default_value = tir;
|
||||
|
||||
if (pc->big_core) {
|
||||
/* 2 "small cores" get the same core index for SMT operations */
|
||||
env->core_index = core_hwid >> 1;
|
||||
} else {
|
||||
env->core_index = core_hwid;
|
||||
}
|
||||
|
||||
if (pc->lpar_per_core) {
|
||||
cpu_ppc_set_1lpar(cpu);
|
||||
}
|
||||
|
||||
/* Set time-base frequency to 512 MHz */
|
||||
cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
|
||||
@@ -278,16 +359,22 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
|
||||
pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
PowerPCCPU *cpu;
|
||||
PnvCPUState *pnv_cpu;
|
||||
|
||||
obj = object_new(typename);
|
||||
cpu = POWERPC_CPU(obj);
|
||||
|
||||
pc->threads[i] = POWERPC_CPU(obj);
|
||||
if (cc->nr_threads > 1) {
|
||||
cpu->env.has_smt_siblings = true;
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "thread[%d]", i);
|
||||
object_property_add_child(OBJECT(pc), name, obj);
|
||||
|
||||
cpu->machine_data = g_new0(PnvCPUState, 1);
|
||||
pnv_cpu = pnv_cpu_state(cpu);
|
||||
pnv_cpu->pnv_core = pc;
|
||||
|
||||
object_unref(obj);
|
||||
}
|
||||
@@ -344,6 +431,10 @@ static void pnv_core_unrealize(DeviceState *dev)
|
||||
static Property pnv_core_properties[] = {
|
||||
DEFINE_PROP_UINT32("hwid", PnvCore, hwid, 0),
|
||||
DEFINE_PROP_UINT64("hrmor", PnvCore, hrmor, 0),
|
||||
DEFINE_PROP_BOOL("big-core", PnvCore, big_core, false),
|
||||
DEFINE_PROP_BOOL("quirk-tb-big-core", PnvCore, tod_state.big_core_quirk,
|
||||
false),
|
||||
DEFINE_PROP_BOOL("lpar-per-core", PnvCore, lpar_per_core, false),
|
||||
DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
@@ -504,6 +595,7 @@ static const MemoryRegionOps pnv_quad_power10_xscom_ops = {
|
||||
static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr,
|
||||
unsigned int width)
|
||||
{
|
||||
PnvQuad *eq = PNV_QUAD(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
uint64_t val = -1;
|
||||
|
||||
@@ -511,10 +603,14 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr,
|
||||
* Forth nibble selects the core within a quad, mask it to process read
|
||||
* for any core.
|
||||
*/
|
||||
switch (offset & ~0xf000) {
|
||||
case P10_QME_SPWU_HYP:
|
||||
switch (offset & ~PPC_BITMASK32(16, 19)) {
|
||||
case P10_QME_SSH_HYP:
|
||||
return 0;
|
||||
val = 0;
|
||||
if (eq->special_wakeup_done) {
|
||||
val |= PPC_BIT(1); /* SPWU DONE */
|
||||
val |= PPC_BIT(4); /* SSH SPWU DONE */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimp read 0x%08x\n", __func__,
|
||||
offset);
|
||||
@@ -526,9 +622,22 @@ static uint64_t pnv_qme_power10_xscom_read(void *opaque, hwaddr addr,
|
||||
static void pnv_qme_power10_xscom_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned int width)
|
||||
{
|
||||
PnvQuad *eq = PNV_QUAD(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
bool set;
|
||||
int i;
|
||||
|
||||
switch (offset) {
|
||||
switch (offset & ~PPC_BITMASK32(16, 19)) {
|
||||
case P10_QME_SPWU_HYP:
|
||||
set = !!(val & PPC_BIT(0));
|
||||
eq->special_wakeup_done = set;
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* These bits select cores in the quad */
|
||||
if (offset & PPC_BIT32(16 + i)) {
|
||||
eq->special_wakeup[i] = set;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unimp write 0x%08x\n", __func__,
|
||||
offset);
|
||||
|
||||
+132
-34
@@ -64,6 +64,7 @@ enum {
|
||||
#define LPC_HC_IRQSER_START_4CLK 0x00000000
|
||||
#define LPC_HC_IRQSER_START_6CLK 0x01000000
|
||||
#define LPC_HC_IRQSER_START_8CLK 0x02000000
|
||||
#define LPC_HC_IRQSER_AUTO_CLEAR 0x00800000
|
||||
#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */
|
||||
#define LPC_HC_IRQSTAT 0x38
|
||||
#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */
|
||||
@@ -235,16 +236,16 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr,
|
||||
* TODO: rework to use address_space_stq() and address_space_ldq()
|
||||
* instead.
|
||||
*/
|
||||
static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
|
||||
int sz)
|
||||
bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr,
|
||||
uint8_t *data, int sz)
|
||||
{
|
||||
/* XXX Handle access size limits and FW read caching here */
|
||||
return !address_space_read(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
|
||||
data, sz);
|
||||
}
|
||||
|
||||
static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
|
||||
int sz)
|
||||
bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr,
|
||||
uint8_t *data, int sz)
|
||||
{
|
||||
/* XXX Handle access size limits here */
|
||||
return !address_space_write(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
|
||||
@@ -276,7 +277,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
|
||||
}
|
||||
|
||||
if (cmd & ECCB_CTL_READ) {
|
||||
success = opb_read(lpc, opb_addr, data, sz);
|
||||
success = pnv_lpc_opb_read(lpc, opb_addr, data, sz);
|
||||
if (success) {
|
||||
lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
|
||||
(((uint64_t)data[0]) << 24 |
|
||||
@@ -293,7 +294,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
|
||||
data[2] = lpc->eccb_data_reg >> 8;
|
||||
data[3] = lpc->eccb_data_reg;
|
||||
|
||||
success = opb_write(lpc, opb_addr, data, sz);
|
||||
success = pnv_lpc_opb_write(lpc, opb_addr, data, sz);
|
||||
lpc->eccb_stat_reg = ECCB_STAT_OP_DONE;
|
||||
}
|
||||
/* XXX Which error bit (if any) to signal OPB error ? */
|
||||
@@ -420,32 +421,90 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = {
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
/* Program the POWER9 LPC irq to PSI serirq routing table */
|
||||
static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc)
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (!lpc->psi_has_serirq) {
|
||||
if ((lpc->opb_irq_route0 & PPC_BITMASK(8, 13)) ||
|
||||
(lpc->opb_irq_route1 & PPC_BITMASK(4, 31))) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"OPB: setting serirq routing on POWER8 system, ignoring.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (irq = 0; irq <= 13; irq++) {
|
||||
int serirq = (lpc->opb_irq_route1 >> (31 - 5 - (irq * 2))) & 0x3;
|
||||
lpc->irq_to_serirq_route[irq] = serirq;
|
||||
}
|
||||
|
||||
for (irq = 14; irq < ISA_NUM_IRQS; irq++) {
|
||||
int serirq = (lpc->opb_irq_route0 >> (31 - 9 - (irq * 2))) & 0x3;
|
||||
lpc->irq_to_serirq_route[irq] = serirq;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
|
||||
{
|
||||
bool lpc_to_opb_irq = false;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Update LPC controller to OPB line */
|
||||
if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
|
||||
uint32_t irqs;
|
||||
|
||||
irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
|
||||
lpc_to_opb_irq = (irqs != 0);
|
||||
active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask;
|
||||
}
|
||||
|
||||
/* We don't honor the polarity register, it's pointless and unused
|
||||
* anyway
|
||||
*/
|
||||
if (lpc_to_opb_irq) {
|
||||
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;
|
||||
|
||||
/* Reflect the interrupt */
|
||||
qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0);
|
||||
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
|
||||
* 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;
|
||||
|
||||
for (irq = 0; irq < ISA_NUM_IRQS; irq++) {
|
||||
if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) {
|
||||
serirq_out[lpc->irq_to_serirq_route[irq]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
@@ -505,7 +564,14 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
break;
|
||||
case LPC_HC_IRQSTAT:
|
||||
lpc->lpc_hc_irqstat &= ~val;
|
||||
/*
|
||||
* This register is write-to-clear for the IRQSER (LPC device IRQ)
|
||||
* status. However if the device has not de-asserted its interrupt
|
||||
* that will just raise this IRQ status bit again. Model this by
|
||||
* keeping track of the inputs and only clearing if the inputs are
|
||||
* deasserted.
|
||||
*/
|
||||
lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs);
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
break;
|
||||
case LPC_HC_ERROR_ADDRESS:
|
||||
@@ -536,10 +602,10 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
|
||||
uint64_t val = 0xfffffffffffffffful;
|
||||
|
||||
switch (addr) {
|
||||
case OPB_MASTER_LS_ROUTE0: /* TODO */
|
||||
case OPB_MASTER_LS_ROUTE0:
|
||||
val = lpc->opb_irq_route0;
|
||||
break;
|
||||
case OPB_MASTER_LS_ROUTE1: /* TODO */
|
||||
case OPB_MASTER_LS_ROUTE1:
|
||||
val = lpc->opb_irq_route1;
|
||||
break;
|
||||
case OPB_MASTER_LS_IRQ_STAT:
|
||||
@@ -568,11 +634,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
|
||||
PnvLpcController *lpc = opaque;
|
||||
|
||||
switch (addr) {
|
||||
case OPB_MASTER_LS_ROUTE0: /* TODO */
|
||||
case OPB_MASTER_LS_ROUTE0:
|
||||
lpc->opb_irq_route0 = val;
|
||||
pnv_lpc_eval_serirq_routes(lpc);
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
break;
|
||||
case OPB_MASTER_LS_ROUTE1: /* TODO */
|
||||
case OPB_MASTER_LS_ROUTE1:
|
||||
lpc->opb_irq_route1 = val;
|
||||
pnv_lpc_eval_serirq_routes(lpc);
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
break;
|
||||
case OPB_MASTER_LS_IRQ_STAT:
|
||||
lpc->opb_irq_stat &= ~val;
|
||||
@@ -657,6 +727,8 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
|
||||
PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
object_property_set_bool(OBJECT(lpc), "psi-serirq", true, &error_abort);
|
||||
|
||||
plc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -666,6 +738,9 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
|
||||
/* P9 uses a MMIO region */
|
||||
memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops,
|
||||
lpc, "lpcm", PNV9_LPCM_SIZE);
|
||||
|
||||
/* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */
|
||||
qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4);
|
||||
}
|
||||
|
||||
static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
|
||||
@@ -744,13 +819,19 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
|
||||
&lpc->lpc_hc_regs);
|
||||
|
||||
qdev_init_gpio_out(dev, &lpc->psi_irq, 1);
|
||||
qdev_init_gpio_out_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1);
|
||||
}
|
||||
|
||||
static Property pnv_lpc_properties[] = {
|
||||
DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_lpc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, pnv_lpc_properties);
|
||||
dc->realize = pnv_lpc_realize;
|
||||
dc->desc = "PowerNV LPC Controller";
|
||||
dc->user_creatable = false;
|
||||
@@ -796,18 +877,34 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
|
||||
}
|
||||
|
||||
if (pnv->cpld_irqstate != old_state) {
|
||||
qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0);
|
||||
qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
|
||||
{
|
||||
PnvLpcController *lpc = PNV_LPC(opaque);
|
||||
uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n;
|
||||
|
||||
/* The Naples HW latches the 1 levels, clearing is done by SW */
|
||||
if (level) {
|
||||
lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n;
|
||||
lpc->lpc_hc_irq_inputs |= irq_bit;
|
||||
|
||||
/*
|
||||
* The LPC HC in Naples and later latches LPC IRQ into a bit field in
|
||||
* the IRQSTAT register, and that drives the PSI IRQ to the IC.
|
||||
* Software clears this bit manually (see LPC_HC_IRQSTAT handler).
|
||||
*/
|
||||
lpc->lpc_hc_irqstat |= irq_bit;
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
} else {
|
||||
lpc->lpc_hc_irq_inputs &= ~irq_bit;
|
||||
|
||||
/* POWER9 adds an auto-clear mode that clears IRQSTAT bits on EOI */
|
||||
if (lpc->psi_has_serirq &&
|
||||
(lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) {
|
||||
lpc->lpc_hc_irqstat &= ~irq_bit;
|
||||
pnv_lpc_eval_irqs(lpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,6 +935,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp)
|
||||
handler = pnv_lpc_isa_irq_handler;
|
||||
}
|
||||
|
||||
/* POWER has a 17th irq, QEMU only implements the 16 regular device irqs */
|
||||
irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS);
|
||||
|
||||
isa_bus_register_input_irqs(isa_bus, irqs);
|
||||
|
||||
@@ -75,11 +75,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
|
||||
case PRD_P9_IPOLL_REG_MASK:
|
||||
case PRD_P9_IPOLL_REG_STATUS:
|
||||
|
||||
/* P9 xscom reset */
|
||||
case 0x0090018: /* Receive status reg */
|
||||
case 0x0090012: /* log register */
|
||||
case 0x0090013: /* error register */
|
||||
|
||||
/* P8 xscom reset */
|
||||
case 0x2020007: /* ADU stuff, log register */
|
||||
case 0x2020009: /* ADU stuff, error register */
|
||||
@@ -119,10 +114,6 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
|
||||
case 0x1010c03: /* PIBAM FIR MASK */
|
||||
case 0x1010c04: /* PIBAM FIR MASK */
|
||||
case 0x1010c05: /* PIBAM FIR MASK */
|
||||
/* P9 xscom reset */
|
||||
case 0x0090018: /* Receive status reg */
|
||||
case 0x0090012: /* log register */
|
||||
case 0x0090013: /* error register */
|
||||
|
||||
/* P8 xscom reset */
|
||||
case 0x2020007: /* ADU stuff, log register */
|
||||
|
||||
@@ -2195,6 +2195,7 @@ static const VMStateDescription vmstate_spapr = {
|
||||
&vmstate_spapr_cap_fwnmi,
|
||||
&vmstate_spapr_fwnmi,
|
||||
&vmstate_spapr_cap_rpt_invalidate,
|
||||
&vmstate_spapr_cap_ail_mode_3,
|
||||
&vmstate_spapr_cap_nested_papr,
|
||||
NULL
|
||||
}
|
||||
|
||||
@@ -974,6 +974,7 @@ SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
|
||||
SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
|
||||
SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI);
|
||||
SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE);
|
||||
SPAPR_CAP_MIG_STATE(ail_mode_3, SPAPR_CAP_AIL_MODE_3);
|
||||
|
||||
void spapr_caps_init(SpaprMachineState *spapr)
|
||||
{
|
||||
|
||||
+13
-3
@@ -300,11 +300,13 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp)
|
||||
g_autofree char *id = NULL;
|
||||
CPUState *cs;
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
|
||||
obj = object_new(scc->cpu_type);
|
||||
|
||||
cs = CPU(obj);
|
||||
cpu = POWERPC_CPU(obj);
|
||||
env = &cpu->env;
|
||||
/*
|
||||
* All CPUs start halted. CPU0 is unhalted from the machine level reset code
|
||||
* and the rest are explicitly started up by the guest using an RTAS call.
|
||||
@@ -315,6 +317,8 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->core_index = cc->core_id;
|
||||
|
||||
cpu->node_id = sc->node_id;
|
||||
|
||||
id = g_strdup_printf("thread[%d]", i);
|
||||
@@ -345,9 +349,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)
|
||||
qemu_register_reset(spapr_cpu_core_reset_handler, sc);
|
||||
sc->threads = g_new0(PowerPCCPU *, cc->nr_threads);
|
||||
for (i = 0; i < cc->nr_threads; i++) {
|
||||
sc->threads[i] = spapr_create_vcpu(sc, i, errp);
|
||||
if (!sc->threads[i] ||
|
||||
!spapr_realize_vcpu(sc->threads[i], spapr, sc, i, errp)) {
|
||||
PowerPCCPU *cpu;
|
||||
|
||||
cpu = spapr_create_vcpu(sc, i, errp);
|
||||
sc->threads[i] = cpu;
|
||||
if (cpu && cc->nr_threads > 1) {
|
||||
cpu->env.has_smt_siblings = true;
|
||||
}
|
||||
|
||||
if (!cpu || !spapr_realize_vcpu(cpu, spapr, sc, i, errp)) {
|
||||
spapr_cpu_core_unrealize(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
+4
-17
@@ -15,19 +15,6 @@
|
||||
#include "helper_regs.h"
|
||||
#include "hw/ppc/spapr.h"
|
||||
#include "mmu-hash64.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
|
||||
|
||||
static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex)
|
||||
{
|
||||
/*
|
||||
* hash value/pteg group index is normalized by HPT mask
|
||||
*/
|
||||
if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
@@ -70,7 +57,7 @@ static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
|
||||
pteh &= ~0x60ULL;
|
||||
|
||||
if (!valid_ptex(cpu, ptex)) {
|
||||
if (!ppc_hash64_valid_ptex(cpu, ptex)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
@@ -119,7 +106,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu
|
||||
const ppc_hash_pte64_t *hptes;
|
||||
target_ulong v, r;
|
||||
|
||||
if (!valid_ptex(cpu, ptex)) {
|
||||
if (!ppc_hash64_valid_ptex(cpu, ptex)) {
|
||||
return REMOVE_PARM;
|
||||
}
|
||||
|
||||
@@ -250,7 +237,7 @@ static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
const ppc_hash_pte64_t *hptes;
|
||||
target_ulong v, r;
|
||||
|
||||
if (!valid_ptex(cpu, ptex)) {
|
||||
if (!ppc_hash64_valid_ptex(cpu, ptex)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
@@ -287,7 +274,7 @@ static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
int i, ridx, n_entries = 1;
|
||||
const ppc_hash_pte64_t *hptes;
|
||||
|
||||
if (!valid_ptex(cpu, ptex)) {
|
||||
if (!ppc_hash64_valid_ptex(cpu, ptex)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
|
||||
void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt)
|
||||
{
|
||||
char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
|
||||
g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
|
||||
|
||||
vof_build_dt(fdt, spapr->vof);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user