/* * QEMU PowerPC nest pervasive common chiplet model * * Copyright (c) 2023, 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_xscom.h" #include "hw/ppc/pnv_nest_pervasive.h" /* * Status, configuration, and control units in POWER chips is provided * by the pervasive subsystem, which connects registers to the SCOM bus, * which can be programmed by processor cores, other units on the chip, * BMCs, or other POWER chips. * * A POWER10 chip is divided into logical units called chiplets. Chiplets * are broadly divided into "core chiplets" (with the processor cores) and * "nest chiplets" (with everything else). Each chiplet has an attachment * to the pervasive bus (PIB) and with chiplet-specific registers. * All nest chiplets have a common basic set of registers. * * This model will provide the registers functionality for common registers of * nest unit (PB Chiplet, PCI Chiplets, MC Chiplet, PAU Chiplets) * * Currently this model provide the read/write functionality of chiplet control * scom registers. */ #define CPLT_CONF0 0x08 #define CPLT_CONF0_OR 0x18 #define CPLT_CONF0_CLEAR 0x28 #define CPLT_CONF1 0x09 #define CPLT_CONF1_OR 0x19 #define CPLT_CONF1_CLEAR 0x29 #define CPLT_STAT0 0x100 #define CPLT_MASK0 0x101 #define CPLT_PROTECT_MODE 0x3FE #define CPLT_ATOMIC_CLOCK 0x3FF static uint64_t pnv_chiplet_ctrl_read(void *opaque, hwaddr addr, unsigned size) { PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE( opaque); uint32_t reg = addr >> 3; uint64_t val = ~0ull; /* CPLT_CTRL0 to CPLT_CTRL5 */ for (int i = 0; i < PNV_CPLT_CTRL_SIZE; i++) { if (reg == i) { return nest_pervasive->control_regs.cplt_ctrl[i]; } else if ((reg == (i + 0x10)) || (reg == (i + 0x20))) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " "xscom read at 0x%" PRIx32 "\n", __func__, reg); return val; } } switch (reg) { case CPLT_CONF0: val = nest_pervasive->control_regs.cplt_cfg0; break; case CPLT_CONF0_OR: case CPLT_CONF0_CLEAR: qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " "xscom read at 0x%" PRIx32 "\n", __func__, reg); break; case CPLT_CONF1: val = nest_pervasive->control_regs.cplt_cfg1; break; case CPLT_CONF1_OR: case CPLT_CONF1_CLEAR: qemu_log_mask(LOG_GUEST_ERROR, "%s: Write only register, ignoring " "xscom read at 0x%" PRIx32 "\n", __func__, reg); break; case CPLT_STAT0: val = nest_pervasive->control_regs.cplt_stat0; break; case CPLT_MASK0: val = nest_pervasive->control_regs.cplt_mask0; break; case CPLT_PROTECT_MODE: val = nest_pervasive->control_regs.ctrl_protect_mode; break; case CPLT_ATOMIC_CLOCK: val = nest_pervasive->control_regs.ctrl_atomic_lock; break; default: qemu_log_mask(LOG_UNIMP, "%s: Chiplet_control_regs: Invalid xscom " "read at 0x%" PRIx32 "\n", __func__, reg); } return val; } static void pnv_chiplet_ctrl_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE( opaque); uint32_t reg = addr >> 3; /* CPLT_CTRL0 to CPLT_CTRL5 */ for (int i = 0; i < PNV_CPLT_CTRL_SIZE; i++) { if (reg == i) { nest_pervasive->control_regs.cplt_ctrl[i] = val; return; } else if (reg == (i + 0x10)) { nest_pervasive->control_regs.cplt_ctrl[i] |= val; return; } else if (reg == (i + 0x20)) { nest_pervasive->control_regs.cplt_ctrl[i] &= ~val; return; } } switch (reg) { case CPLT_CONF0: nest_pervasive->control_regs.cplt_cfg0 = val; break; case CPLT_CONF0_OR: nest_pervasive->control_regs.cplt_cfg0 |= val; break; case CPLT_CONF0_CLEAR: nest_pervasive->control_regs.cplt_cfg0 &= ~val; break; case CPLT_CONF1: nest_pervasive->control_regs.cplt_cfg1 = val; break; case CPLT_CONF1_OR: nest_pervasive->control_regs.cplt_cfg1 |= val; break; case CPLT_CONF1_CLEAR: nest_pervasive->control_regs.cplt_cfg1 &= ~val; break; case CPLT_STAT0: nest_pervasive->control_regs.cplt_stat0 = val; break; case CPLT_MASK0: nest_pervasive->control_regs.cplt_mask0 = val; break; case CPLT_PROTECT_MODE: nest_pervasive->control_regs.ctrl_protect_mode = val; break; case CPLT_ATOMIC_CLOCK: nest_pervasive->control_regs.ctrl_atomic_lock = val; break; default: qemu_log_mask(LOG_UNIMP, "%s: Chiplet_control_regs: Invalid xscom " "write at 0x%" PRIx32 "\n", __func__, reg); } } static const MemoryRegionOps pnv_nest_pervasive_control_xscom_ops = { .read = pnv_chiplet_ctrl_read, .write = pnv_chiplet_ctrl_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_nest_pervasive_realize(DeviceState *dev, Error **errp) { PnvNestChipletPervasive *nest_pervasive = PNV_NEST_CHIPLET_PERVASIVE(dev); /* Chiplet control scoms */ pnv_xscom_region_init(&nest_pervasive->xscom_ctrl_regs_mr, OBJECT(nest_pervasive), &pnv_nest_pervasive_control_xscom_ops, nest_pervasive, "pervasive-control", PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE); } static void pnv_nest_pervasive_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "PowerNV nest pervasive chiplet"; dc->realize = pnv_nest_pervasive_realize; } static const TypeInfo pnv_nest_pervasive_info = { .name = TYPE_PNV_NEST_CHIPLET_PERVASIVE, .parent = TYPE_DEVICE, .instance_size = sizeof(PnvNestChipletPervasive), .class_init = pnv_nest_pervasive_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } }; static void pnv_nest_pervasive_register_types(void) { type_register_static(&pnv_nest_pervasive_info); } type_init(pnv_nest_pervasive_register_types);