Message ID | 20220811164024.1489017-1-npiggin@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC] pnv/chiptod: Add basic P9 chiptod model | expand |
On 8/11/22 18:40, Nicholas Piggin wrote: > The chiptod is a pervasive facility which can keep a time, synchronise > it across multiple chips, and can move that time to or from the core > timebase units. > > This adds a very basic initial emulation of chiptod registers. The > interesting thing about chiptod is that it targets cores and interacts > with core registers (e.g., TB, TFMR). So far there is no actual time > keeping or TB interaction, but core targeting and initial TFMR is > implemented. > > This implements enough for skiboot to boot and go through the chiptod > code (with a small patch to remove QUIRK_NO_CHIPTOD from qemu). > > POWER10 is much the same, not implemented yet because skiboot uses a > different core target addressing mode (due to hardware issues) that is > not implemented yet. > > This is not completely tidy yet, just thought I would see if there are > comments, particularly with the core TFMR interactions. The TFMR implementation should be in its own patch. > > Thanks, > Nick > > --- > hw/ppc/meson.build | 1 + > hw/ppc/pnv.c | 9 + > hw/ppc/pnv_chiptod.c | 320 +++++++++++++++++++++++++++++++++++ > hw/ppc/pnv_xscom.c | 2 + > hw/ppc/trace-events | 4 + > include/hw/ppc/pnv.h | 2 + > include/hw/ppc/pnv_chiptod.h | 51 ++++++ > include/hw/ppc/pnv_xscom.h | 6 + > target/ppc/cpu.h | 13 ++ > target/ppc/cpu_init.c | 2 +- > target/ppc/helper.h | 2 + > target/ppc/misc_helper.c | 25 +++ > target/ppc/spr_common.h | 2 + > target/ppc/translate.c | 10 ++ > 14 files changed, 448 insertions(+), 1 deletion(-) > create mode 100644 hw/ppc/pnv_chiptod.c > create mode 100644 include/hw/ppc/pnv_chiptod.h > > diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build > index 62801923f3..7eb5031055 100644 > --- a/hw/ppc/meson.build > +++ b/hw/ppc/meson.build > @@ -45,6 +45,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( > 'pnv_core.c', > 'pnv_lpc.c', > 'pnv_psi.c', > + 'pnv_chiptod.c', > 'pnv_occ.c', > 'pnv_sbe.c', > 'pnv_bmc.c', > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c > index 7ff1f464d3..bdd641381c 100644 > --- a/hw/ppc/pnv.c > +++ b/hw/ppc/pnv.c > @@ -1395,6 +1395,8 @@ static void pnv_chip_power9_instance_init(Object *obj) > > object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC); > > + object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD); > + > object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC); > > object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE); > @@ -1539,6 +1541,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) > chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", > (uint64_t) PNV9_LPCM_BASE(chip)); > > + /* ChipTOD */ > + if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) { > + return; > + } > + 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; > diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c > new file mode 100644 > index 0000000000..9ef463e640 > --- /dev/null > +++ b/hw/ppc/pnv_chiptod.c > @@ -0,0 +1,320 @@ > +/* > + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour > + * > + * Copyright (c) 2022, IBM Corporation. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License, version 2, as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include "qemu/osdep.h" > +#include "target/ppc/cpu.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > +#include "qemu/module.h" > +#include "hw/irq.h" > +#include "hw/qdev-properties.h" > +#include "hw/ppc/fdt.h" > +#include "hw/ppc/pnv.h" > +#include "hw/ppc/pnv_xscom.h" > +#include "hw/ppc/pnv_chiptod.h" > +#include "trace.h" > + > +#include <libfdt.h> > + > +/* TOD chip XSCOM addresses */ > +#define TOD_MASTER_PATH_CTRL 0x00000000 /* Master Path ctrl reg */ > +#define TOD_PRI_PORT0_CTRL 0x00000001 /* Primary port0 ctrl reg */ > +#define TOD_PRI_PORT1_CTRL 0x00000002 /* Primary port1 ctrl reg */ > +#define TOD_SEC_PORT0_CTRL 0x00000003 /* Secondary p0 ctrl reg */ > +#define TOD_SEC_PORT1_CTRL 0x00000004 /* Secondary p1 ctrl reg */ > +#define TOD_SLAVE_PATH_CTRL 0x00000005 /* Slave Path ctrl reg */ > +#define TOD_INTERNAL_PATH_CTRL 0x00000006 /* Internal Path ctrl reg */ > + > +/* -- TOD primary/secondary master/slave control register -- */ > +#define TOD_PSMS_CTRL 0x00000007 > +#define TOD_PSMSC_PM_TOD_SELECT PPC_BIT(1) /* Primary Master TOD */ > +#define TOD_PSMSC_PM_DRAW_SELECT PPC_BIT(2) /* Primary Master Drawer */ > +#define TOD_PSMSC_SM_TOD_SELECT PPC_BIT(9) /* Secondary Master TOD */ > +#define TOD_PSMSC_SM_DRAW_SELECT PPC_BIT(10) /* Secondary Master Draw */ > + > +/* -- TOD primary/secondary master/slave status register -- */ > +#define TOD_STATUS 0x00000008 > +#define TOD_ST_TOPOLOGY_SELECT PPC_BITMASK(0, 2) > +#define TOD_ST_MPATH0_STEP_VALID PPC_BIT(6) /* MasterPath0 step valid */ > +#define TOD_ST_MPATH1_STEP_VALID PPC_BIT(7) /* MasterPath1 step valid */ > +#define TOD_ST_SPATH0_STEP_VALID PPC_BIT(8) /* SlavePath0 step valid */ > +#define TOD_ST_SPATH1_STEP_VALID PPC_BIT(10) /* SlavePath1 step valid */ > +/* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */ > +#define TOD_ST_PRI_MPATH_SELECT PPC_BIT(12) /* Primary MPath Select */ > +#define TOD_ST_PRI_SPATH_SELECT PPC_BIT(15) /* Primary SPath Select */ > +/* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */ > +#define TOD_ST_SEC_MPATH_SELECT PPC_BIT(16) /* Secondary MPath Select */ > +#define TOD_ST_SEC_SPATH_SELECT PPC_BIT(19) /* Secondary SPath Select */ > +#define TOD_ST_ACTIVE_MASTER PPC_BIT(23) > +#define TOD_ST_BACKUP_MASTER PPC_BIT(24) > + > +/* TOD chip XSCOM addresses */ > +#define TOD_CHIP_CTRL 0x00000010 /* Chip control register */ > +#define TOD_TTYPE_0 0x00000011 > +#define TOD_TTYPE_1 0x00000012 /* PSS switch */ > +#define TOD_TTYPE_2 0x00000013 /* Enable step checkers */ > +#define TOD_TTYPE_3 0x00000014 /* Request TOD */ > +#define TOD_TTYPE_4 0x00000015 /* Send TOD */ > +#define TOD_TTYPE_5 0x00000016 /* Invalidate TOD */ > +#define TOD_CHIPTOD_TO_TB 0x00000017 > +#define TOD_LOAD_TOD_MOD 0x00000018 > +#define TOD_CHIPTOD_VALUE 0x00000020 > +#define TOD_CHIPTOD_LOAD_TB 0x00000021 > +#define TOD_CHIPTOD_FSM 0x00000024 > + > +/* -- TOD PIB Master reg -- */ > +#define TOD_PIB_MASTER 0x00000027 > +#define TOD_PIBM_ADDR_CFG_MCAST PPC_BIT(25) > +#define TOD_PIBM_ADDR_CFG_SLADDR PPC_BITMASK(26, 31) > +#define TOD_PIBM_TTYPE4_SEND_MODE PPC_BIT(32) > +#define TOD_PIBM_TTYPE4_SEND_ENBL PPC_BIT(33) > + > +/* -- TOD Error interrupt register -- */ > +#define TOD_ERROR 0x00000030 > +/* SYNC errors */ > +#define TOD_ERR_CRMO_PARITY PPC_BIT(0) > +#define TOD_ERR_OSC0_PARITY PPC_BIT(1) > +#define TOD_ERR_OSC1_PARITY PPC_BIT(2) > +#define TOD_ERR_PPORT0_CREG_PARITY PPC_BIT(3) > +#define TOD_ERR_PPORT1_CREG_PARITY PPC_BIT(4) > +#define TOD_ERR_SPORT0_CREG_PARITY PPC_BIT(5) > +#define TOD_ERR_SPORT1_CREG_PARITY PPC_BIT(6) > +#define TOD_ERR_SPATH_CREG_PARITY PPC_BIT(7) > +#define TOD_ERR_IPATH_CREG_PARITY PPC_BIT(8) > +#define TOD_ERR_PSMS_CREG_PARITY PPC_BIT(9) > +#define TOD_ERR_CRITC_PARITY PPC_BIT(13) > +#define TOD_ERR_MP0_STEP_CHECK PPC_BIT(14) > +#define TOD_ERR_MP1_STEP_CHECK PPC_BIT(15) > +#define TOD_ERR_PSS_HAMMING_DISTANCE PPC_BIT(18) > +#define TOD_ERR_DELAY_COMPL_PARITY PPC_BIT(22) > +/* CNTR errors */ > +#define TOD_ERR_CTCR_PARITY PPC_BIT(32) > +#define TOD_ERR_TOD_SYNC_CHECK PPC_BIT(33) > +#define TOD_ERR_TOD_FSM_PARITY PPC_BIT(34) > +#define TOD_ERR_TOD_REGISTER_PARITY PPC_BIT(35) > +#define TOD_ERR_OVERFLOW_YR2042 PPC_BIT(36) > +#define TOD_ERR_TOD_WOF_LSTEP_PARITY PPC_BIT(37) > +#define TOD_ERR_TTYPE0_RECVD PPC_BIT(38) > +#define TOD_ERR_TTYPE1_RECVD PPC_BIT(39) > +#define TOD_ERR_TTYPE2_RECVD PPC_BIT(40) > +#define TOD_ERR_TTYPE3_RECVD PPC_BIT(41) > +#define TOD_ERR_TTYPE4_RECVD PPC_BIT(42) > +#define TOD_ERR_TTYPE5_RECVD PPC_BIT(43) > + > +/* -- TOD Error interrupt register -- */ > +#define TOD_ERROR_INJECT 0x00000031 > + > +/* PC unit PIB address which recieves the timebase transfer from TOD */ > +#define PC_TOD 0x4A3 > + > +static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); > + uint32_t offset = addr >> 3; > + uint64_t val = 0; > + > + switch (offset) { > + case TOD_ERROR: > + val = chiptod->tod_error; > + break; > + case TOD_CHIPTOD_FSM: > + if (chiptod->tod_state == tod_running) { > + val |= PPC_BIT(4); > + } > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr >> 3); > + } > + > + trace_pnv_chiptod_xscom_read(addr >> 3, val); > + > + return val; > +} > + > +static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); > + uint32_t offset = addr >> 3; > + > + trace_pnv_chiptod_xscom_write(addr >> 3, val); > + > + switch (offset) { > + case TOD_PIB_MASTER: > + if (val & PPC_BIT(35)) { > + /* SCOM addressing */ > + /* XXX: todo */ > + } else { > + /* PIR addressing */ > + /* This implementation is POWER9 specific */ > + uint32_t pir = (GETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, val) & 0x1f) << 2; > + CPUState *cs; > + > + chiptod->slave_cpu_target = NULL; > + CPU_FOREACH(cs) { > + PowerPCCPU *cpu = POWERPC_CPU(cs); > + CPUPPCState *env = &cpu->env; > + if (env->spr_cb[SPR_PIR].default_value == pir) { > + chiptod->slave_cpu_target = cpu; > + break; > + } > + } You could use : chiptod->slave_cpu_target = ppc_get_vcpu_by_pir(pir). > + if (chiptod->slave_cpu_target == NULL) { > + printf("CHIPTOD NO SLAVE!\n"); It should be a guest error. > + } > + } > + break; > + case TOD_ERROR: > + chiptod->tod_error &= ~val; > + break; > + case TOD_LOAD_TOD_MOD: > + chiptod->tod_state = tod_not_set; > + break; > + case TOD_CHIPTOD_LOAD_TB: > + chiptod->tod_state = tod_running; > + break; > + case TOD_CHIPTOD_TO_TB: > + if (chiptod->slave_cpu_target == NULL) { > + printf("CHIPTOD NO SLAVE!\n"); It should be a guest error. > + } else { > + PowerPCCPU *cpu = chiptod->slave_cpu_target; > + CPUPPCState *env = &cpu->env; > + uint64_t tfmr = env->spr[SPR_TFMR]; > + > + if (!(tfmr & TFMR_MOVE_CHIP_TOD_TO_TB)) { > + printf("CORE TFMR NOT READY TO RECEIVE!\n"); > + break; > + } > + env->spr[SPR_TFMR] &= ~TFMR_MOVE_CHIP_TOD_TO_TB; > + env->spr[SPR_TFMR] |= TFMR_TB_VALID; > + } > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" > 7+ HWADDR_PRIx "\n", addr >> 3); > + } > +} > + > +static const MemoryRegionOps pnv_chiptod_xscom_ops = { > + .read = pnv_chiptod_xscom_read, > + .write = pnv_chiptod_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 int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt, > + int xscom_offset, > + const char compat[], size_t compat_size) > +{ > + static bool primary = false; > + char *name; > + int offset; > + uint32_t lpc_pcba = PNV9_XSCOM_CHIPTOD_BASE; > + uint32_t reg[] = { > + cpu_to_be32(lpc_pcba), > + cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE) > + }; > + > + name = g_strdup_printf("chiptod@%x", lpc_pcba); > + offset = fdt_add_subnode(fdt, xscom_offset, name); > + _FDT(offset); > + g_free(name); > + > + if (!primary) { > + primary = true; > + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); > + } > + > + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); > + _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size))); > + return 0; > +} > + > +static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, > + int xscom_offset) > +{ > + const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod"; > + > + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); > +} > + > +static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) > +{ > +// PnvChipTODClass *psc = PNV_CHIPTOD_CLASS(klass); That's a left over I guess ? > + DeviceClass *dc = DEVICE_CLASS(klass); > + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); > + > + dc->desc = "PowerNV CHIPTOD Controller (POWER9)"; > + > + xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; > +} > + > +static const TypeInfo pnv_chiptod_power9_type_info = { > + .name = TYPE_PNV9_CHIPTOD, > + .parent = TYPE_PNV_CHIPTOD, > + .instance_size = sizeof(PnvChipTOD), > + .class_init = pnv_chiptod_power9_class_init, > + .interfaces = (InterfaceInfo[]) { > + { TYPE_PNV_XSCOM_INTERFACE }, > + { } > + } > +}; > + > +static void pnv_chiptod_realize(DeviceState *dev, Error **errp) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); > +// PnvChipTODClass *psc = PNV_CHIPTOD_GET_CLASS(chiptod); > + > + chiptod->tod_state = tod_running; > + > + /* XScom regions for CHIPTOD registers */ > + pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), > + &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", > + PNV_XSCOM_CHIPTOD_SIZE); > +} > + > +static void pnv_chiptod_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = pnv_chiptod_realize; > + dc->desc = "PowerNV ChipTOD Controller"; > + dc->user_creatable = false; > +} > + > +static const TypeInfo pnv_chiptod_type_info = { > + .name = TYPE_PNV_CHIPTOD, > + .parent = TYPE_DEVICE, > + .instance_size = sizeof(PnvChipTOD), > + .class_init = pnv_chiptod_class_init, > + .class_size = sizeof(PnvChipTODClass), > + .abstract = true, > +}; > + > +static void pnv_chiptod_register_types(void) > +{ > + type_register_static(&pnv_chiptod_type_info); > + type_register_static(&pnv_chiptod_power9_type_info); > +} > + > +type_init(pnv_chiptod_register_types); > diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c > index 79f10de57f..10d2d9edd4 100644 > --- a/hw/ppc/pnv_xscom.c > +++ b/hw/ppc/pnv_xscom.c > @@ -297,6 +297,8 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, > _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); > if (chip->chip_id == 0) { > _FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0))); > + } else if (chip->chip_id == 1) { > + _FDT((fdt_setprop(fdt, xscom_offset, "secondary", NULL, 0))); > } > > args.fdt = fdt; > diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events > index f6990439d1..60c0a1c42c 100644 > --- a/hw/ppc/trace-events > +++ b/hw/ppc/trace-events > @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" > vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 > vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 > > +# pnv_chiptod.c > +pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > +pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > + > # pnv_sbe.c > pnv_sbe_xscom_ctrl_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > pnv_sbe_xscom_ctrl_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h > index 37c303bf36..501e9b0138 100644 > --- a/include/hw/ppc/pnv.h > +++ b/include/hw/ppc/pnv.h > @@ -26,6 +26,7 @@ > #include "hw/ppc/pnv_lpc.h" > #include "hw/ppc/pnv_pnor.h" > #include "hw/ppc/pnv_psi.h" > +#include "hw/ppc/pnv_chiptod.h" > #include "hw/ppc/pnv_occ.h" > #include "hw/ppc/pnv_sbe.h" > #include "hw/ppc/pnv_homer.h" > @@ -100,6 +101,7 @@ struct Pnv9Chip { > PnvXive xive; > Pnv9Psi psi; > PnvLpcController lpc; > + PnvChipTOD chiptod; > PnvOCC occ; > PnvSBE sbe; > PnvHomer homer; > diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h > new file mode 100644 > index 0000000000..44700bbd81 > --- /dev/null > +++ b/include/hw/ppc/pnv_chiptod.h > @@ -0,0 +1,51 @@ > +/* > + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour > + * > + * Copyright (c) 2022, IBM Corporation. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef PPC_PNV_CHIPTOD_H > +#define PPC_PNV_CHIPTOD_H > + > +#include "qom/object.h" > + > +#define TYPE_PNV_CHIPTOD "pnv-chiptod" > +OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) > +#define TYPE_PNV9_CHIPTOD TYPE_PNV_CHIPTOD "-POWER9" > +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) > +#define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" > +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) > + > +enum tod_state { > + tod_running = 1, > + tod_not_set, > +}; > + > +struct PnvChipTOD { > + DeviceState xd; > + > + enum tod_state tod_state; > + uint64_t tod_error; > + PowerPCCPU *slave_cpu_target; > + > + MemoryRegion xscom_regs; > +}; > + > +struct PnvChipTODClass { > + DeviceClass parent_class; Will PnvChipTODClass become useful with P10 ? > +}; > + > +#endif /* PPC_PNV_CHIPTOD_H */ > diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h > index c6e9ef8dd2..a8bd15401f 100644 > --- a/include/hw/ppc/pnv_xscom.h > +++ b/include/hw/ppc/pnv_xscom.h > @@ -63,6 +63,9 @@ struct PnvXScomInterfaceClass { > #define PNV_XSCOM_PSIHB_BASE 0x2010900 > #define PNV_XSCOM_PSIHB_SIZE 0x20 > > +#define PNV_XSCOM_CHIPTOD_BASE 0x0040000 > +#define PNV_XSCOM_CHIPTOD_SIZE 0x31 > + > #define PNV_XSCOM_OCC_BASE 0x0066000 > #define PNV_XSCOM_OCC_SIZE 0x6000 > > @@ -89,6 +92,9 @@ struct PnvXScomInterfaceClass { > ((uint64_t)(((core) & 0x1C) + 0x40) << 22) > #define PNV9_XSCOM_EQ_SIZE 0x100000 > > +#define PNV9_XSCOM_CHIPTOD_BASE PNV_XSCOM_CHIPTOD_BASE > +#define PNV9_XSCOM_CHIPTOD_SIZE PNV_XSCOM_CHIPTOD_SIZE > + > #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE > #define PNV9_XSCOM_OCC_SIZE 0x8000 > > diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h > index a4c893cfad..7edb3447fa 100644 > --- a/target/ppc/cpu.h > +++ b/target/ppc/cpu.h > @@ -1135,6 +1135,11 @@ struct CPUArchState { > uint32_t tlb_need_flush; /* Delayed flush needed */ > #define TLB_NEED_LOCAL_FLUSH 0x1 > #define TLB_NEED_GLOBAL_FLUSH 0x2 > + > +#if defined(TARGET_PPC64) > + /* PowerNV timebase facility */ > + int tfmr_op_timeout; > +#endif > #endif > > /* Other registers */ > @@ -2471,6 +2476,14 @@ enum { > HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), > }; > > +/* TFMR */ > +enum { > + TFMR_LOAD_TOD_MOD = PPC_BIT(16), > + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), > + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), > + TFMR_TB_VALID = PPC_BIT(41), > +}; > + > /*****************************************************************************/ > > #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) > diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c > index d1493a660c..6d555e16b9 100644 > --- a/target/ppc/cpu_init.c > +++ b/target/ppc/cpu_init.c > @@ -5392,7 +5392,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) > spr_register_hv(env, SPR_TFMR, "TFMR", > SPR_NOACCESS, SPR_NOACCESS, > SPR_NOACCESS, SPR_NOACCESS, > - &spr_read_generic, &spr_write_generic, > + &spr_read_tfmr, &spr_write_tfmr, > 0x00000000); > spr_register_hv(env, SPR_LPIDR, "LPIDR", > SPR_NOACCESS, SPR_NOACCESS, > diff --git a/target/ppc/helper.h b/target/ppc/helper.h > index 159b352f6e..643c9457c7 100644 > --- a/target/ppc/helper.h > +++ b/target/ppc/helper.h > @@ -720,6 +720,8 @@ DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env) > DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl) > DEF_HELPER_2(book3s_msgsndp, void, env, tl) > DEF_HELPER_2(book3s_msgclrp, void, env, tl) > +DEF_HELPER_1(load_tfmr, tl, env) > +DEF_HELPER_2(store_tfmr, void, env, tl) > #endif > DEF_HELPER_2(store_sdr1, void, env, tl) > DEF_HELPER_2(store_pidr, void, env, tl) > diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c > index b0a5e7ce76..d979d936b3 100644 > --- a/target/ppc/misc_helper.c > +++ b/target/ppc/misc_helper.c > @@ -191,6 +191,31 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) > env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); > } > } > + > +target_ulong helper_load_tfmr(CPUPPCState *env) > +{ > + if (env->tfmr_op_timeout) { > + env->tfmr_op_timeout--; > + if (!env->tfmr_op_timeout) { > + env->spr[SPR_TFMR] &= ~(TFMR_LOAD_TOD_MOD | > + TFMR_CLEAR_TB_ERRORS); > + } > + } > + > + return env->spr[SPR_TFMR]; > +} > + > +void helper_store_tfmr(CPUPPCState *env, target_ulong val) > +{ > + env->spr[SPR_TFMR] = val; > + > + if (val & TFMR_LOAD_TOD_MOD) { > + env->tfmr_op_timeout = 3; why 3 ? > + } > + if (val & TFMR_CLEAR_TB_ERRORS) { > + env->tfmr_op_timeout = 3; > + } > +} This should generate an exception if not in HV mode ? > #endif /* defined(TARGET_PPC64) */ > > void helper_store_pidr(CPUPPCState *env, target_ulong val) > diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h > index b5a5bc6895..7acf7df451 100644 > --- a/target/ppc/spr_common.h > +++ b/target/ppc/spr_common.h > @@ -194,6 +194,8 @@ void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); > void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); > void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); > void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); > +void spr_read_tfmr(DisasContext *ctx, int sprn, int gprn); > +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); > void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); > #endif > > diff --git a/target/ppc/translate.c b/target/ppc/translate.c > index 388337f81b..e235334944 100644 > --- a/target/ppc/translate.c > +++ b/target/ppc/translate.c > @@ -1173,6 +1173,16 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) > tcg_temp_free(hmer); > } > > +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) > +{ > + gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env); > +} > + > +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) > +{ > + gen_helper_store_tfmr(cpu_env, cpu_gpr[gprn]); > +} > + > void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) > { > gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
On 8/11/22 13:40, Nicholas Piggin wrote: > The chiptod is a pervasive facility which can keep a time, synchronise > it across multiple chips, and can move that time to or from the core > timebase units. > > This adds a very basic initial emulation of chiptod registers. The > interesting thing about chiptod is that it targets cores and interacts > with core registers (e.g., TB, TFMR). So far there is no actual time > keeping or TB interaction, but core targeting and initial TFMR is > implemented. > > This implements enough for skiboot to boot and go through the chiptod > code (with a small patch to remove QUIRK_NO_CHIPTOD from qemu). > > POWER10 is much the same, not implemented yet because skiboot uses a > different core target addressing mode (due to hardware issues) that is > not implemented yet. > > This is not completely tidy yet, just thought I would see if there are > comments, particularly with the core TFMR interactions. > > Thanks, > Nick Apart from Cedric's comment about sending a separated TFMR I have a few other small comments: > > --- > hw/ppc/meson.build | 1 + > hw/ppc/pnv.c | 9 + > hw/ppc/pnv_chiptod.c | 320 +++++++++++++++++++++++++++++++++++ > hw/ppc/pnv_xscom.c | 2 + > hw/ppc/trace-events | 4 + > include/hw/ppc/pnv.h | 2 + > include/hw/ppc/pnv_chiptod.h | 51 ++++++ > include/hw/ppc/pnv_xscom.h | 6 + > target/ppc/cpu.h | 13 ++ > target/ppc/cpu_init.c | 2 +- > target/ppc/helper.h | 2 + > target/ppc/misc_helper.c | 25 +++ > target/ppc/spr_common.h | 2 + > target/ppc/translate.c | 10 ++ > 14 files changed, 448 insertions(+), 1 deletion(-) > create mode 100644 hw/ppc/pnv_chiptod.c > create mode 100644 include/hw/ppc/pnv_chiptod.h > > diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build > index 62801923f3..7eb5031055 100644 > --- a/hw/ppc/meson.build > +++ b/hw/ppc/meson.build > @@ -45,6 +45,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( > 'pnv_core.c', > 'pnv_lpc.c', > 'pnv_psi.c', > + 'pnv_chiptod.c', > 'pnv_occ.c', > 'pnv_sbe.c', > 'pnv_bmc.c', > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c > index 7ff1f464d3..bdd641381c 100644 > --- a/hw/ppc/pnv.c > +++ b/hw/ppc/pnv.c > @@ -1395,6 +1395,8 @@ static void pnv_chip_power9_instance_init(Object *obj) > > object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC); > > + object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD); > + > object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC); > > object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE); > @@ -1539,6 +1541,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) > chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", > (uint64_t) PNV9_LPCM_BASE(chip)); > > + /* ChipTOD */ > + if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) { > + return; > + } > + 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; > diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c > new file mode 100644 > index 0000000000..9ef463e640 > --- /dev/null > +++ b/hw/ppc/pnv_chiptod.c > @@ -0,0 +1,320 @@ > +/* > + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour > + * > + * Copyright (c) 2022, IBM Corporation. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License, version 2, as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include "qemu/osdep.h" > +#include "target/ppc/cpu.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > +#include "qemu/module.h" > +#include "hw/irq.h" > +#include "hw/qdev-properties.h" > +#include "hw/ppc/fdt.h" > +#include "hw/ppc/pnv.h" > +#include "hw/ppc/pnv_xscom.h" > +#include "hw/ppc/pnv_chiptod.h" > +#include "trace.h" > + > +#include <libfdt.h> > + > +/* TOD chip XSCOM addresses */ > +#define TOD_MASTER_PATH_CTRL 0x00000000 /* Master Path ctrl reg */ > +#define TOD_PRI_PORT0_CTRL 0x00000001 /* Primary port0 ctrl reg */ > +#define TOD_PRI_PORT1_CTRL 0x00000002 /* Primary port1 ctrl reg */ > +#define TOD_SEC_PORT0_CTRL 0x00000003 /* Secondary p0 ctrl reg */ > +#define TOD_SEC_PORT1_CTRL 0x00000004 /* Secondary p1 ctrl reg */ > +#define TOD_SLAVE_PATH_CTRL 0x00000005 /* Slave Path ctrl reg */ > +#define TOD_INTERNAL_PATH_CTRL 0x00000006 /* Internal Path ctrl reg */ > + > +/* -- TOD primary/secondary master/slave control register -- */ > +#define TOD_PSMS_CTRL 0x00000007 > +#define TOD_PSMSC_PM_TOD_SELECT PPC_BIT(1) /* Primary Master TOD */ > +#define TOD_PSMSC_PM_DRAW_SELECT PPC_BIT(2) /* Primary Master Drawer */ > +#define TOD_PSMSC_SM_TOD_SELECT PPC_BIT(9) /* Secondary Master TOD */ > +#define TOD_PSMSC_SM_DRAW_SELECT PPC_BIT(10) /* Secondary Master Draw */ > + > +/* -- TOD primary/secondary master/slave status register -- */ > +#define TOD_STATUS 0x00000008 > +#define TOD_ST_TOPOLOGY_SELECT PPC_BITMASK(0, 2) > +#define TOD_ST_MPATH0_STEP_VALID PPC_BIT(6) /* MasterPath0 step valid */ > +#define TOD_ST_MPATH1_STEP_VALID PPC_BIT(7) /* MasterPath1 step valid */ > +#define TOD_ST_SPATH0_STEP_VALID PPC_BIT(8) /* SlavePath0 step valid */ > +#define TOD_ST_SPATH1_STEP_VALID PPC_BIT(10) /* SlavePath1 step valid */ > +/* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */ > +#define TOD_ST_PRI_MPATH_SELECT PPC_BIT(12) /* Primary MPath Select */ > +#define TOD_ST_PRI_SPATH_SELECT PPC_BIT(15) /* Primary SPath Select */ > +/* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */ > +#define TOD_ST_SEC_MPATH_SELECT PPC_BIT(16) /* Secondary MPath Select */ > +#define TOD_ST_SEC_SPATH_SELECT PPC_BIT(19) /* Secondary SPath Select */ > +#define TOD_ST_ACTIVE_MASTER PPC_BIT(23) > +#define TOD_ST_BACKUP_MASTER PPC_BIT(24) > + > +/* TOD chip XSCOM addresses */ > +#define TOD_CHIP_CTRL 0x00000010 /* Chip control register */ > +#define TOD_TTYPE_0 0x00000011 > +#define TOD_TTYPE_1 0x00000012 /* PSS switch */ > +#define TOD_TTYPE_2 0x00000013 /* Enable step checkers */ > +#define TOD_TTYPE_3 0x00000014 /* Request TOD */ > +#define TOD_TTYPE_4 0x00000015 /* Send TOD */ > +#define TOD_TTYPE_5 0x00000016 /* Invalidate TOD */ > +#define TOD_CHIPTOD_TO_TB 0x00000017 > +#define TOD_LOAD_TOD_MOD 0x00000018 > +#define TOD_CHIPTOD_VALUE 0x00000020 > +#define TOD_CHIPTOD_LOAD_TB 0x00000021 > +#define TOD_CHIPTOD_FSM 0x00000024 > + > +/* -- TOD PIB Master reg -- */ > +#define TOD_PIB_MASTER 0x00000027 > +#define TOD_PIBM_ADDR_CFG_MCAST PPC_BIT(25) > +#define TOD_PIBM_ADDR_CFG_SLADDR PPC_BITMASK(26, 31) > +#define TOD_PIBM_TTYPE4_SEND_MODE PPC_BIT(32) > +#define TOD_PIBM_TTYPE4_SEND_ENBL PPC_BIT(33) > + > +/* -- TOD Error interrupt register -- */ > +#define TOD_ERROR 0x00000030 > +/* SYNC errors */ > +#define TOD_ERR_CRMO_PARITY PPC_BIT(0) > +#define TOD_ERR_OSC0_PARITY PPC_BIT(1) > +#define TOD_ERR_OSC1_PARITY PPC_BIT(2) > +#define TOD_ERR_PPORT0_CREG_PARITY PPC_BIT(3) > +#define TOD_ERR_PPORT1_CREG_PARITY PPC_BIT(4) > +#define TOD_ERR_SPORT0_CREG_PARITY PPC_BIT(5) > +#define TOD_ERR_SPORT1_CREG_PARITY PPC_BIT(6) > +#define TOD_ERR_SPATH_CREG_PARITY PPC_BIT(7) > +#define TOD_ERR_IPATH_CREG_PARITY PPC_BIT(8) > +#define TOD_ERR_PSMS_CREG_PARITY PPC_BIT(9) > +#define TOD_ERR_CRITC_PARITY PPC_BIT(13) > +#define TOD_ERR_MP0_STEP_CHECK PPC_BIT(14) > +#define TOD_ERR_MP1_STEP_CHECK PPC_BIT(15) > +#define TOD_ERR_PSS_HAMMING_DISTANCE PPC_BIT(18) > +#define TOD_ERR_DELAY_COMPL_PARITY PPC_BIT(22) > +/* CNTR errors */ > +#define TOD_ERR_CTCR_PARITY PPC_BIT(32) > +#define TOD_ERR_TOD_SYNC_CHECK PPC_BIT(33) > +#define TOD_ERR_TOD_FSM_PARITY PPC_BIT(34) > +#define TOD_ERR_TOD_REGISTER_PARITY PPC_BIT(35) > +#define TOD_ERR_OVERFLOW_YR2042 PPC_BIT(36) > +#define TOD_ERR_TOD_WOF_LSTEP_PARITY PPC_BIT(37) > +#define TOD_ERR_TTYPE0_RECVD PPC_BIT(38) > +#define TOD_ERR_TTYPE1_RECVD PPC_BIT(39) > +#define TOD_ERR_TTYPE2_RECVD PPC_BIT(40) > +#define TOD_ERR_TTYPE3_RECVD PPC_BIT(41) > +#define TOD_ERR_TTYPE4_RECVD PPC_BIT(42) > +#define TOD_ERR_TTYPE5_RECVD PPC_BIT(43) > + > +/* -- TOD Error interrupt register -- */ > +#define TOD_ERROR_INJECT 0x00000031 > + > +/* PC unit PIB address which recieves the timebase transfer from TOD */ > +#define PC_TOD 0x4A3 I think these defines would be better in pnv_chiptod.h. > + > +static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); > + uint32_t offset = addr >> 3; > + uint64_t val = 0; > + > + switch (offset) { > + case TOD_ERROR: > + val = chiptod->tod_error; > + break; > + case TOD_CHIPTOD_FSM: > + if (chiptod->tod_state == tod_running) { > + val |= PPC_BIT(4); > + } > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr >> 3); > + } > + > + trace_pnv_chiptod_xscom_read(addr >> 3, val); > + > + return val; > +} > + > +static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); > + uint32_t offset = addr >> 3; > + > + trace_pnv_chiptod_xscom_write(addr >> 3, val); > + > + switch (offset) { > + case TOD_PIB_MASTER: > + if (val & PPC_BIT(35)) { > + /* SCOM addressing */ > + /* XXX: todo */ > + } else { > + /* PIR addressing */ > + /* This implementation is POWER9 specific */ > + uint32_t pir = (GETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, val) & 0x1f) << 2; > + CPUState *cs; > + > + chiptod->slave_cpu_target = NULL; > + CPU_FOREACH(cs) { > + PowerPCCPU *cpu = POWERPC_CPU(cs); > + CPUPPCState *env = &cpu->env; > + if (env->spr_cb[SPR_PIR].default_value == pir) { > + chiptod->slave_cpu_target = cpu; > + break; > + } > + } > + if (chiptod->slave_cpu_target == NULL) { > + printf("CHIPTOD NO SLAVE!\n"); Instead of printfs (which I believe you were using for quick debugging) you can use a trace. > + } > + } > + break; > + case TOD_ERROR: > + chiptod->tod_error &= ~val; > + break; > + case TOD_LOAD_TOD_MOD: > + chiptod->tod_state = tod_not_set; > + break; > + case TOD_CHIPTOD_LOAD_TB: > + chiptod->tod_state = tod_running; > + break; > + case TOD_CHIPTOD_TO_TB: > + if (chiptod->slave_cpu_target == NULL) { > + printf("CHIPTOD NO SLAVE!\n"); Same here. > + } else { > + PowerPCCPU *cpu = chiptod->slave_cpu_target; > + CPUPPCState *env = &cpu->env; > + uint64_t tfmr = env->spr[SPR_TFMR]; > + > + if (!(tfmr & TFMR_MOVE_CHIP_TOD_TO_TB)) { > + printf("CORE TFMR NOT READY TO RECEIVE!\n"); Same here. > + break; > + } > + env->spr[SPR_TFMR] &= ~TFMR_MOVE_CHIP_TOD_TO_TB; > + env->spr[SPR_TFMR] |= TFMR_TB_VALID; > + } > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr >> 3); > + } > +} > + > +static const MemoryRegionOps pnv_chiptod_xscom_ops = { > + .read = pnv_chiptod_xscom_read, > + .write = pnv_chiptod_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 int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt, > + int xscom_offset, > + const char compat[], size_t compat_size) > +{ > + static bool primary = false; > + char *name; Declare this as g_autofree char *name = NULL; And then you can get rid of the g_free(name) down there. > + int offset; > + uint32_t lpc_pcba = PNV9_XSCOM_CHIPTOD_BASE; > + uint32_t reg[] = { > + cpu_to_be32(lpc_pcba), > + cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE) > + }; > + > + name = g_strdup_printf("chiptod@%x", lpc_pcba); > + offset = fdt_add_subnode(fdt, xscom_offset, name); > + _FDT(offset); > + g_free(name); > + > + if (!primary) { > + primary = true; > + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); > + } I'm confused about how you're using the 'primary' flag. It is being declared as 'false' up there, it's never used/changed until you check here for '!primary', which is always going to be the case, then you're setting it to true and writing the FDT. I believe you can get rid of the 'primary' var altogether and simply write the FDT all the time. > + > + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); > + _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size))); > + return 0; > +} > + > +static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, > + int xscom_offset) > +{ > + const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod"; > + > + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); > +} > + > +static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) > +{ > +// PnvChipTODClass *psc = PNV_CHIPTOD_CLASS(klass); Remove this commented line please :D > + DeviceClass *dc = DEVICE_CLASS(klass); > + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); > + > + dc->desc = "PowerNV CHIPTOD Controller (POWER9)"; > + > + xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; > +} > + > +static const TypeInfo pnv_chiptod_power9_type_info = { > + .name = TYPE_PNV9_CHIPTOD, > + .parent = TYPE_PNV_CHIPTOD, > + .instance_size = sizeof(PnvChipTOD), > + .class_init = pnv_chiptod_power9_class_init, > + .interfaces = (InterfaceInfo[]) { > + { TYPE_PNV_XSCOM_INTERFACE }, > + { } > + } > +}; > + > +static void pnv_chiptod_realize(DeviceState *dev, Error **errp) > +{ > + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); > +// PnvChipTODClass *psc = PNV_CHIPTOD_GET_CLASS(chiptod); Same here. Thanks, Daniel > + > + chiptod->tod_state = tod_running; > + > + /* XScom regions for CHIPTOD registers */ > + pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), > + &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", > + PNV_XSCOM_CHIPTOD_SIZE); > +} > + > +static void pnv_chiptod_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = pnv_chiptod_realize; > + dc->desc = "PowerNV ChipTOD Controller"; > + dc->user_creatable = false; > +} > + > +static const TypeInfo pnv_chiptod_type_info = { > + .name = TYPE_PNV_CHIPTOD, > + .parent = TYPE_DEVICE, > + .instance_size = sizeof(PnvChipTOD), > + .class_init = pnv_chiptod_class_init, > + .class_size = sizeof(PnvChipTODClass), > + .abstract = true, > +}; > + > +static void pnv_chiptod_register_types(void) > +{ > + type_register_static(&pnv_chiptod_type_info); > + type_register_static(&pnv_chiptod_power9_type_info); > +} > + > +type_init(pnv_chiptod_register_types); > diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c > index 79f10de57f..10d2d9edd4 100644 > --- a/hw/ppc/pnv_xscom.c > +++ b/hw/ppc/pnv_xscom.c > @@ -297,6 +297,8 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, > _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); > if (chip->chip_id == 0) { > _FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0))); > + } else if (chip->chip_id == 1) { > + _FDT((fdt_setprop(fdt, xscom_offset, "secondary", NULL, 0))); > } > > args.fdt = fdt; > diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events > index f6990439d1..60c0a1c42c 100644 > --- a/hw/ppc/trace-events > +++ b/hw/ppc/trace-events > @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" > vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 > vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 > > +# pnv_chiptod.c > +pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > +pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > + > # pnv_sbe.c > pnv_sbe_xscom_ctrl_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > pnv_sbe_xscom_ctrl_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h > index 37c303bf36..501e9b0138 100644 > --- a/include/hw/ppc/pnv.h > +++ b/include/hw/ppc/pnv.h > @@ -26,6 +26,7 @@ > #include "hw/ppc/pnv_lpc.h" > #include "hw/ppc/pnv_pnor.h" > #include "hw/ppc/pnv_psi.h" > +#include "hw/ppc/pnv_chiptod.h" > #include "hw/ppc/pnv_occ.h" > #include "hw/ppc/pnv_sbe.h" > #include "hw/ppc/pnv_homer.h" > @@ -100,6 +101,7 @@ struct Pnv9Chip { > PnvXive xive; > Pnv9Psi psi; > PnvLpcController lpc; > + PnvChipTOD chiptod; > PnvOCC occ; > PnvSBE sbe; > PnvHomer homer; > diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h > new file mode 100644 > index 0000000000..44700bbd81 > --- /dev/null > +++ b/include/hw/ppc/pnv_chiptod.h > @@ -0,0 +1,51 @@ > +/* > + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour > + * > + * Copyright (c) 2022, IBM Corporation. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef PPC_PNV_CHIPTOD_H > +#define PPC_PNV_CHIPTOD_H > + > +#include "qom/object.h" > + > +#define TYPE_PNV_CHIPTOD "pnv-chiptod" > +OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) > +#define TYPE_PNV9_CHIPTOD TYPE_PNV_CHIPTOD "-POWER9" > +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) > +#define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" > +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) > + > +enum tod_state { > + tod_running = 1, > + tod_not_set, > +}; > + > +struct PnvChipTOD { > + DeviceState xd; > + > + enum tod_state tod_state; > + uint64_t tod_error; > + PowerPCCPU *slave_cpu_target; > + > + MemoryRegion xscom_regs; > +}; > + > +struct PnvChipTODClass { > + DeviceClass parent_class; > +}; > + > +#endif /* PPC_PNV_CHIPTOD_H */ > diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h > index c6e9ef8dd2..a8bd15401f 100644 > --- a/include/hw/ppc/pnv_xscom.h > +++ b/include/hw/ppc/pnv_xscom.h > @@ -63,6 +63,9 @@ struct PnvXScomInterfaceClass { > #define PNV_XSCOM_PSIHB_BASE 0x2010900 > #define PNV_XSCOM_PSIHB_SIZE 0x20 > > +#define PNV_XSCOM_CHIPTOD_BASE 0x0040000 > +#define PNV_XSCOM_CHIPTOD_SIZE 0x31 > + > #define PNV_XSCOM_OCC_BASE 0x0066000 > #define PNV_XSCOM_OCC_SIZE 0x6000 > > @@ -89,6 +92,9 @@ struct PnvXScomInterfaceClass { > ((uint64_t)(((core) & 0x1C) + 0x40) << 22) > #define PNV9_XSCOM_EQ_SIZE 0x100000 > > +#define PNV9_XSCOM_CHIPTOD_BASE PNV_XSCOM_CHIPTOD_BASE > +#define PNV9_XSCOM_CHIPTOD_SIZE PNV_XSCOM_CHIPTOD_SIZE > + > #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE > #define PNV9_XSCOM_OCC_SIZE 0x8000 > > diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h > index a4c893cfad..7edb3447fa 100644 > --- a/target/ppc/cpu.h > +++ b/target/ppc/cpu.h > @@ -1135,6 +1135,11 @@ struct CPUArchState { > uint32_t tlb_need_flush; /* Delayed flush needed */ > #define TLB_NEED_LOCAL_FLUSH 0x1 > #define TLB_NEED_GLOBAL_FLUSH 0x2 > + > +#if defined(TARGET_PPC64) > + /* PowerNV timebase facility */ > + int tfmr_op_timeout; > +#endif > #endif > > /* Other registers */ > @@ -2471,6 +2476,14 @@ enum { > HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), > }; > > +/* TFMR */ > +enum { > + TFMR_LOAD_TOD_MOD = PPC_BIT(16), > + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), > + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), > + TFMR_TB_VALID = PPC_BIT(41), > +}; > + > /*****************************************************************************/ > > #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) > diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c > index d1493a660c..6d555e16b9 100644 > --- a/target/ppc/cpu_init.c > +++ b/target/ppc/cpu_init.c > @@ -5392,7 +5392,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) > spr_register_hv(env, SPR_TFMR, "TFMR", > SPR_NOACCESS, SPR_NOACCESS, > SPR_NOACCESS, SPR_NOACCESS, > - &spr_read_generic, &spr_write_generic, > + &spr_read_tfmr, &spr_write_tfmr, > 0x00000000); > spr_register_hv(env, SPR_LPIDR, "LPIDR", > SPR_NOACCESS, SPR_NOACCESS, > diff --git a/target/ppc/helper.h b/target/ppc/helper.h > index 159b352f6e..643c9457c7 100644 > --- a/target/ppc/helper.h > +++ b/target/ppc/helper.h > @@ -720,6 +720,8 @@ DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env) > DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl) > DEF_HELPER_2(book3s_msgsndp, void, env, tl) > DEF_HELPER_2(book3s_msgclrp, void, env, tl) > +DEF_HELPER_1(load_tfmr, tl, env) > +DEF_HELPER_2(store_tfmr, void, env, tl) > #endif > DEF_HELPER_2(store_sdr1, void, env, tl) > DEF_HELPER_2(store_pidr, void, env, tl) > diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c > index b0a5e7ce76..d979d936b3 100644 > --- a/target/ppc/misc_helper.c > +++ b/target/ppc/misc_helper.c > @@ -191,6 +191,31 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) > env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); > } > } > + > +target_ulong helper_load_tfmr(CPUPPCState *env) > +{ > + if (env->tfmr_op_timeout) { > + env->tfmr_op_timeout--; > + if (!env->tfmr_op_timeout) { > + env->spr[SPR_TFMR] &= ~(TFMR_LOAD_TOD_MOD | > + TFMR_CLEAR_TB_ERRORS); > + } > + } > + > + return env->spr[SPR_TFMR]; > +} > + > +void helper_store_tfmr(CPUPPCState *env, target_ulong val) > +{ > + env->spr[SPR_TFMR] = val; > + > + if (val & TFMR_LOAD_TOD_MOD) { > + env->tfmr_op_timeout = 3; > + } > + if (val & TFMR_CLEAR_TB_ERRORS) { > + env->tfmr_op_timeout = 3; > + } > +} > #endif /* defined(TARGET_PPC64) */ > > void helper_store_pidr(CPUPPCState *env, target_ulong val) > diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h > index b5a5bc6895..7acf7df451 100644 > --- a/target/ppc/spr_common.h > +++ b/target/ppc/spr_common.h > @@ -194,6 +194,8 @@ void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); > void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); > void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); > void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); > +void spr_read_tfmr(DisasContext *ctx, int sprn, int gprn); > +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); > void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); > #endif > > diff --git a/target/ppc/translate.c b/target/ppc/translate.c > index 388337f81b..e235334944 100644 > --- a/target/ppc/translate.c > +++ b/target/ppc/translate.c > @@ -1173,6 +1173,16 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) > tcg_temp_free(hmer); > } > > +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) > +{ > + gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env); > +} > + > +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) > +{ > + gen_helper_store_tfmr(cpu_env, cpu_gpr[gprn]); > +} > + > void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) > { > gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);
On 8/11/22 19:30, Cédric Le Goater wrote: > On 8/11/22 18:40, Nicholas Piggin wrote: >> The chiptod is a pervasive facility which can keep a time, synchronise >> it across multiple chips, and can move that time to or from the core >> timebase units. >> >> This adds a very basic initial emulation of chiptod registers. The >> interesting thing about chiptod is that it targets cores and interacts >> with core registers (e.g., TB, TFMR). So far there is no actual time >> keeping or TB interaction, but core targeting and initial TFMR is >> implemented. >> >> This implements enough for skiboot to boot and go through the chiptod >> code (with a small patch to remove QUIRK_NO_CHIPTOD from qemu). >> >> POWER10 is much the same, not implemented yet because skiboot uses a >> different core target addressing mode (due to hardware issues) that is >> not implemented yet. >> >> This is not completely tidy yet, just thought I would see if there are >> comments, particularly with the core TFMR interactions. > > The TFMR implementation should be in its own patch. This looked like an interesting extension to the baremetal models. Did you abandon the idea ? if not, it would be good material for QEMU 8.1. Thanks, C. > >> >> Thanks, >> Nick >> >> --- >> hw/ppc/meson.build | 1 + >> hw/ppc/pnv.c | 9 + >> hw/ppc/pnv_chiptod.c | 320 +++++++++++++++++++++++++++++++++++ >> hw/ppc/pnv_xscom.c | 2 + >> hw/ppc/trace-events | 4 + >> include/hw/ppc/pnv.h | 2 + >> include/hw/ppc/pnv_chiptod.h | 51 ++++++ >> include/hw/ppc/pnv_xscom.h | 6 + >> target/ppc/cpu.h | 13 ++ >> target/ppc/cpu_init.c | 2 +- >> target/ppc/helper.h | 2 + >> target/ppc/misc_helper.c | 25 +++ >> target/ppc/spr_common.h | 2 + >> target/ppc/translate.c | 10 ++ >> 14 files changed, 448 insertions(+), 1 deletion(-) >> create mode 100644 hw/ppc/pnv_chiptod.c >> create mode 100644 include/hw/ppc/pnv_chiptod.h >> >> diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build >> index 62801923f3..7eb5031055 100644 >> --- a/hw/ppc/meson.build >> +++ b/hw/ppc/meson.build >> @@ -45,6 +45,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( >> 'pnv_core.c', >> 'pnv_lpc.c', >> 'pnv_psi.c', >> + 'pnv_chiptod.c', >> 'pnv_occ.c', >> 'pnv_sbe.c', >> 'pnv_bmc.c', >> diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c >> index 7ff1f464d3..bdd641381c 100644 >> --- a/hw/ppc/pnv.c >> +++ b/hw/ppc/pnv.c >> @@ -1395,6 +1395,8 @@ static void pnv_chip_power9_instance_init(Object *obj) >> object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC); >> + object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD); >> + >> object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC); >> object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE); >> @@ -1539,6 +1541,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) >> chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", >> (uint64_t) PNV9_LPCM_BASE(chip)); >> + /* ChipTOD */ >> + if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) { >> + return; >> + } >> + 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; >> diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c >> new file mode 100644 >> index 0000000000..9ef463e640 >> --- /dev/null >> +++ b/hw/ppc/pnv_chiptod.c >> @@ -0,0 +1,320 @@ >> +/* >> + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour >> + * >> + * Copyright (c) 2022, IBM Corporation. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License, version 2, as >> + * published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, see <http://www.gnu.org/licenses/>. >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "target/ppc/cpu.h" >> +#include "qapi/error.h" >> +#include "qemu/log.h" >> +#include "qemu/module.h" >> +#include "hw/irq.h" >> +#include "hw/qdev-properties.h" >> +#include "hw/ppc/fdt.h" >> +#include "hw/ppc/pnv.h" >> +#include "hw/ppc/pnv_xscom.h" >> +#include "hw/ppc/pnv_chiptod.h" >> +#include "trace.h" >> + >> +#include <libfdt.h> >> + >> +/* TOD chip XSCOM addresses */ >> +#define TOD_MASTER_PATH_CTRL 0x00000000 /* Master Path ctrl reg */ >> +#define TOD_PRI_PORT0_CTRL 0x00000001 /* Primary port0 ctrl reg */ >> +#define TOD_PRI_PORT1_CTRL 0x00000002 /* Primary port1 ctrl reg */ >> +#define TOD_SEC_PORT0_CTRL 0x00000003 /* Secondary p0 ctrl reg */ >> +#define TOD_SEC_PORT1_CTRL 0x00000004 /* Secondary p1 ctrl reg */ >> +#define TOD_SLAVE_PATH_CTRL 0x00000005 /* Slave Path ctrl reg */ >> +#define TOD_INTERNAL_PATH_CTRL 0x00000006 /* Internal Path ctrl reg */ >> + >> +/* -- TOD primary/secondary master/slave control register -- */ >> +#define TOD_PSMS_CTRL 0x00000007 >> +#define TOD_PSMSC_PM_TOD_SELECT PPC_BIT(1) /* Primary Master TOD */ >> +#define TOD_PSMSC_PM_DRAW_SELECT PPC_BIT(2) /* Primary Master Drawer */ >> +#define TOD_PSMSC_SM_TOD_SELECT PPC_BIT(9) /* Secondary Master TOD */ >> +#define TOD_PSMSC_SM_DRAW_SELECT PPC_BIT(10) /* Secondary Master Draw */ >> + >> +/* -- TOD primary/secondary master/slave status register -- */ >> +#define TOD_STATUS 0x00000008 >> +#define TOD_ST_TOPOLOGY_SELECT PPC_BITMASK(0, 2) >> +#define TOD_ST_MPATH0_STEP_VALID PPC_BIT(6) /* MasterPath0 step valid */ >> +#define TOD_ST_MPATH1_STEP_VALID PPC_BIT(7) /* MasterPath1 step valid */ >> +#define TOD_ST_SPATH0_STEP_VALID PPC_BIT(8) /* SlavePath0 step valid */ >> +#define TOD_ST_SPATH1_STEP_VALID PPC_BIT(10) /* SlavePath1 step valid */ >> +/* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */ >> +#define TOD_ST_PRI_MPATH_SELECT PPC_BIT(12) /* Primary MPath Select */ >> +#define TOD_ST_PRI_SPATH_SELECT PPC_BIT(15) /* Primary SPath Select */ >> +/* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */ >> +#define TOD_ST_SEC_MPATH_SELECT PPC_BIT(16) /* Secondary MPath Select */ >> +#define TOD_ST_SEC_SPATH_SELECT PPC_BIT(19) /* Secondary SPath Select */ >> +#define TOD_ST_ACTIVE_MASTER PPC_BIT(23) >> +#define TOD_ST_BACKUP_MASTER PPC_BIT(24) >> + >> +/* TOD chip XSCOM addresses */ >> +#define TOD_CHIP_CTRL 0x00000010 /* Chip control register */ >> +#define TOD_TTYPE_0 0x00000011 >> +#define TOD_TTYPE_1 0x00000012 /* PSS switch */ >> +#define TOD_TTYPE_2 0x00000013 /* Enable step checkers */ >> +#define TOD_TTYPE_3 0x00000014 /* Request TOD */ >> +#define TOD_TTYPE_4 0x00000015 /* Send TOD */ >> +#define TOD_TTYPE_5 0x00000016 /* Invalidate TOD */ >> +#define TOD_CHIPTOD_TO_TB 0x00000017 >> +#define TOD_LOAD_TOD_MOD 0x00000018 >> +#define TOD_CHIPTOD_VALUE 0x00000020 >> +#define TOD_CHIPTOD_LOAD_TB 0x00000021 >> +#define TOD_CHIPTOD_FSM 0x00000024 >> + >> +/* -- TOD PIB Master reg -- */ >> +#define TOD_PIB_MASTER 0x00000027 >> +#define TOD_PIBM_ADDR_CFG_MCAST PPC_BIT(25) >> +#define TOD_PIBM_ADDR_CFG_SLADDR PPC_BITMASK(26, 31) >> +#define TOD_PIBM_TTYPE4_SEND_MODE PPC_BIT(32) >> +#define TOD_PIBM_TTYPE4_SEND_ENBL PPC_BIT(33) >> + >> +/* -- TOD Error interrupt register -- */ >> +#define TOD_ERROR 0x00000030 >> +/* SYNC errors */ >> +#define TOD_ERR_CRMO_PARITY PPC_BIT(0) >> +#define TOD_ERR_OSC0_PARITY PPC_BIT(1) >> +#define TOD_ERR_OSC1_PARITY PPC_BIT(2) >> +#define TOD_ERR_PPORT0_CREG_PARITY PPC_BIT(3) >> +#define TOD_ERR_PPORT1_CREG_PARITY PPC_BIT(4) >> +#define TOD_ERR_SPORT0_CREG_PARITY PPC_BIT(5) >> +#define TOD_ERR_SPORT1_CREG_PARITY PPC_BIT(6) >> +#define TOD_ERR_SPATH_CREG_PARITY PPC_BIT(7) >> +#define TOD_ERR_IPATH_CREG_PARITY PPC_BIT(8) >> +#define TOD_ERR_PSMS_CREG_PARITY PPC_BIT(9) >> +#define TOD_ERR_CRITC_PARITY PPC_BIT(13) >> +#define TOD_ERR_MP0_STEP_CHECK PPC_BIT(14) >> +#define TOD_ERR_MP1_STEP_CHECK PPC_BIT(15) >> +#define TOD_ERR_PSS_HAMMING_DISTANCE PPC_BIT(18) >> +#define TOD_ERR_DELAY_COMPL_PARITY PPC_BIT(22) >> +/* CNTR errors */ >> +#define TOD_ERR_CTCR_PARITY PPC_BIT(32) >> +#define TOD_ERR_TOD_SYNC_CHECK PPC_BIT(33) >> +#define TOD_ERR_TOD_FSM_PARITY PPC_BIT(34) >> +#define TOD_ERR_TOD_REGISTER_PARITY PPC_BIT(35) >> +#define TOD_ERR_OVERFLOW_YR2042 PPC_BIT(36) >> +#define TOD_ERR_TOD_WOF_LSTEP_PARITY PPC_BIT(37) >> +#define TOD_ERR_TTYPE0_RECVD PPC_BIT(38) >> +#define TOD_ERR_TTYPE1_RECVD PPC_BIT(39) >> +#define TOD_ERR_TTYPE2_RECVD PPC_BIT(40) >> +#define TOD_ERR_TTYPE3_RECVD PPC_BIT(41) >> +#define TOD_ERR_TTYPE4_RECVD PPC_BIT(42) >> +#define TOD_ERR_TTYPE5_RECVD PPC_BIT(43) >> + >> +/* -- TOD Error interrupt register -- */ >> +#define TOD_ERROR_INJECT 0x00000031 >> + >> +/* PC unit PIB address which recieves the timebase transfer from TOD */ >> +#define PC_TOD 0x4A3 >> + >> +static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr, >> + unsigned size) >> +{ >> + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); >> + uint32_t offset = addr >> 3; >> + uint64_t val = 0; >> + >> + switch (offset) { >> + case TOD_ERROR: >> + val = chiptod->tod_error; >> + break; >> + case TOD_CHIPTOD_FSM: >> + if (chiptod->tod_state == tod_running) { >> + val |= PPC_BIT(4); >> + } >> + break; >> + default: >> + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" >> + HWADDR_PRIx "\n", addr >> 3); >> + } >> + >> + trace_pnv_chiptod_xscom_read(addr >> 3, val); >> + >> + return val; >> +} >> + >> +static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, >> + uint64_t val, unsigned size) >> +{ >> + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); >> + uint32_t offset = addr >> 3; >> + >> + trace_pnv_chiptod_xscom_write(addr >> 3, val); >> + >> + switch (offset) { >> + case TOD_PIB_MASTER: >> + if (val & PPC_BIT(35)) { >> + /* SCOM addressing */ >> + /* XXX: todo */ >> + } else { >> + /* PIR addressing */ >> + /* This implementation is POWER9 specific */ >> + uint32_t pir = (GETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, val) & 0x1f) << 2; >> + CPUState *cs; >> + >> + chiptod->slave_cpu_target = NULL; >> + CPU_FOREACH(cs) { >> + PowerPCCPU *cpu = POWERPC_CPU(cs); >> + CPUPPCState *env = &cpu->env; >> + if (env->spr_cb[SPR_PIR].default_value == pir) { >> + chiptod->slave_cpu_target = cpu; >> + break; >> + } >> + } > > You could use : > > chiptod->slave_cpu_target = ppc_get_vcpu_by_pir(pir). > > >> + if (chiptod->slave_cpu_target == NULL) { >> + printf("CHIPTOD NO SLAVE!\n"); > > It should be a guest error. > >> + } >> + } >> + break; >> + case TOD_ERROR: >> + chiptod->tod_error &= ~val; >> + break; >> + case TOD_LOAD_TOD_MOD: >> + chiptod->tod_state = tod_not_set; >> + break; >> + case TOD_CHIPTOD_LOAD_TB: >> + chiptod->tod_state = tod_running; >> + break; >> + case TOD_CHIPTOD_TO_TB: >> + if (chiptod->slave_cpu_target == NULL) { >> + printf("CHIPTOD NO SLAVE!\n"); > > It should be a guest error. > >> + } else { >> + PowerPCCPU *cpu = chiptod->slave_cpu_target; >> + CPUPPCState *env = &cpu->env; >> + uint64_t tfmr = env->spr[SPR_TFMR]; >> + >> + if (!(tfmr & TFMR_MOVE_CHIP_TOD_TO_TB)) { >> + printf("CORE TFMR NOT READY TO RECEIVE!\n"); >> + break; >> + } >> + env->spr[SPR_TFMR] &= ~TFMR_MOVE_CHIP_TOD_TO_TB; >> + env->spr[SPR_TFMR] |= TFMR_TB_VALID; >> + } >> + break; >> + default: >> + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" >> 7+ HWADDR_PRIx "\n", addr >> 3); >> + } >> +} >> + >> +static const MemoryRegionOps pnv_chiptod_xscom_ops = { >> + .read = pnv_chiptod_xscom_read, >> + .write = pnv_chiptod_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 int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt, >> + int xscom_offset, >> + const char compat[], size_t compat_size) >> +{ >> + static bool primary = false; >> + char *name; >> + int offset; >> + uint32_t lpc_pcba = PNV9_XSCOM_CHIPTOD_BASE; >> + uint32_t reg[] = { >> + cpu_to_be32(lpc_pcba), >> + cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE) >> + }; >> + >> + name = g_strdup_printf("chiptod@%x", lpc_pcba); >> + offset = fdt_add_subnode(fdt, xscom_offset, name); >> + _FDT(offset); >> + g_free(name); >> + >> + if (!primary) { >> + primary = true; >> + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); >> + } >> + >> + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); >> + _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size))); >> + return 0; >> +} >> + >> +static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, >> + int xscom_offset) >> +{ >> + const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod"; >> + >> + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); >> +} >> + >> +static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) >> +{ >> +// PnvChipTODClass *psc = PNV_CHIPTOD_CLASS(klass); > > That's a left over I guess ? > >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); >> + >> + dc->desc = "PowerNV CHIPTOD Controller (POWER9)"; >> + >> + xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; >> +} >> + >> +static const TypeInfo pnv_chiptod_power9_type_info = { >> + .name = TYPE_PNV9_CHIPTOD, >> + .parent = TYPE_PNV_CHIPTOD, >> + .instance_size = sizeof(PnvChipTOD), >> + .class_init = pnv_chiptod_power9_class_init, >> + .interfaces = (InterfaceInfo[]) { >> + { TYPE_PNV_XSCOM_INTERFACE }, >> + { } >> + } >> +}; >> + >> +static void pnv_chiptod_realize(DeviceState *dev, Error **errp) >> +{ >> + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); >> +// PnvChipTODClass *psc = PNV_CHIPTOD_GET_CLASS(chiptod); >> + >> + chiptod->tod_state = tod_running; >> + >> + /* XScom regions for CHIPTOD registers */ >> + pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), >> + &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", >> + PNV_XSCOM_CHIPTOD_SIZE); >> +} >> + >> +static void pnv_chiptod_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = pnv_chiptod_realize; >> + dc->desc = "PowerNV ChipTOD Controller"; >> + dc->user_creatable = false; >> +} >> + >> +static const TypeInfo pnv_chiptod_type_info = { >> + .name = TYPE_PNV_CHIPTOD, >> + .parent = TYPE_DEVICE, >> + .instance_size = sizeof(PnvChipTOD), >> + .class_init = pnv_chiptod_class_init, >> + .class_size = sizeof(PnvChipTODClass), >> + .abstract = true, >> +}; >> + >> +static void pnv_chiptod_register_types(void) >> +{ >> + type_register_static(&pnv_chiptod_type_info); >> + type_register_static(&pnv_chiptod_power9_type_info); >> +} >> + >> +type_init(pnv_chiptod_register_types); >> diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c >> index 79f10de57f..10d2d9edd4 100644 >> --- a/hw/ppc/pnv_xscom.c >> +++ b/hw/ppc/pnv_xscom.c >> @@ -297,6 +297,8 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, >> _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); >> if (chip->chip_id == 0) { >> _FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0))); >> + } else if (chip->chip_id == 1) { >> + _FDT((fdt_setprop(fdt, xscom_offset, "secondary", NULL, 0))); >> } >> args.fdt = fdt; >> diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events >> index f6990439d1..60c0a1c42c 100644 >> --- a/hw/ppc/trace-events >> +++ b/hw/ppc/trace-events >> @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" >> vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 >> vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 >> +# pnv_chiptod.c >> +pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 >> +pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 >> + >> # pnv_sbe.c >> pnv_sbe_xscom_ctrl_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 >> pnv_sbe_xscom_ctrl_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 >> diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h >> index 37c303bf36..501e9b0138 100644 >> --- a/include/hw/ppc/pnv.h >> +++ b/include/hw/ppc/pnv.h >> @@ -26,6 +26,7 @@ >> #include "hw/ppc/pnv_lpc.h" >> #include "hw/ppc/pnv_pnor.h" >> #include "hw/ppc/pnv_psi.h" >> +#include "hw/ppc/pnv_chiptod.h" >> #include "hw/ppc/pnv_occ.h" >> #include "hw/ppc/pnv_sbe.h" >> #include "hw/ppc/pnv_homer.h" >> @@ -100,6 +101,7 @@ struct Pnv9Chip { >> PnvXive xive; >> Pnv9Psi psi; >> PnvLpcController lpc; >> + PnvChipTOD chiptod; >> PnvOCC occ; >> PnvSBE sbe; >> PnvHomer homer; >> diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h >> new file mode 100644 >> index 0000000000..44700bbd81 >> --- /dev/null >> +++ b/include/hw/ppc/pnv_chiptod.h >> @@ -0,0 +1,51 @@ >> +/* >> + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour >> + * >> + * Copyright (c) 2022, IBM Corporation. >> + * >> + * This library is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU Lesser General Public >> + * License as published by the Free Software Foundation; either >> + * version 2.1 of the License, or (at your option) any later version. >> + * >> + * This library is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> + * Lesser General Public License for more details. >> + * >> + * You should have received a copy of the GNU Lesser General Public >> + * License along with this library; if not, see <http://www.gnu.org/licenses/>. >> + */ >> + >> +#ifndef PPC_PNV_CHIPTOD_H >> +#define PPC_PNV_CHIPTOD_H >> + >> +#include "qom/object.h" >> + >> +#define TYPE_PNV_CHIPTOD "pnv-chiptod" >> +OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) >> +#define TYPE_PNV9_CHIPTOD TYPE_PNV_CHIPTOD "-POWER9" >> +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) >> +#define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" >> +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) >> + >> +enum tod_state { >> + tod_running = 1, >> + tod_not_set, >> +}; >> + >> +struct PnvChipTOD { >> + DeviceState xd; >> + >> + enum tod_state tod_state; >> + uint64_t tod_error; >> + PowerPCCPU *slave_cpu_target; >> + >> + MemoryRegion xscom_regs; >> +}; >> + >> +struct PnvChipTODClass { >> + DeviceClass parent_class; > > Will PnvChipTODClass become useful with P10 ? > >> +}; >> + >> +#endif /* PPC_PNV_CHIPTOD_H */ >> diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h >> index c6e9ef8dd2..a8bd15401f 100644 >> --- a/include/hw/ppc/pnv_xscom.h >> +++ b/include/hw/ppc/pnv_xscom.h >> @@ -63,6 +63,9 @@ struct PnvXScomInterfaceClass { >> #define PNV_XSCOM_PSIHB_BASE 0x2010900 >> #define PNV_XSCOM_PSIHB_SIZE 0x20 >> +#define PNV_XSCOM_CHIPTOD_BASE 0x0040000 >> +#define PNV_XSCOM_CHIPTOD_SIZE 0x31 >> + >> #define PNV_XSCOM_OCC_BASE 0x0066000 >> #define PNV_XSCOM_OCC_SIZE 0x6000 >> @@ -89,6 +92,9 @@ struct PnvXScomInterfaceClass { >> ((uint64_t)(((core) & 0x1C) + 0x40) << 22) >> #define PNV9_XSCOM_EQ_SIZE 0x100000 >> +#define PNV9_XSCOM_CHIPTOD_BASE PNV_XSCOM_CHIPTOD_BASE >> +#define PNV9_XSCOM_CHIPTOD_SIZE PNV_XSCOM_CHIPTOD_SIZE >> + >> #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE >> #define PNV9_XSCOM_OCC_SIZE 0x8000 >> diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h >> index a4c893cfad..7edb3447fa 100644 >> --- a/target/ppc/cpu.h >> +++ b/target/ppc/cpu.h >> @@ -1135,6 +1135,11 @@ struct CPUArchState { >> uint32_t tlb_need_flush; /* Delayed flush needed */ >> #define TLB_NEED_LOCAL_FLUSH 0x1 >> #define TLB_NEED_GLOBAL_FLUSH 0x2 >> + >> +#if defined(TARGET_PPC64) >> + /* PowerNV timebase facility */ >> + int tfmr_op_timeout; >> +#endif >> #endif >> /* Other registers */ >> @@ -2471,6 +2476,14 @@ enum { >> HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), >> }; >> +/* TFMR */ >> +enum { >> + TFMR_LOAD_TOD_MOD = PPC_BIT(16), >> + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), >> + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), >> + TFMR_TB_VALID = PPC_BIT(41), >> +}; >> + >> /*****************************************************************************/ >> #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) >> diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c >> index d1493a660c..6d555e16b9 100644 >> --- a/target/ppc/cpu_init.c >> +++ b/target/ppc/cpu_init.c >> @@ -5392,7 +5392,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) >> spr_register_hv(env, SPR_TFMR, "TFMR", >> SPR_NOACCESS, SPR_NOACCESS, >> SPR_NOACCESS, SPR_NOACCESS, >> - &spr_read_generic, &spr_write_generic, >> + &spr_read_tfmr, &spr_write_tfmr, >> 0x00000000); >> spr_register_hv(env, SPR_LPIDR, "LPIDR", >> SPR_NOACCESS, SPR_NOACCESS, >> diff --git a/target/ppc/helper.h b/target/ppc/helper.h >> index 159b352f6e..643c9457c7 100644 >> --- a/target/ppc/helper.h >> +++ b/target/ppc/helper.h >> @@ -720,6 +720,8 @@ DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env) >> DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl) >> DEF_HELPER_2(book3s_msgsndp, void, env, tl) >> DEF_HELPER_2(book3s_msgclrp, void, env, tl) >> +DEF_HELPER_1(load_tfmr, tl, env) >> +DEF_HELPER_2(store_tfmr, void, env, tl) >> #endif >> DEF_HELPER_2(store_sdr1, void, env, tl) >> DEF_HELPER_2(store_pidr, void, env, tl) >> diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c >> index b0a5e7ce76..d979d936b3 100644 >> --- a/target/ppc/misc_helper.c >> +++ b/target/ppc/misc_helper.c >> @@ -191,6 +191,31 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) >> env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); >> } >> } >> + >> +target_ulong helper_load_tfmr(CPUPPCState *env) >> +{ >> + if (env->tfmr_op_timeout) { >> + env->tfmr_op_timeout--; >> + if (!env->tfmr_op_timeout) { >> + env->spr[SPR_TFMR] &= ~(TFMR_LOAD_TOD_MOD | >> + TFMR_CLEAR_TB_ERRORS); >> + } >> + } >> + >> + return env->spr[SPR_TFMR]; >> +} >> + >> +void helper_store_tfmr(CPUPPCState *env, target_ulong val) >> +{ >> + env->spr[SPR_TFMR] = val; >> + >> + if (val & TFMR_LOAD_TOD_MOD) { >> + env->tfmr_op_timeout = 3; > > why 3 ? > >> + } >> + if (val & TFMR_CLEAR_TB_ERRORS) { >> + env->tfmr_op_timeout = 3; >> + } >> +} > > This should generate an exception if not in HV mode ? > >> #endif /* defined(TARGET_PPC64) */ >> void helper_store_pidr(CPUPPCState *env, target_ulong val) >> diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h >> index b5a5bc6895..7acf7df451 100644 >> --- a/target/ppc/spr_common.h >> +++ b/target/ppc/spr_common.h >> @@ -194,6 +194,8 @@ void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); >> void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); >> void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); >> void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); >> +void spr_read_tfmr(DisasContext *ctx, int sprn, int gprn); >> +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); >> void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); >> #endif >> diff --git a/target/ppc/translate.c b/target/ppc/translate.c >> index 388337f81b..e235334944 100644 >> --- a/target/ppc/translate.c >> +++ b/target/ppc/translate.c >> @@ -1173,6 +1173,16 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) >> tcg_temp_free(hmer); >> } >> +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) >> +{ >> + gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env); >> +} >> + >> +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) >> +{ >> + gen_helper_store_tfmr(cpu_env, cpu_gpr[gprn]); >> +} >> + >> void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) >> { >> gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); >
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 62801923f3..7eb5031055 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -45,6 +45,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv_core.c', 'pnv_lpc.c', 'pnv_psi.c', + 'pnv_chiptod.c', 'pnv_occ.c', 'pnv_sbe.c', 'pnv_bmc.c', diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7ff1f464d3..bdd641381c 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1395,6 +1395,8 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC); + object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD); + object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC); object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE); @@ -1539,6 +1541,13 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", (uint64_t) PNV9_LPCM_BASE(chip)); + /* ChipTOD */ + if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) { + return; + } + 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; diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c new file mode 100644 index 0000000000..9ef463e640 --- /dev/null +++ b/hw/ppc/pnv_chiptod.c @@ -0,0 +1,320 @@ +/* + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour + * + * Copyright (c) 2022, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "target/ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_chiptod.h" +#include "trace.h" + +#include <libfdt.h> + +/* TOD chip XSCOM addresses */ +#define TOD_MASTER_PATH_CTRL 0x00000000 /* Master Path ctrl reg */ +#define TOD_PRI_PORT0_CTRL 0x00000001 /* Primary port0 ctrl reg */ +#define TOD_PRI_PORT1_CTRL 0x00000002 /* Primary port1 ctrl reg */ +#define TOD_SEC_PORT0_CTRL 0x00000003 /* Secondary p0 ctrl reg */ +#define TOD_SEC_PORT1_CTRL 0x00000004 /* Secondary p1 ctrl reg */ +#define TOD_SLAVE_PATH_CTRL 0x00000005 /* Slave Path ctrl reg */ +#define TOD_INTERNAL_PATH_CTRL 0x00000006 /* Internal Path ctrl reg */ + +/* -- TOD primary/secondary master/slave control register -- */ +#define TOD_PSMS_CTRL 0x00000007 +#define TOD_PSMSC_PM_TOD_SELECT PPC_BIT(1) /* Primary Master TOD */ +#define TOD_PSMSC_PM_DRAW_SELECT PPC_BIT(2) /* Primary Master Drawer */ +#define TOD_PSMSC_SM_TOD_SELECT PPC_BIT(9) /* Secondary Master TOD */ +#define TOD_PSMSC_SM_DRAW_SELECT PPC_BIT(10) /* Secondary Master Draw */ + +/* -- TOD primary/secondary master/slave status register -- */ +#define TOD_STATUS 0x00000008 +#define TOD_ST_TOPOLOGY_SELECT PPC_BITMASK(0, 2) +#define TOD_ST_MPATH0_STEP_VALID PPC_BIT(6) /* MasterPath0 step valid */ +#define TOD_ST_MPATH1_STEP_VALID PPC_BIT(7) /* MasterPath1 step valid */ +#define TOD_ST_SPATH0_STEP_VALID PPC_BIT(8) /* SlavePath0 step valid */ +#define TOD_ST_SPATH1_STEP_VALID PPC_BIT(10) /* SlavePath1 step valid */ +/* Primary master/slave path select (0 = PATH_0, 1 = PATH_1) */ +#define TOD_ST_PRI_MPATH_SELECT PPC_BIT(12) /* Primary MPath Select */ +#define TOD_ST_PRI_SPATH_SELECT PPC_BIT(15) /* Primary SPath Select */ +/* Secondary master/slave path select (0 = PATH_0, 1 = PATH_1) */ +#define TOD_ST_SEC_MPATH_SELECT PPC_BIT(16) /* Secondary MPath Select */ +#define TOD_ST_SEC_SPATH_SELECT PPC_BIT(19) /* Secondary SPath Select */ +#define TOD_ST_ACTIVE_MASTER PPC_BIT(23) +#define TOD_ST_BACKUP_MASTER PPC_BIT(24) + +/* TOD chip XSCOM addresses */ +#define TOD_CHIP_CTRL 0x00000010 /* Chip control register */ +#define TOD_TTYPE_0 0x00000011 +#define TOD_TTYPE_1 0x00000012 /* PSS switch */ +#define TOD_TTYPE_2 0x00000013 /* Enable step checkers */ +#define TOD_TTYPE_3 0x00000014 /* Request TOD */ +#define TOD_TTYPE_4 0x00000015 /* Send TOD */ +#define TOD_TTYPE_5 0x00000016 /* Invalidate TOD */ +#define TOD_CHIPTOD_TO_TB 0x00000017 +#define TOD_LOAD_TOD_MOD 0x00000018 +#define TOD_CHIPTOD_VALUE 0x00000020 +#define TOD_CHIPTOD_LOAD_TB 0x00000021 +#define TOD_CHIPTOD_FSM 0x00000024 + +/* -- TOD PIB Master reg -- */ +#define TOD_PIB_MASTER 0x00000027 +#define TOD_PIBM_ADDR_CFG_MCAST PPC_BIT(25) +#define TOD_PIBM_ADDR_CFG_SLADDR PPC_BITMASK(26, 31) +#define TOD_PIBM_TTYPE4_SEND_MODE PPC_BIT(32) +#define TOD_PIBM_TTYPE4_SEND_ENBL PPC_BIT(33) + +/* -- TOD Error interrupt register -- */ +#define TOD_ERROR 0x00000030 +/* SYNC errors */ +#define TOD_ERR_CRMO_PARITY PPC_BIT(0) +#define TOD_ERR_OSC0_PARITY PPC_BIT(1) +#define TOD_ERR_OSC1_PARITY PPC_BIT(2) +#define TOD_ERR_PPORT0_CREG_PARITY PPC_BIT(3) +#define TOD_ERR_PPORT1_CREG_PARITY PPC_BIT(4) +#define TOD_ERR_SPORT0_CREG_PARITY PPC_BIT(5) +#define TOD_ERR_SPORT1_CREG_PARITY PPC_BIT(6) +#define TOD_ERR_SPATH_CREG_PARITY PPC_BIT(7) +#define TOD_ERR_IPATH_CREG_PARITY PPC_BIT(8) +#define TOD_ERR_PSMS_CREG_PARITY PPC_BIT(9) +#define TOD_ERR_CRITC_PARITY PPC_BIT(13) +#define TOD_ERR_MP0_STEP_CHECK PPC_BIT(14) +#define TOD_ERR_MP1_STEP_CHECK PPC_BIT(15) +#define TOD_ERR_PSS_HAMMING_DISTANCE PPC_BIT(18) +#define TOD_ERR_DELAY_COMPL_PARITY PPC_BIT(22) +/* CNTR errors */ +#define TOD_ERR_CTCR_PARITY PPC_BIT(32) +#define TOD_ERR_TOD_SYNC_CHECK PPC_BIT(33) +#define TOD_ERR_TOD_FSM_PARITY PPC_BIT(34) +#define TOD_ERR_TOD_REGISTER_PARITY PPC_BIT(35) +#define TOD_ERR_OVERFLOW_YR2042 PPC_BIT(36) +#define TOD_ERR_TOD_WOF_LSTEP_PARITY PPC_BIT(37) +#define TOD_ERR_TTYPE0_RECVD PPC_BIT(38) +#define TOD_ERR_TTYPE1_RECVD PPC_BIT(39) +#define TOD_ERR_TTYPE2_RECVD PPC_BIT(40) +#define TOD_ERR_TTYPE3_RECVD PPC_BIT(41) +#define TOD_ERR_TTYPE4_RECVD PPC_BIT(42) +#define TOD_ERR_TTYPE5_RECVD PPC_BIT(43) + +/* -- TOD Error interrupt register -- */ +#define TOD_ERROR_INJECT 0x00000031 + +/* PC unit PIB address which recieves the timebase transfer from TOD */ +#define PC_TOD 0x4A3 + +static uint64_t pnv_chiptod_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case TOD_ERROR: + val = chiptod->tod_error; + break; + case TOD_CHIPTOD_FSM: + if (chiptod->tod_state == tod_running) { + val |= PPC_BIT(4); + } + break; + default: + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + + trace_pnv_chiptod_xscom_read(addr >> 3, val); + + return val; +} + +static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(opaque); + uint32_t offset = addr >> 3; + + trace_pnv_chiptod_xscom_write(addr >> 3, val); + + switch (offset) { + case TOD_PIB_MASTER: + if (val & PPC_BIT(35)) { + /* SCOM addressing */ + /* XXX: todo */ + } else { + /* PIR addressing */ + /* This implementation is POWER9 specific */ + uint32_t pir = (GETFIELD(TOD_PIBM_ADDR_CFG_SLADDR, val) & 0x1f) << 2; + CPUState *cs; + + chiptod->slave_cpu_target = NULL; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + if (env->spr_cb[SPR_PIR].default_value == pir) { + chiptod->slave_cpu_target = cpu; + break; + } + } + if (chiptod->slave_cpu_target == NULL) { + printf("CHIPTOD NO SLAVE!\n"); + } + } + break; + case TOD_ERROR: + chiptod->tod_error &= ~val; + break; + case TOD_LOAD_TOD_MOD: + chiptod->tod_state = tod_not_set; + break; + case TOD_CHIPTOD_LOAD_TB: + chiptod->tod_state = tod_running; + break; + case TOD_CHIPTOD_TO_TB: + if (chiptod->slave_cpu_target == NULL) { + printf("CHIPTOD NO SLAVE!\n"); + } else { + PowerPCCPU *cpu = chiptod->slave_cpu_target; + CPUPPCState *env = &cpu->env; + uint64_t tfmr = env->spr[SPR_TFMR]; + + if (!(tfmr & TFMR_MOVE_CHIP_TOD_TO_TB)) { + printf("CORE TFMR NOT READY TO RECEIVE!\n"); + break; + } + env->spr[SPR_TFMR] &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + env->spr[SPR_TFMR] |= TFMR_TB_VALID; + } + break; + default: + qemu_log_mask(LOG_UNIMP, "CHIPTOD Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } +} + +static const MemoryRegionOps pnv_chiptod_xscom_ops = { + .read = pnv_chiptod_xscom_read, + .write = pnv_chiptod_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 int pnv_chiptod_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset, + const char compat[], size_t compat_size) +{ + static bool primary = false; + char *name; + int offset; + uint32_t lpc_pcba = PNV9_XSCOM_CHIPTOD_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE) + }; + + name = g_strdup_printf("chiptod@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + if (!primary) { + primary = true; + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); + } + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, compat_size))); + return 0; +} + +static int pnv_chiptod_power9_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power-chiptod\0ibm,power9-chiptod"; + + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); +} + +static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) +{ +// PnvChipTODClass *psc = PNV_CHIPTOD_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + dc->desc = "PowerNV CHIPTOD Controller (POWER9)"; + + xdc->dt_xscom = pnv_chiptod_power9_dt_xscom; +} + +static const TypeInfo pnv_chiptod_power9_type_info = { + .name = TYPE_PNV9_CHIPTOD, + .parent = TYPE_PNV_CHIPTOD, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_chiptod_realize(DeviceState *dev, Error **errp) +{ + PnvChipTOD *chiptod = PNV_CHIPTOD(dev); +// PnvChipTODClass *psc = PNV_CHIPTOD_GET_CLASS(chiptod); + + chiptod->tod_state = tod_running; + + /* XScom regions for CHIPTOD registers */ + pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev), + &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod", + PNV_XSCOM_CHIPTOD_SIZE); +} + +static void pnv_chiptod_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_chiptod_realize; + dc->desc = "PowerNV ChipTOD Controller"; + dc->user_creatable = false; +} + +static const TypeInfo pnv_chiptod_type_info = { + .name = TYPE_PNV_CHIPTOD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_class_init, + .class_size = sizeof(PnvChipTODClass), + .abstract = true, +}; + +static void pnv_chiptod_register_types(void) +{ + type_register_static(&pnv_chiptod_type_info); + type_register_static(&pnv_chiptod_power9_type_info); +} + +type_init(pnv_chiptod_register_types); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index 79f10de57f..10d2d9edd4 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -297,6 +297,8 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); if (chip->chip_id == 0) { _FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0))); + } else if (chip->chip_id == 1) { + _FDT((fdt_setprop(fdt, xscom_offset, "secondary", NULL, 0))); } args.fdt = fdt; diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index f6990439d1..60c0a1c42c 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -95,6 +95,10 @@ vof_write(uint32_t ih, unsigned cb, const char *msg) "ih=0x%x [%u] \"%s\"" vof_avail(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 vof_claimed(uint64_t start, uint64_t end, uint64_t size) "0x%"PRIx64"..0x%"PRIx64" size=0x%"PRIx64 +# pnv_chiptod.c +pnv_chiptod_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_chiptod_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 + # pnv_sbe.c pnv_sbe_xscom_ctrl_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 pnv_sbe_xscom_ctrl_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 37c303bf36..501e9b0138 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -26,6 +26,7 @@ #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_pnor.h" #include "hw/ppc/pnv_psi.h" +#include "hw/ppc/pnv_chiptod.h" #include "hw/ppc/pnv_occ.h" #include "hw/ppc/pnv_sbe.h" #include "hw/ppc/pnv_homer.h" @@ -100,6 +101,7 @@ struct Pnv9Chip { PnvXive xive; Pnv9Psi psi; PnvLpcController lpc; + PnvChipTOD chiptod; PnvOCC occ; PnvSBE sbe; PnvHomer homer; diff --git a/include/hw/ppc/pnv_chiptod.h b/include/hw/ppc/pnv_chiptod.h new file mode 100644 index 0000000000..44700bbd81 --- /dev/null +++ b/include/hw/ppc/pnv_chiptod.h @@ -0,0 +1,51 @@ +/* + * QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour + * + * Copyright (c) 2022, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PPC_PNV_CHIPTOD_H +#define PPC_PNV_CHIPTOD_H + +#include "qom/object.h" + +#define TYPE_PNV_CHIPTOD "pnv-chiptod" +OBJECT_DECLARE_TYPE(PnvChipTOD, PnvChipTODClass, PNV_CHIPTOD) +#define TYPE_PNV9_CHIPTOD TYPE_PNV_CHIPTOD "-POWER9" +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV9_CHIPTOD, TYPE_PNV9_CHIPTOD) +#define TYPE_PNV10_CHIPTOD TYPE_PNV_CHIPTOD "-POWER10" +DECLARE_INSTANCE_CHECKER(PnvChipTOD, PNV10_CHIPTOD, TYPE_PNV10_CHIPTOD) + +enum tod_state { + tod_running = 1, + tod_not_set, +}; + +struct PnvChipTOD { + DeviceState xd; + + enum tod_state tod_state; + uint64_t tod_error; + PowerPCCPU *slave_cpu_target; + + MemoryRegion xscom_regs; +}; + +struct PnvChipTODClass { + DeviceClass parent_class; +}; + +#endif /* PPC_PNV_CHIPTOD_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index c6e9ef8dd2..a8bd15401f 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -63,6 +63,9 @@ struct PnvXScomInterfaceClass { #define PNV_XSCOM_PSIHB_BASE 0x2010900 #define PNV_XSCOM_PSIHB_SIZE 0x20 +#define PNV_XSCOM_CHIPTOD_BASE 0x0040000 +#define PNV_XSCOM_CHIPTOD_SIZE 0x31 + #define PNV_XSCOM_OCC_BASE 0x0066000 #define PNV_XSCOM_OCC_SIZE 0x6000 @@ -89,6 +92,9 @@ struct PnvXScomInterfaceClass { ((uint64_t)(((core) & 0x1C) + 0x40) << 22) #define PNV9_XSCOM_EQ_SIZE 0x100000 +#define PNV9_XSCOM_CHIPTOD_BASE PNV_XSCOM_CHIPTOD_BASE +#define PNV9_XSCOM_CHIPTOD_SIZE PNV_XSCOM_CHIPTOD_SIZE + #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE #define PNV9_XSCOM_OCC_SIZE 0x8000 diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index a4c893cfad..7edb3447fa 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1135,6 +1135,11 @@ struct CPUArchState { uint32_t tlb_need_flush; /* Delayed flush needed */ #define TLB_NEED_LOCAL_FLUSH 0x1 #define TLB_NEED_GLOBAL_FLUSH 0x2 + +#if defined(TARGET_PPC64) + /* PowerNV timebase facility */ + int tfmr_op_timeout; +#endif #endif /* Other registers */ @@ -2471,6 +2476,14 @@ enum { HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23), }; +/* TFMR */ +enum { + TFMR_LOAD_TOD_MOD = PPC_BIT(16), + TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18), + TFMR_CLEAR_TB_ERRORS = PPC_BIT(24), + TFMR_TB_VALID = PPC_BIT(41), +}; + /*****************************************************************************/ #define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300)) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index d1493a660c..6d555e16b9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -5392,7 +5392,7 @@ static void register_book3s_ids_sprs(CPUPPCState *env) spr_register_hv(env, SPR_TFMR, "TFMR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_tfmr, &spr_write_tfmr, 0x00000000); spr_register_hv(env, SPR_LPIDR, "LPIDR", SPR_NOACCESS, SPR_NOACCESS, diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 159b352f6e..643c9457c7 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -720,6 +720,8 @@ DEF_HELPER_FLAGS_1(load_dpdes, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_dpdes, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_2(book3s_msgsndp, void, env, tl) DEF_HELPER_2(book3s_msgclrp, void, env, tl) +DEF_HELPER_1(load_tfmr, tl, env) +DEF_HELPER_2(store_tfmr, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index b0a5e7ce76..d979d936b3 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -191,6 +191,31 @@ void helper_store_dpdes(CPUPPCState *env, target_ulong val) env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); } } + +target_ulong helper_load_tfmr(CPUPPCState *env) +{ + if (env->tfmr_op_timeout) { + env->tfmr_op_timeout--; + if (!env->tfmr_op_timeout) { + env->spr[SPR_TFMR] &= ~(TFMR_LOAD_TOD_MOD | + TFMR_CLEAR_TB_ERRORS); + } + } + + return env->spr[SPR_TFMR]; +} + +void helper_store_tfmr(CPUPPCState *env, target_ulong val) +{ + env->spr[SPR_TFMR] = val; + + if (val & TFMR_LOAD_TOD_MOD) { + env->tfmr_op_timeout = 3; + } + if (val & TFMR_CLEAR_TB_ERRORS) { + env->tfmr_op_timeout = 3; + } +} #endif /* defined(TARGET_PPC64) */ void helper_store_pidr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h index b5a5bc6895..7acf7df451 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -194,6 +194,8 @@ void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); +void spr_read_tfmr(DisasContext *ctx, int sprn, int gprn); +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn); void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); #endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 388337f81b..e235334944 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1173,6 +1173,16 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) tcg_temp_free(hmer); } +void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_tfmr(cpu_gpr[gprn], cpu_env); +} + +void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_tfmr(cpu_env, cpu_gpr[gprn]); +} + void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]);