aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-02-23 18:59:11 +0000
committerPeter Maydell <peter.maydell@linaro.org>2024-02-23 18:59:11 +0000
commit91e3bf2e925671eb37e3b71cf7fdeb6b7f30248c (patch)
tree1acdff9476c4fc16f41e2039b86118aae18a7ebf /hw
parent3d54cbf269d63ff1d500b35b2bcf4565ff8ad485 (diff)
parent4acc505d2236190efea94746e7f22e2c07bce5d6 (diff)
downloadqemu-91e3bf2e925671eb37e3b71cf7fdeb6b7f30248c.zip
qemu-91e3bf2e925671eb37e3b71cf7fdeb6b7f30248c.tar.gz
qemu-91e3bf2e925671eb37e3b71cf7fdeb6b7f30248c.tar.bz2
Merge tag 'pull-ppc-for-9.0-20240224' of https://gitlab.com/npiggin/qemu into staging
* Avocado tests for ppc64 to boot FreeBSD, run guests with emulated or nested hypervisor facilities, among other things. * Update ppc64 CPU defaults to Power10. * Add a new powernv10-rainier machine to better capture differences between the different Power10 systems. * Implement more device models for powernv. * 4xx TLB flushing performance and correctness improvements. * Correct gdb implementation to access some important SPRs. * Misc cleanups and bug fixes. # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCgAdFiEETkN92lZhb0MpsKeVZ7MCdqhiHK4FAmXYuX0ACgkQZ7MCdqhi # HK6t1Q/9Hxw+MseFUa/6sbWX6mhv/8emrFFOwI9qxapxDoMyic+SjIhR5PPCYh6t # TLE1vJiV54XYB3286hz3eQfDxfHNjkgsF7PYp9SEd6D1rMT9ESxeu5NkifenEfP0 # UoTFXJyfg/OF1h+JQRrVv1m+D4mqGGNCQB4QiU3DYTmRhrhp7H3mKfUX/KvkEwiX # EqZibmrqb9SVSjT66LBQzY328mEH4nipF33QtYKfYjb6kMe8ACSznL2VYP0NmacU # T+3eHJeLtOLeRlHwYfADx2ekRHlsJuE9/fMMHJHb2qxJkHSQ7yGBqSLESAe6kNP8 # TnKJ9x4433K7IjFqaoiDONrMVJbVZDh/DUh1WWdY14iiUOYEy7uLkLtmThmNSyUB # 622Rd5Ch09JWzA/tg1aC9mR2f9boe9/Z1VeHeN8j+sVj1e6MEh8un8SER3X+9TDz # myGLsmPXQnu1yjebycuE+9RAPbR9npOAkQpE5ZfDwjUM7y4s4jzZUKUoIhtCXeEF # eIykVnaGbPlEBGpuf+E+w2ZxhZUIfxRUhuunK8Ib4TE8khJn/Ir4BxoLweSnqtKM # O4xiFvHm72RUVK232Kox5HWbFJ8XSLBUb3ABNGbXXynzAMD+THB4ImFBbysOmIkR # xcF1tWQ+xoMMcCxbx73b0PhO5AR/PgYc2ctug9rAc9fh4ypJLEs= # =LZzb # -----END PGP SIGNATURE----- # gpg: Signature made Fri 23 Feb 2024 15:27:57 GMT # gpg: using RSA key 4E437DDA56616F4329B0A79567B30276A8621CAE # gpg: Good signature from "Nicholas Piggin <npiggin@gmail.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 4E43 7DDA 5661 6F43 29B0 A795 67B3 0276 A862 1CAE * tag 'pull-ppc-for-9.0-20240224' of https://gitlab.com/npiggin/qemu: (47 commits) target/ppc: optimise ppcemb_tlb_t flushing target/ppc: 440 optimise tlbwe TLB flushing target/ppc: 4xx optimise tlbwe_lo TLB flushing target/ppc: 4xx don't flush TLB for a newly written software TLB entry target/ppc: Factor out 4xx ppcemb_tlb_t flushing target/ppc: Fix 440 tlbwe TLB invalidation gaps target/ppc: Add SMT support to time facilities target/ppc: Implement core timebase state machine and TFMR ppc/pnv: Implement the ChipTOD to Core transfer ppc/pnv: Wire ChipTOD model to powernv9 and powernv10 machines ppc/pnv: Add POWER9/10 chiptod model target/ppc: Fix move-to timebase SPR access permissions target/ppc: Improve timebase register defines naming target/ppc: Rename TBL to TB on 64-bit target/ppc: Update gdbstub to read SPR's CFAR, DEC, HDEC, TB-L/U hw/ppc: N1 chiplet wiring hw/ppc: Add N1 chiplet model hw/ppc: Add pnv nest pervasive common chiplet model ppc/pnv: Test pnv i2c master and connected devices ppc/pnv: Add a pca9554 I2C device to powernv10-rainier ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r--hw/misc/Kconfig4
-rw-r--r--hw/misc/meson.build1
-rw-r--r--hw/misc/pca9552.c58
-rw-r--r--hw/misc/pca9554.c328
-rw-r--r--hw/ppc/Kconfig2
-rw-r--r--hw/ppc/meson.build5
-rw-r--r--hw/ppc/pnv.c131
-rw-r--r--hw/ppc/pnv_chiptod.c586
-rw-r--r--hw/ppc/pnv_i2c.c146
-rw-r--r--hw/ppc/pnv_n1_chiplet.c173
-rw-r--r--hw/ppc/pnv_nest_pervasive.c208
-rw-r--r--hw/ppc/spapr.c12
-rw-r--r--hw/ppc/spapr_hcall.c12
-rw-r--r--hw/ppc/spapr_irq.c6
-rw-r--r--hw/ppc/spapr_vhyp_mmu.c (renamed from hw/ppc/spapr_softmmu.c)13
-rw-r--r--hw/ppc/trace-events4
16 files changed, 1531 insertions, 158 deletions
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 4fc6b29..83ad849 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -34,6 +34,10 @@ config PCA9552
bool
depends on I2C
+config PCA9554
+ bool
+ depends on I2C
+
config I2C_ECHO
bool
default y if TEST_DEVICES
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index e4ef1da..7466868 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -4,6 +4,7 @@ system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c'))
system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c'))
system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c'))
system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
+system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c
index 72b6534..2ae13af 100644
--- a/hw/misc/pca9552.c
+++ b/hw/misc/pca9552.c
@@ -36,11 +36,16 @@ typedef struct PCA955xClass PCA955xClass;
DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X,
TYPE_PCA955X)
-
+/*
+ * Note: The LED_ON and LED_OFF configuration values for the PCA955X
+ * chips are the reverse of the PCA953X family of chips.
+ */
#define PCA9552_LED_ON 0x0
#define PCA9552_LED_OFF 0x1
#define PCA9552_LED_PWM0 0x2
#define PCA9552_LED_PWM1 0x3
+#define PCA9552_PIN_LOW 0x0
+#define PCA9552_PIN_HIZ 0x1
static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
@@ -107,17 +112,27 @@ static void pca955x_update_pin_input(PCA955xState *s)
for (i = 0; i < k->pin_count; i++) {
uint8_t input_reg = PCA9552_INPUT0 + (i / 8);
- uint8_t input_shift = (i % 8);
+ uint8_t bit_mask = 1 << (i % 8);
uint8_t config = pca955x_pin_get_config(s, i);
+ uint8_t old_value = s->regs[input_reg] & bit_mask;
+ uint8_t new_value;
switch (config) {
case PCA9552_LED_ON:
- qemu_set_irq(s->gpio[i], 1);
- s->regs[input_reg] |= 1 << input_shift;
+ /* Pin is set to 0V to turn on LED */
+ s->regs[input_reg] &= ~bit_mask;
break;
case PCA9552_LED_OFF:
- qemu_set_irq(s->gpio[i], 0);
- s->regs[input_reg] &= ~(1 << input_shift);
+ /*
+ * Pin is set to Hi-Z to turn off LED and
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+ if (s->ext_state[i] == PCA9552_PIN_LOW) {
+ s->regs[input_reg] &= ~bit_mask;
+ } else {
+ s->regs[input_reg] |= bit_mask;
+ }
break;
case PCA9552_LED_PWM0:
case PCA9552_LED_PWM1:
@@ -125,6 +140,12 @@ static void pca955x_update_pin_input(PCA955xState *s)
default:
break;
}
+
+ /* update irq state only if pin state changed */
+ new_value = s->regs[input_reg] & bit_mask;
+ if (new_value != old_value) {
+ qemu_set_irq(s->gpio_out[i], !!new_value);
+ }
}
}
@@ -332,6 +353,7 @@ static const VMStateDescription pca9552_vmstate = {
VMSTATE_UINT8(len, PCA955xState),
VMSTATE_UINT8(pointer, PCA955xState),
VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS),
+ VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX),
VMSTATE_I2C_SLAVE(i2c, PCA955xState),
VMSTATE_END_OF_LIST()
}
@@ -350,6 +372,7 @@ static void pca9552_reset(DeviceState *dev)
s->regs[PCA9552_LS2] = 0x55;
s->regs[PCA9552_LS3] = 0x55;
+ memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
pca955x_update_pin_input(s);
s->pointer = 0xFF;
@@ -372,6 +395,26 @@ static void pca955x_initfn(Object *obj)
}
}
+static void pca955x_set_ext_state(PCA955xState *s, int pin, int level)
+{
+ if (s->ext_state[pin] != level) {
+ uint16_t pins_status = pca955x_pins_get_status(s);
+ s->ext_state[pin] = level;
+ pca955x_update_pin_input(s);
+ pca955x_display_pins_status(s, pins_status);
+ }
+}
+
+static void pca955x_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+ PCA955xState *s = PCA955X(opaque);
+ PCA955xClass *k = PCA955X_GET_CLASS(s);
+
+ assert((pin >= 0) && (pin < k->pin_count));
+ pca955x_set_ext_state(s, pin, level);
+}
+
static void pca955x_realize(DeviceState *dev, Error **errp)
{
PCA955xClass *k = PCA955X_GET_CLASS(dev);
@@ -381,7 +424,8 @@ static void pca955x_realize(DeviceState *dev, Error **errp)
s->description = g_strdup("pca-unspecified");
}
- qdev_init_gpio_out(dev, s->gpio, k->pin_count);
+ qdev_init_gpio_out(dev, s->gpio_out, k->pin_count);
+ qdev_init_gpio_in(dev, pca955x_gpio_in_handler, k->pin_count);
}
static Property pca955x_properties[] = {
diff --git a/hw/misc/pca9554.c b/hw/misc/pca9554.c
new file mode 100644
index 0000000..778b32e
--- /dev/null
+++ b/hw/misc/pca9554.c
@@ -0,0 +1,328 @@
+/*
+ * PCA9554 I/O port
+ *
+ * Copyright (c) 2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pca9554.h"
+#include "hw/misc/pca9554_regs.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "trace.h"
+#include "qom/object.h"
+
+struct PCA9554Class {
+ /*< private >*/
+ I2CSlaveClass parent_class;
+ /*< public >*/
+};
+typedef struct PCA9554Class PCA9554Class;
+
+DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
+ TYPE_PCA9554)
+
+#define PCA9554_PIN_LOW 0x0
+#define PCA9554_PIN_HIZ 0x1
+
+static const char *pin_state[] = {"low", "high"};
+
+static void pca9554_update_pin_input(PCA9554State *s)
+{
+ int i;
+ uint8_t config = s->regs[PCA9554_CONFIG];
+ uint8_t output = s->regs[PCA9554_OUTPUT];
+ uint8_t internal_state = config | output;
+
+ for (i = 0; i < PCA9554_PIN_COUNT; i++) {
+ uint8_t bit_mask = 1 << i;
+ uint8_t internal_pin_state = (internal_state >> i) & 0x1;
+ uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask;
+ uint8_t new_value;
+
+ switch (internal_pin_state) {
+ case PCA9554_PIN_LOW:
+ s->regs[PCA9554_INPUT] &= ~bit_mask;
+ break;
+ case PCA9554_PIN_HIZ:
+ /*
+ * pullup sets it to a logical 1 unless
+ * external device drives it low.
+ */
+ if (s->ext_state[i] == PCA9554_PIN_LOW) {
+ s->regs[PCA9554_INPUT] &= ~bit_mask;
+ } else {
+ s->regs[PCA9554_INPUT] |= bit_mask;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* update irq state only if pin state changed */
+ new_value = s->regs[PCA9554_INPUT] & bit_mask;
+ if (new_value != old_value) {
+ if (new_value) {
+ /* changed from 0 to 1 */
+ qemu_set_irq(s->gpio_out[i], 1);
+ } else {
+ /* changed from 1 to 0 */
+ qemu_set_irq(s->gpio_out[i], 0);
+ }
+ }
+ }
+}
+
+static uint8_t pca9554_read(PCA9554State *s, uint8_t reg)
+{
+ switch (reg) {
+ case PCA9554_INPUT:
+ return s->regs[PCA9554_INPUT] ^ s->regs[PCA9554_POLARITY];
+ case PCA9554_OUTPUT:
+ case PCA9554_POLARITY:
+ case PCA9554_CONFIG:
+ return s->regs[reg];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n",
+ __func__, reg);
+ return 0xFF;
+ }
+}
+
+static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data)
+{
+ switch (reg) {
+ case PCA9554_OUTPUT:
+ case PCA9554_CONFIG:
+ s->regs[reg] = data;
+ pca9554_update_pin_input(s);
+ break;
+ case PCA9554_POLARITY:
+ s->regs[reg] = data;
+ break;
+ case PCA9554_INPUT:
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n",
+ __func__, reg);
+ }
+}
+
+static uint8_t pca9554_recv(I2CSlave *i2c)
+{
+ PCA9554State *s = PCA9554(i2c);
+ uint8_t ret;
+
+ ret = pca9554_read(s, s->pointer & 0x3);
+
+ return ret;
+}
+
+static int pca9554_send(I2CSlave *i2c, uint8_t data)
+{
+ PCA9554State *s = PCA9554(i2c);
+
+ /* First byte sent by is the register address */
+ if (s->len == 0) {
+ s->pointer = data;
+ s->len++;
+ } else {
+ pca9554_write(s, s->pointer & 0x3, data);
+ }
+
+ return 0;
+}
+
+static int pca9554_event(I2CSlave *i2c, enum i2c_event event)
+{
+ PCA9554State *s = PCA9554(i2c);
+
+ s->len = 0;
+ return 0;
+}
+
+static void pca9554_get_pin(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ PCA9554State *s = PCA9554(obj);
+ int pin, rc;
+ uint8_t state;
+
+ rc = sscanf(name, "pin%2d", &pin);
+ if (rc != 1) {
+ error_setg(errp, "%s: error reading %s", __func__, name);
+ return;
+ }
+ if (pin < 0 || pin > PCA9554_PIN_COUNT) {
+ error_setg(errp, "%s invalid pin %s", __func__, name);
+ return;
+ }
+
+ state = pca9554_read(s, PCA9554_CONFIG);
+ state |= pca9554_read(s, PCA9554_OUTPUT);
+ state = (state >> pin) & 0x1;
+ visit_type_str(v, name, (char **)&pin_state[state], errp);
+}
+
+static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ PCA9554State *s = PCA9554(obj);
+ int pin, rc, val;
+ uint8_t state, mask;
+ char *state_str;
+
+ if (!visit_type_str(v, name, &state_str, errp)) {
+ return;
+ }
+ rc = sscanf(name, "pin%2d", &pin);
+ if (rc != 1) {
+ error_setg(errp, "%s: error reading %s", __func__, name);
+ return;
+ }
+ if (pin < 0 || pin > PCA9554_PIN_COUNT) {
+ error_setg(errp, "%s invalid pin %s", __func__, name);
+ return;
+ }
+
+ for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
+ if (!strcmp(state_str, pin_state[state])) {
+ break;
+ }
+ }
+ if (state >= ARRAY_SIZE(pin_state)) {
+ error_setg(errp, "%s invalid pin state %s", __func__, state_str);
+ return;
+ }
+
+ /* First, modify the output register bit */
+ val = pca9554_read(s, PCA9554_OUTPUT);
+ mask = 0x1 << pin;
+ if (state == PCA9554_PIN_LOW) {
+ val &= ~(mask);
+ } else {
+ val |= mask;
+ }
+ pca9554_write(s, PCA9554_OUTPUT, val);
+
+ /* Then, clear the config register bit for output mode */
+ val = pca9554_read(s, PCA9554_CONFIG);
+ val &= ~mask;
+ pca9554_write(s, PCA9554_CONFIG, val);
+}
+
+static const VMStateDescription pca9554_vmstate = {
+ .name = "PCA9554",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(len, PCA9554State),
+ VMSTATE_UINT8(pointer, PCA9554State),
+ VMSTATE_UINT8_ARRAY(regs, PCA9554State, PCA9554_NR_REGS),
+ VMSTATE_UINT8_ARRAY(ext_state, PCA9554State, PCA9554_PIN_COUNT),
+ VMSTATE_I2C_SLAVE(i2c, PCA9554State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pca9554_reset(DeviceState *dev)
+{
+ PCA9554State *s = PCA9554(dev);
+
+ s->regs[PCA9554_INPUT] = 0xFF;
+ s->regs[PCA9554_OUTPUT] = 0xFF;
+ s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */
+ s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */
+
+ memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT);
+ pca9554_update_pin_input(s);
+
+ s->pointer = 0x0;
+ s->len = 0;
+}
+
+static void pca9554_initfn(Object *obj)
+{
+ int pin;
+
+ for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) {
+ char *name;
+
+ name = g_strdup_printf("pin%d", pin);
+ object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin,
+ NULL, NULL);
+ g_free(name);
+ }
+}
+
+static void pca9554_set_ext_state(PCA9554State *s, int pin, int level)
+{
+ if (s->ext_state[pin] != level) {
+ s->ext_state[pin] = level;
+ pca9554_update_pin_input(s);
+ }
+}
+
+static void pca9554_gpio_in_handler(void *opaque, int pin, int level)
+{
+
+ PCA9554State *s = PCA9554(opaque);
+
+ assert((pin >= 0) && (pin < PCA9554_PIN_COUNT));
+ pca9554_set_ext_state(s, pin, level);
+}
+
+static void pca9554_realize(DeviceState *dev, Error **errp)
+{
+ PCA9554State *s = PCA9554(dev);
+
+ if (!s->description) {
+ s->description = g_strdup("pca9554");
+ }
+
+ qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT);
+ qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT);
+}
+
+static Property pca9554_properties[] = {
+ DEFINE_PROP_STRING("description", PCA9554State, description),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pca9554_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->event = pca9554_event;
+ k->recv = pca9554_recv;
+ k->send = pca9554_send;
+ dc->realize = pca9554_realize;
+ dc->reset = pca9554_reset;
+ dc->vmsd = &pca9554_vmstate;
+ device_class_set_props(dc, pca9554_properties);
+}
+
+static const TypeInfo pca9554_info = {
+ .name = TYPE_PCA9554,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_init = pca9554_initfn,
+ .instance_size = sizeof(PCA9554State),
+ .class_init = pca9554_class_init,
+ .class_size = sizeof(PCA9554Class),
+ .abstract = false,
+};
+
+static void pca9554_register_types(void)
+{
+ type_register_static(&pca9554_info);
+}
+
+type_init(pca9554_register_types)
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 99d571f..a890699 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -32,6 +32,8 @@ config POWERNV
select XIVE
select FDT_PPC
select PCI_POWERNV
+ select PCA9552
+ select PCA9554
config PPC405
bool
diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build
index da14fcc..d096636 100644
--- a/hw/ppc/meson.build
+++ b/hw/ppc/meson.build
@@ -31,7 +31,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files(
'pef.c',
))
ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_TCG'], if_true: files(
- 'spapr_softmmu.c',
+ 'spapr_vhyp_mmu.c',
))
ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c'))
if host_os == 'linux'
@@ -48,11 +48,14 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
'pnv_i2c.c',
'pnv_lpc.c',
'pnv_psi.c',
+ 'pnv_chiptod.c',
'pnv_occ.c',
'pnv_sbe.c',
'pnv_bmc.c',
'pnv_homer.c',
'pnv_pnor.c',
+ 'pnv_nest_pervasive.c',
+ 'pnv_n1_chiplet.c',
))
# PowerPC 4xx boards
ppc_ss.add(when: 'CONFIG_PPC405', if_true: files(
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 0297871..0b47b92 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
const char *bios_name = machine->firmware ?: FW_FILE_NAME;
PnvMachineState *pnv = PNV_MACHINE(machine);
MachineClass *mc = MACHINE_GET_CLASS(machine);
+ PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
char *fw_filename;
long fw_size;
uint64_t chip_ram_start = 0;
@@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
*/
pnv->powerdown_notifier.notify = pnv_powerdown_notify;
qemu_register_powerdown_notifier(&pnv->powerdown_notifier);
+
+ /*
+ * Create/Connect any machine-specific I2C devices
+ */
+ if (pmc->i2c_init) {
+ pmc->i2c_init(pnv);
+ }
}
/*
@@ -1419,6 +1427,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);
@@ -1565,6 +1575,19 @@ 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 */
+ object_property_set_bool(OBJECT(&chip9->chiptod), "primary",
+ chip->chip_id == 0, &error_abort);
+ object_property_set_bool(OBJECT(&chip9->chiptod), "secondary",
+ chip->chip_id == 1, &error_abort);
+ object_property_set_link(OBJECT(&chip9->chiptod), "chip", OBJECT(chip),
+ &error_abort);
+ 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;
@@ -1677,9 +1700,13 @@ static void pnv_chip_power10_instance_init(Object *obj)
"xive-fabric");
object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI);
object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC);
+ object_initialize_child(obj, "chiptod", &chip10->chiptod,
+ TYPE_PNV10_CHIPTOD);
object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC);
object_initialize_child(obj, "sbe", &chip10->sbe, TYPE_PNV10_SBE);
object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER);
+ object_initialize_child(obj, "n1-chiplet", &chip10->n1_chiplet,
+ TYPE_PNV_N1_CHIPLET);
chip->num_pecs = pcc->num_pecs;
@@ -1810,6 +1837,19 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0",
(uint64_t) PNV10_LPCM_BASE(chip));
+ /* ChipTOD */
+ object_property_set_bool(OBJECT(&chip10->chiptod), "primary",
+ chip->chip_id == 0, &error_abort);
+ object_property_set_bool(OBJECT(&chip10->chiptod), "secondary",
+ chip->chip_id == 1, &error_abort);
+ object_property_set_link(OBJECT(&chip10->chiptod), "chip", OBJECT(chip),
+ &error_abort);
+ if (!qdev_realize(DEVICE(&chip10->chiptod), NULL, errp)) {
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE,
+ &chip10->chiptod.xscom_regs);
+
/* Create the simplified OCC model */
if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) {
return;
@@ -1849,6 +1889,19 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip),
&chip10->homer.regs);
+ /* N1 chiplet */
+ if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) {
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE,
+ &chip10->n1_chiplet.nest_pervasive.xscom_ctrl_regs_mr);
+
+ pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_EQ_BASE,
+ &chip10->n1_chiplet.xscom_pb_eq_mr);
+
+ pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_ES_BASE,
+ &chip10->n1_chiplet.xscom_pb_es_mr);
+
/* PHBs */
pnv_chip_power10_phb_realize(chip, &local_err);
if (local_err) {
@@ -1879,6 +1932,39 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
qdev_get_gpio_in(DEVICE(&chip10->psi),
PSIHB9_IRQ_SBE_I2C));
}
+
+}
+
+static void pnv_rainier_i2c_init(PnvMachineState *pnv)
+{
+ int i;
+ for (i = 0; i < pnv->num_chips; i++) {
+ Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+
+ /*
+ * Add a PCA9552 I2C device for PCIe hotplug control
+ * to engine 2, bus 1, address 0x63
+ */
+ I2CSlave *dev = i2c_slave_create_simple(chip10->i2c[2].busses[1],
+ "pca9552", 0x63);
+
+ /*
+ * Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
+ * (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
+ * after hypervisor code sets a SLOTx_EN pin high.
+ */
+ qdev_connect_gpio_out(DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(dev), 5));
+ qdev_connect_gpio_out(DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(dev), 6));
+ qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 7));
+ qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 8));
+ qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 9));
+
+ /*
+ * Add a PCA9554 I2C device for cable card presence detection
+ * to engine 2, bus 1, address 0x25
+ */
+ i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
+ }
}
static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
@@ -2035,6 +2121,21 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data)
dc->desc = "PowerNV Chip";
}
+PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id)
+{
+ int i;
+
+ for (i = 0; i < chip->nr_cores; i++) {
+ PnvCore *pc = chip->cores[i];
+ CPUCore *cc = CPU_CORE(pc);
+
+ if (cc->core_id == core_id) {
+ return pc;
+ }
+ }
+ return NULL;
+}
+
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
{
int i, j;
@@ -2242,8 +2343,6 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
xfc->match_nvt = pnv_match_nvt;
- mc->alias = "powernv";
-
pmc->compat = compat;
pmc->compat_size = sizeof(compat);
pmc->dt_power_mgt = pnv_dt_power_mgt;
@@ -2251,7 +2350,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
}
-static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
@@ -2263,10 +2362,11 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
{ TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
};
- mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
+ mc->alias = "powernv";
+
pmc->compat = compat;
pmc->compat_size = sizeof(compat);
pmc->dt_power_mgt = pnv_dt_power_mgt;
@@ -2276,6 +2376,24 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
}
+static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ pnv_machine_p10_common_class_init(oc, data);
+ mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
+}
+
+static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
+
+ pnv_machine_p10_common_class_init(oc, data);
+ mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
+ pmc->i2c_init = pnv_rainier_i2c_init;
+}
+
static bool pnv_machine_get_hb(Object *obj, Error **errp)
{
PnvMachineState *pnv = PNV_MACHINE(obj);
@@ -2382,6 +2500,11 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data)
static const TypeInfo types[] = {
{
+ .name = MACHINE_TYPE_NAME("powernv10-rainier"),
+ .parent = MACHINE_TYPE_NAME("powernv10"),
+ .class_init = pnv_machine_p10_rainier_class_init,
+ },
+ {
.name = MACHINE_TYPE_NAME("powernv10"),
.parent = TYPE_PNV_MACHINE,
.class_init = pnv_machine_power10_class_init,
diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c
new file mode 100644
index 0000000..3831a72
--- /dev/null
+++ b/hw/ppc/pnv_chiptod.c
@@ -0,0 +1,586 @@
+/*
+ * QEMU PowerPC PowerNV Emulation of some ChipTOD behaviour
+ *
+ * Copyright (c) 2022-2023, IBM Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * ChipTOD (aka TOD) is a facility implemented in the nest / pervasive. The
+ * purpose is to keep time-of-day across chips and cores.
+ *
+ * There is a master chip TOD, which sends signals to slave chip TODs to
+ * keep them synchronized. There are two sets of configuration registers
+ * called primary and secondary, which can be used fail over.
+ *
+ * The chip TOD also distributes synchronisation signals to the timebase
+ * facility in each of the cores on the chip. In particular there is a
+ * feature that can move the TOD value in the ChipTOD to and from the TB.
+ *
+ * Initialisation typically brings all ChipTOD into sync (see tod_state),
+ * and then brings each core TB into sync with the ChipTODs (see timebase
+ * state and TFMR). This model is a very basic simulation of the init sequence
+ * performed by skiboot.
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/reset.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/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_chip.h"
+#include "hw/ppc/pnv_core.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_M_PATH_CTRL_REG 0x00000000 /* Master Path ctrl reg */
+#define TOD_PRI_PORT_0_CTRL_REG 0x00000001 /* Primary port0 ctrl reg */
+#define TOD_PRI_PORT_1_CTRL_REG 0x00000002 /* Primary port1 ctrl reg */
+#define TOD_SEC_PORT_0_CTRL_REG 0x00000003 /* Secondary p0 ctrl reg */
+#define TOD_SEC_PORT_1_CTRL_REG 0x00000004 /* Secondary p1 ctrl reg */
+#define TOD_S_PATH_CTRL_REG 0x00000005 /* Slave Path ctrl reg */
+#define TOD_I_PATH_CTRL_REG 0x00000006 /* Internal Path ctrl reg */
+
+/* -- TOD primary/secondary master/slave control register -- */
+#define TOD_PSS_MSS_CTRL_REG 0x00000007
+
+/* -- TOD primary/secondary master/slave status register -- */
+#define TOD_PSS_MSS_STATUS_REG 0x00000008
+
+/* TOD chip XSCOM addresses */
+#define TOD_CHIP_CTRL_REG 0x00000010 /* Chip control reg */
+
+#define TOD_TX_TTYPE_0_REG 0x00000011
+#define TOD_TX_TTYPE_1_REG 0x00000012 /* PSS switch reg */
+#define TOD_TX_TTYPE_2_REG 0x00000013 /* Enable step checkers */
+#define TOD_TX_TTYPE_3_REG 0x00000014 /* Request TOD reg */
+#define TOD_TX_TTYPE_4_REG 0x00000015 /* Send TOD reg */
+#define TOD_TX_TTYPE_5_REG 0x00000016 /* Invalidate TOD reg */
+
+#define TOD_MOVE_TOD_TO_TB_REG 0x00000017
+#define TOD_LOAD_TOD_MOD_REG 0x00000018
+#define TOD_LOAD_TOD_REG 0x00000021
+#define TOD_START_TOD_REG 0x00000022
+#define TOD_FSM_REG 0x00000024
+
+#define TOD_TX_TTYPE_CTRL_REG 0x00000027 /* TX TTYPE Control reg */
+#define TOD_TX_TTYPE_PIB_SLAVE_ADDR PPC_BITMASK(26, 31)
+
+/* -- TOD Error interrupt register -- */
+#define TOD_ERROR_REG 0x00000030
+
+/* PC unit PIB address which recieves the timebase transfer from TOD */
+#define PC_TOD 0x4A3
+
+/*
+ * The TOD FSM:
+ * - The reset state is 0 error.
+ * - A hardware error detected will transition to state 0 from any state.
+ * - LOAD_TOD_MOD and TTYPE5 will transition to state 7 from any state.
+ *
+ * | state | action | new |
+ * |------------+------------------------------+-----|
+ * | 0 error | LOAD_TOD_MOD | 7 |
+ * | 0 error | Recv TTYPE5 (invalidate TOD) | 7 |
+ * | 7 not_set | LOAD_TOD (bit-63 = 0) | 2 |
+ * | 7 not_set | LOAD_TOD (bit-63 = 1) | 1 |
+ * | 7 not_set | Recv TTYPE4 (send TOD) | 2 |
+ * | 2 running | | |
+ * | 1 stopped | START_TOD | 2 |
+ *
+ * Note the hardware has additional states but they relate to the sending
+ * and receiving and waiting on synchronisation signals between chips and
+ * are not described or modeled here.
+ */
+
+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_PSS_MSS_STATUS_REG:
+ /*
+ * ChipTOD does not support configurations other than primary
+ * master, does not support errors, etc.
+ */
+ val |= PPC_BITMASK(6, 10); /* STEP checker validity */
+ val |= PPC_BIT(12); /* Primary config master path select */
+ if (chiptod->tod_state == tod_running) {
+ val |= PPC_BIT(20); /* Is running */
+ }
+ val |= PPC_BIT(21); /* Is using primary config */
+ val |= PPC_BIT(26); /* Is using master path select */
+
+ if (chiptod->primary) {
+ val |= PPC_BIT(23); /* Is active master */
+ } else if (chiptod->secondary) {
+ val |= PPC_BIT(24); /* Is backup master */
+ } else {
+ val |= PPC_BIT(25); /* Is slave (should backup master set this?) */
+ }
+ break;
+ case TOD_PSS_MSS_CTRL_REG:
+ val = chiptod->pss_mss_ctrl_reg;
+ break;
+ case TOD_TX_TTYPE_CTRL_REG:
+ val = 0;
+ break;
+ case TOD_ERROR_REG:
+ val = chiptod->tod_error;
+ break;
+ case TOD_FSM_REG:
+ if (chiptod->tod_state == tod_running) {
+ val |= PPC_BIT(4);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "pnv_chiptod: unimplemented register: Ox%"
+ HWADDR_PRIx "\n", addr >> 3);
+ }
+
+ trace_pnv_chiptod_xscom_read(addr >> 3, val);
+
+ return val;
+}
+
+static void chiptod_receive_ttype(PnvChipTOD *chiptod, uint32_t trigger)
+{
+ switch (trigger) {
+ case TOD_TX_TTYPE_4_REG:
+ if (chiptod->tod_state != tod_not_set) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: received TTYPE4 in "
+ " state %d, should be in 7 (TOD_NOT_SET)\n",
+ chiptod->tod_state);
+ } else {
+ chiptod->tod_state = tod_running;
+ }
+ break;
+ case TOD_TX_TTYPE_5_REG:
+ /* Works from any state */
+ chiptod->tod_state = tod_not_set;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "pnv_chiptod: received unimplemented "
+ " TTYPE %u\n", trigger);
+ break;
+ }
+}
+
+static void chiptod_power9_broadcast_ttype(PnvChipTOD *sender,
+ uint32_t trigger)
+{
+ PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
+ int i;
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
+ PnvChipTOD *chiptod = &chip9->chiptod;
+
+ if (chiptod != sender) {
+ chiptod_receive_ttype(chiptod, trigger);
+ }
+ }
+}
+
+static void chiptod_power10_broadcast_ttype(PnvChipTOD *sender,
+ uint32_t trigger)
+{
+ PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
+ int i;
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
+ PnvChipTOD *chiptod = &chip10->chiptod;
+
+ if (chiptod != sender) {
+ chiptod_receive_ttype(chiptod, trigger);
+ }
+ }
+}
+
+static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip,
+ uint32_t xscom_base)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ int i;
+
+ for (i = 0; i < chip->nr_cores; i++) {
+ PnvCore *pc = chip->cores[i];
+ CPUCore *cc = CPU_CORE(pc);
+ int core_hwid = cc->core_id;
+
+ if (pcc->xscom_core_base(chip, core_hwid) == xscom_base) {
+ return pc;
+ }
+ }
+ return NULL;
+}
+
+static PnvCore *chiptod_power9_tx_ttype_target(PnvChipTOD *chiptod,
+ uint64_t val)
+{
+ /*
+ * skiboot uses Core ID for P9, though SCOM should work too.
+ */
+ if (val & PPC_BIT(35)) { /* SCOM addressing */
+ uint32_t addr = val >> 32;
+ uint32_t reg = addr & 0xfff;
+
+ if (reg != PC_TOD) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
+ "unimplemented slave register 0x%" PRIx32 "\n", reg);
+ return NULL;
+ }
+
+ return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
+
+ } else { /* Core ID addressing */
+ uint32_t core_id = GETFIELD(TOD_TX_TTYPE_PIB_SLAVE_ADDR, val) & 0x1f;
+ return pnv_chip_find_core(chiptod->chip, core_id);
+ }
+}
+
+static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod,
+ uint64_t val)
+{
+ /*
+ * skiboot uses SCOM for P10 because Core ID was unable to be made to
+ * work correctly. For this reason only SCOM addressing is implemented.
+ */
+ if (val & PPC_BIT(35)) { /* SCOM addressing */
+ uint32_t addr = val >> 32;
+ uint32_t reg = addr & 0xfff;
+
+ if (reg != PC_TOD) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: SCOM addressing: "
+ "unimplemented slave register 0x%" PRIx32 "\n", reg);
+ return NULL;
+ }
+
+ /*
+ * This may not deal with P10 big-core addressing at the moment.
+ * The big-core code in skiboot syncs small cores, but it targets
+ * the even PIR (first small-core) when syncing second small-core.
+ */
+ return pnv_chip_get_core_by_xscom_base(chiptod->chip, addr & ~0xfff);
+
+ } else { /* Core ID addressing */
+ qemu_log_mask(LOG_UNIMP, "pnv_chiptod: TX TTYPE Core ID "
+ "addressing is not implemented for POWER10\n");
+ return NULL;
+ }
+}
+
+static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvChipTOD *chiptod = PNV_CHIPTOD(opaque);
+ PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod);
+ uint32_t offset = addr >> 3;
+
+ trace_pnv_chiptod_xscom_write(addr >> 3, val);
+
+ switch (offset) {
+ case TOD_PSS_MSS_CTRL_REG:
+ /* Is this correct? */
+ if (chiptod->primary) {
+ val |= PPC_BIT(1); /* TOD is master */
+ } else {
+ val &= ~PPC_BIT(1);
+ }
+ val |= PPC_BIT(2); /* Drawer is master (don't simulate multi-drawer) */
+ chiptod->pss_mss_ctrl_reg = val & PPC_BITMASK(0, 31);
+ break;
+
+ case TOD_TX_TTYPE_CTRL_REG:
+ /*
+ * This register sets the target of the TOD value transfer initiated
+ * by TOD_MOVE_TOD_TO_TB. The TOD is able to send the address to
+ * any target register, though in practice only the PC TOD register
+ * should be used. ChipTOD has a "SCOM addressing" mode which fully
+ * specifies the SCOM address, and a core-ID mode which uses the
+ * core ID to target the PC TOD for a given core.
+ */
+ chiptod->slave_pc_target = pctc->tx_ttype_target(chiptod, val);
+ if (!chiptod->slave_pc_target) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_TX_TTYPE_CTRL_REG val 0x%" PRIx64
+ " invalid slave address\n", val);
+ }
+ break;
+ case TOD_ERROR_REG:
+ chiptod->tod_error &= ~val;
+ break;
+ case TOD_LOAD_TOD_MOD_REG:
+ if (!(val & PPC_BIT(0))) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_LOAD_TOD_MOD_REG with bad val 0x%" PRIx64"\n",
+ val);
+ } else {
+ chiptod->tod_state = tod_not_set;
+ }
+ break;
+ case TOD_LOAD_TOD_REG:
+ if (chiptod->tod_state != tod_not_set) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in "
+ " state %d, should be in 7 (TOD_NOT_SET)\n",
+ chiptod->tod_state);
+ } else {
+ if (val & PPC_BIT(63)) {
+ chiptod->tod_state = tod_stopped;
+ } else {
+ chiptod->tod_state = tod_running;
+ }
+ }
+ break;
+
+ case TOD_MOVE_TOD_TO_TB_REG:
+ /*
+ * XXX: it should be a cleaner model to have this drive a SCOM
+ * transaction to the target address, and implement the state machine
+ * in the PnvCore. For now, this hack makes things work.
+ */
+ if (chiptod->tod_state != tod_running) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_MOVE_TOD_TO_TB_REG in bad state %d\n",
+ chiptod->tod_state);
+ } else if (!(val & PPC_BIT(0))) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_MOVE_TOD_TO_TB_REG with bad val 0x%" PRIx64"\n",
+ val);
+ } else if (chiptod->slave_pc_target == NULL) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_MOVE_TOD_TO_TB_REG with no slave target\n");
+ } else {
+ PowerPCCPU *cpu = chiptod->slave_pc_target->threads[0];
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * Moving TOD to TB will set the TB of all threads in a
+ * core, so skiboot only does this once per thread0, so
+ * that is where we keep the timebase state machine.
+ *
+ * It is likely possible for TBST to be driven from other
+ * threads in the core, but for now we only implement it for
+ * thread 0.
+ */
+
+ if (env->pnv_tod_tbst.tb_ready_for_tod) {
+ env->pnv_tod_tbst.tod_sent_to_tb = 1;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: xscom write reg"
+ " TOD_MOVE_TOD_TO_TB_REG with TB not ready to"
+ " receive TOD\n");
+ }
+ }
+ break;
+ case TOD_START_TOD_REG:
+ if (chiptod->tod_state != tod_stopped) {
+ qemu_log_mask(LOG_GUEST_ERROR, "pnv_chiptod: LOAD_TOG_REG in "
+ " state %d, should be in 1 (TOD_STOPPED)\n",
+ chiptod->tod_state);
+ } else {
+ chiptod->tod_state = tod_running;
+ }
+ break;
+ case TOD_TX_TTYPE_4_REG:
+ case TOD_TX_TTYPE_5_REG:
+ pctc->broadcast_ttype(chiptod, offset);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "pnv_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)
+{
+ PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
+ g_autofree char *name = NULL;
+ int offset;
+ uint32_t chiptod_pcba = PNV9_XSCOM_CHIPTOD_BASE;
+ uint32_t reg[] = {
+ cpu_to_be32(chiptod_pcba),
+ cpu_to_be32(PNV9_XSCOM_CHIPTOD_SIZE)
+ };
+
+ name = g_strdup_printf("chiptod@%x", chiptod_pcba);
+ offset = fdt_add_subnode(fdt, xscom_offset, name);
+ _FDT(offset);
+
+ if (chiptod->primary) {
+ _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
+ } else if (chiptod->secondary) {
+ _FDT((fdt_setprop(fdt, offset, "secondary", 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 Property pnv_chiptod_properties[] = {
+ DEFINE_PROP_BOOL("primary", PnvChipTOD, primary, false),
+ DEFINE_PROP_BOOL("secondary", PnvChipTOD, secondary, false),
+ DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data)
+{
+ PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+ dc->desc = "PowerNV ChipTOD Controller (POWER9)";
+ device_class_set_props(dc, pnv_chiptod_properties);
+
+ xdc->dt_xscom = pnv_chiptod_power9_dt_xscom;
+
+ pctc->broadcast_ttype = chiptod_power9_broadcast_ttype;
+ pctc->tx_ttype_target = chiptod_power9_tx_ttype_target;
+
+ pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
+}
+
+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 int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt,
+ int xscom_offset)
+{
+ const char compat[] = "ibm,power-chiptod\0ibm,power10-chiptod";
+
+ return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat));
+}
+
+static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data)
+{
+ PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+ dc->desc = "PowerNV ChipTOD Controller (POWER10)";
+ device_class_set_props(dc, pnv_chiptod_properties);
+
+ xdc->dt_xscom = pnv_chiptod_power10_dt_xscom;
+
+ pctc->broadcast_ttype = chiptod_power10_broadcast_ttype;
+ pctc->tx_ttype_target = chiptod_power10_tx_ttype_target;
+
+ pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE;
+}
+
+static const TypeInfo pnv_chiptod_power10_type_info = {
+ .name = TYPE_PNV10_CHIPTOD,
+ .parent = TYPE_PNV_CHIPTOD,
+ .instance_size = sizeof(PnvChipTOD),
+ .class_init = pnv_chiptod_power10_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_PNV_XSCOM_INTERFACE },
+ { }
+ }
+};
+
+static void pnv_chiptod_reset(void *dev)
+{
+ PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
+
+ chiptod->pss_mss_ctrl_reg = 0;
+ if (chiptod->primary) {
+ chiptod->pss_mss_ctrl_reg |= PPC_BIT(1); /* TOD is master */
+ }
+ /* Drawer is master (we do not simulate multi-drawer) */
+ chiptod->pss_mss_ctrl_reg |= PPC_BIT(2);
+
+ chiptod->tod_error = 0;
+ chiptod->tod_state = tod_error;
+}
+
+static void pnv_chiptod_realize(DeviceState *dev, Error **errp)
+{
+ PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
+ PnvChipTODClass *pctc = PNV_CHIPTOD_GET_CLASS(chiptod);
+
+ /* XScom regions for ChipTOD registers */
+ pnv_xscom_region_init(&chiptod->xscom_regs, OBJECT(dev),
+ &pnv_chiptod_xscom_ops, chiptod, "xscom-chiptod",
+ pctc->xscom_size);
+
+ qemu_register_reset(pnv_chiptod_reset, chiptod);
+}
+
+static void pnv_chiptod_unrealize(DeviceState *dev)
+{
+ PnvChipTOD *chiptod = PNV_CHIPTOD(dev);
+
+ qemu_unregister_reset(pnv_chiptod_reset, chiptod);
+}
+
+static void pnv_chiptod_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = pnv_chiptod_realize;
+ dc->unrealize = pnv_chiptod_unrealize;
+ 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_register_static(&pnv_chiptod_power10_type_info);
+}
+
+type_init(pnv_chiptod_register_types);
diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c
index 656a48e..4581cc5 100644
--- a/hw/ppc/pnv_i2c.c
+++ b/hw/ppc/pnv_i2c.c
@@ -22,136 +22,7 @@
#include <libfdt.h>
-/* I2C FIFO register */
-#define I2C_FIFO_REG 0x4
-#define I2C_FIFO PPC_BITMASK(0, 7)
-
-/* I2C command register */
-#define I2C_CMD_REG 0x5
-#define I2C_CMD_WITH_START PPC_BIT(0)
-#define I2C_CMD_WITH_ADDR PPC_BIT(1)
-#define I2C_CMD_READ_CONT PPC_BIT(2)
-#define I2C_CMD_WITH_STOP PPC_BIT(3)
-#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */
-#define I2C_CMD_INTR_STEER_HOST 1
-#define I2C_CMD_INTR_STEER_OCC 2
-#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14)
-#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15)
-#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31)
-#define I2C_MAX_TFR_LEN 0xfff0ull
-
-/* I2C mode register */
-#define I2C_MODE_REG 0x6
-#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15)
-#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21)
-#define I2C_MODE_ENHANCED PPC_BIT(28)
-#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
-#define I2C_MODE_PACING_ALLOW PPC_BIT(30)
-#define I2C_MODE_WRAP PPC_BIT(31)
-
-/* I2C watermark register */
-#define I2C_WATERMARK_REG 0x7
-#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19)
-#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27)
-
-/*
- * I2C interrupt mask and condition registers
- *
- * NB: The function of 0x9 and 0xa changes depending on whether you're reading
- * or writing to them. When read they return the interrupt condition bits
- * and on writes they update the interrupt mask register.
- *
- * The bit definitions are the same for all the interrupt registers.
- */
-#define I2C_INTR_MASK_REG 0x8
-
-#define I2C_INTR_RAW_COND_REG 0x9 /* read */
-#define I2C_INTR_MASK_OR_REG 0x9 /* write*/
-
-#define I2C_INTR_COND_REG 0xa /* read */
-#define I2C_INTR_MASK_AND_REG 0xa /* write */
-
-#define I2C_INTR_ALL PPC_BITMASK(16, 31)
-#define I2C_INTR_INVALID_CMD PPC_BIT(16)
-#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17)
-#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18)
-#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19)
-#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20)
-#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21)
-#define I2C_INTR_DATA_REQ PPC_BIT(22)
-#define I2C_INTR_CMD_COMP PPC_BIT(23)
-#define I2C_INTR_STOP_ERR PPC_BIT(24)
-#define I2C_INTR_I2C_BUSY PPC_BIT(25)
-#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26)
-#define I2C_INTR_SCL_EQ_1 PPC_BIT(28)
-#define I2C_INTR_SCL_EQ_0 PPC_BIT(29)
-#define I2C_INTR_SDA_EQ_1 PPC_BIT(30)
-#define I2C_INTR_SDA_EQ_0 PPC_BIT(31)
-
-/* I2C status register */
-#define I2C_RESET_I2C_REG 0xb /* write */
-#define I2C_RESET_ERRORS 0xc
-#define I2C_STAT_REG 0xb /* read */
-#define I2C_STAT_INVALID_CMD PPC_BIT(0)
-#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1)
-#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2)
-#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3)
-#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4)
-#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5)
-#define I2C_STAT_DATA_REQ PPC_BIT(6)
-#define I2C_STAT_CMD_COMP PPC_BIT(7)
-#define I2C_STAT_STOP_ERR PPC_BIT(8)
-#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15)
-#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16)
-#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19)
-#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20)
-#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21)
-#define I2C_STAT_PORT_BUSY PPC_BIT(22)
-#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23)
-#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31)
-
-#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \
- I2C_STAT_BKEND_OVERRUN_ERR | \
- I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \
- I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR)
-
-
-#define I2C_INTR_ACTIVE \
- ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ)
-
-/* Pseudo-status used for timeouts */
-#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63)
-
-/* I2C extended status register */
-#define I2C_EXTD_STAT_REG 0xc
-#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7)
-#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15)
-#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16)
-#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17)
-#define I2C_EXTD_STAT_S_SCL PPC_BIT(18)
-#define I2C_EXTD_STAT_S_SDA PPC_BIT(19)
-#define I2C_EXTD_STAT_M_SCL PPC_BIT(20)
-#define I2C_EXTD_STAT_M_SDA PPC_BIT(21)
-#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22)
-#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23)
-#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24)
-#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25)
-#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31)
-
-/* I2C residual front end/back end length */
-#define I2C_RESIDUAL_LEN_REG 0xd
-#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15)
-#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31)
-
-/* Port busy register */
-#define I2C_PORT_BUSY_REG 0xe
-#define I2C_SET_S_SCL_REG 0xd
-#define I2C_RESET_S_SCL_REG 0xf
-#define I2C_SET_S_SDA_REG 0x10
-#define I2C_RESET_S_SDA_REG 0x11
-
-#define PNV_I2C_FIFO_SIZE 8
-#define PNV_I2C_MAX_BUSSES 64
+#include "hw/i2c/pnv_i2c_regs.h"
static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c)
{
@@ -629,6 +500,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void *fdt,
return 0;
}
+static void pnv_i2c_sys_reset(void *dev)
+{
+ int port;
+ PnvI2C *i2c = PNV_I2C(dev);
+
+ pnv_i2c_reset(dev);
+
+ /* reset all buses connected to this i2c controller */
+ for (port = 0; port < i2c->num_busses; port++) {
+ bus_cold_reset(BUS(i2c->busses[port]));
+ }
+}
+
static void pnv_i2c_realize(DeviceState *dev, Error **errp)
{
PnvI2C *i2c = PNV_I2C(dev);
@@ -654,7 +538,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
fifo8_create(&i2c->fifo, PNV_I2C_FIFO_SIZE);
- qemu_register_reset(pnv_i2c_reset, dev);
+ qemu_register_reset(pnv_i2c_sys_reset, dev);
qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1);
}
diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c
new file mode 100644
index 0000000..03ff9fb
--- /dev/null
+++ b/hw/ppc/pnv_n1_chiplet.c
@@ -0,0 +1,173 @@
+/*
+ * QEMU PowerPC N1 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_n1_chiplet.h"
+#include "hw/ppc/pnv_nest_pervasive.h"
+
+/*
+ * The n1 chiplet contains chiplet control unit,
+ * PowerBus/RaceTrack/Bridge logic, nest Memory Management Unit(nMMU)
+ * and more.
+ *
+ * In this model Nest1 chiplet control registers are modelled via common
+ * nest pervasive model and few PowerBus racetrack registers are modelled.
+ */
+
+#define PB_SCOM_EQ0_HP_MODE2_CURR 0xe
+#define PB_SCOM_ES3_MODE 0x8a
+
+static uint64_t pnv_n1_chiplet_pb_scom_eq_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque);
+ uint32_t reg = addr >> 3;
+ uint64_t val = ~0ull;
+
+ switch (reg) {
+ case PB_SCOM_EQ0_HP_MODE2_CURR:
+ val = n1_chiplet->eq[0].hp_mode2_curr;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom read at 0x%" PRIx32 "\n",
+ __func__, reg);
+ }
+ return val;
+}
+
+static void pnv_n1_chiplet_pb_scom_eq_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque);
+ uint32_t reg = addr >> 3;
+
+ switch (reg) {
+ case PB_SCOM_EQ0_HP_MODE2_CURR:
+ n1_chiplet->eq[0].hp_mode2_curr = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom write at 0x%" PRIx32 "\n",
+ __func__, reg);
+ }
+}
+
+static const MemoryRegionOps pnv_n1_chiplet_pb_scom_eq_ops = {
+ .read = pnv_n1_chiplet_pb_scom_eq_read,
+ .write = pnv_n1_chiplet_pb_scom_eq_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 uint64_t pnv_n1_chiplet_pb_scom_es_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque);
+ uint32_t reg = addr >> 3;
+ uint64_t val = ~0ull;
+
+ switch (reg) {
+ case PB_SCOM_ES3_MODE:
+ val = n1_chiplet->es[3].mode;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom read at 0x%" PRIx32 "\n",
+ __func__, reg);
+ }
+ return val;
+}
+
+static void pnv_n1_chiplet_pb_scom_es_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(opaque);
+ uint32_t reg = addr >> 3;
+
+ switch (reg) {
+ case PB_SCOM_ES3_MODE:
+ n1_chiplet->es[3].mode = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: Invalid xscom write at 0x%" PRIx32 "\n",
+ __func__, reg);
+ }
+}
+
+static const MemoryRegionOps pnv_n1_chiplet_pb_scom_es_ops = {
+ .read = pnv_n1_chiplet_pb_scom_es_read,
+ .write = pnv_n1_chiplet_pb_scom_es_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_n1_chiplet_realize(DeviceState *dev, Error **errp)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(dev);
+
+ /* Realize nest pervasive common chiplet model */
+ if (!qdev_realize(DEVICE(&n1_chiplet->nest_pervasive), NULL, errp)) {
+ return;
+ }
+
+ /* Nest1 chiplet power bus EQ xscom region */
+ pnv_xscom_region_init(&n1_chiplet->xscom_pb_eq_mr, OBJECT(n1_chiplet),
+ &pnv_n1_chiplet_pb_scom_eq_ops, n1_chiplet,
+ "xscom-n1-chiplet-pb-scom-eq",
+ PNV10_XSCOM_N1_PB_SCOM_EQ_SIZE);
+
+ /* Nest1 chiplet power bus ES xscom region */
+ pnv_xscom_region_init(&n1_chiplet->xscom_pb_es_mr, OBJECT(n1_chiplet),
+ &pnv_n1_chiplet_pb_scom_es_ops, n1_chiplet,
+ "xscom-n1-chiplet-pb-scom-es",
+ PNV10_XSCOM_N1_PB_SCOM_ES_SIZE);
+}
+
+static void pnv_n1_chiplet_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "PowerNV n1 chiplet";
+ dc->realize = pnv_n1_chiplet_realize;
+}
+
+static void pnv_n1_chiplet_instance_init(Object *obj)
+{
+ PnvN1Chiplet *n1_chiplet = PNV_N1_CHIPLET(obj);
+
+ object_initialize_child(OBJECT(n1_chiplet), "nest-pervasive-common",
+ &n1_chiplet->nest_pervasive,
+ TYPE_PNV_NEST_CHIPLET_PERVASIVE);
+}
+
+static const TypeInfo pnv_n1_chiplet_info = {
+ .name = TYPE_PNV_N1_CHIPLET,
+ .parent = TYPE_DEVICE,
+ .instance_init = pnv_n1_chiplet_instance_init,
+ .instance_size = sizeof(PnvN1Chiplet),
+ .class_init = pnv_n1_chiplet_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_PNV_XSCOM_INTERFACE },
+ { }
+ }
+};
+
+static void pnv_n1_chiplet_register_types(void)
+{
+ type_register_static(&pnv_n1_chiplet_info);
+}
+
+type_init(pnv_n1_chiplet_register_types);
diff --git a/hw/ppc/pnv_nest_pervasive.c b/hw/ppc/pnv_nest_pervasive.c
new file mode 100644
index 0000000..7747675
--- /dev/null
+++ b/hw/ppc/pnv_nest_pervasive.c
@@ -0,0 +1,208 @@
+/*
+ * 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);
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 0d72d28..d1c6d70 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -4639,13 +4639,10 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->block_default_type = IF_SCSI;
/*
- * Setting max_cpus to INT32_MAX. Both KVM and TCG max_cpus values
- * should be limited by the host capability instead of hardcoded.
- * max_cpus for KVM guests will be checked in kvm_init(), and TCG
- * guests are welcome to have as many CPUs as the host are capable
- * of emulate.
+ * While KVM determines max cpus in kvm_init() using kvm_max_vcpus(),
+ * In TCG the limit is restricted by the range of CPU IPIs available.
*/
- mc->max_cpus = INT32_MAX;
+ mc->max_cpus = SPAPR_IRQ_NR_IPIS;
mc->no_parallel = 1;
mc->default_boot_order = "";
@@ -4667,7 +4664,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
smc->dr_lmb_enabled = true;
smc->update_dt_enabled = true;
- mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.2");
+ mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
mc->has_hotpluggable_cpus = true;
mc->nvdimm_supported = true;
smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED;
@@ -5086,6 +5083,7 @@ static void spapr_machine_2_11_class_options(MachineClass *mc)
spapr_machine_2_12_class_options(mc);
smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON;
compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len);
+ mc->deprecation_reason = "old and not maintained - use a 2.12+ version";
}
DEFINE_SPAPR_MACHINE(2_11, "2.11", false);
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index fcefd1d..75c2d12 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -123,9 +123,11 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu,
if (kvm_enabled()) {
return H_HARDWARE;
+ } else if (tcg_enabled()) {
+ return vhyp_mmu_resize_hpt_prepare(cpu, spapr, shift);
+ } else {
+ g_assert_not_reached();
}
-
- return softmmu_resize_hpt_prepare(cpu, spapr, shift);
}
static void do_push_sregs_to_kvm_pr(CPUState *cs, run_on_cpu_data data)
@@ -191,9 +193,11 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu,
if (kvm_enabled()) {
return H_HARDWARE;
+ } else if (tcg_enabled()) {
+ return vhyp_mmu_resize_hpt_commit(cpu, spapr, flags, shift);
+ } else {
+ g_assert_not_reached();
}
-
- return softmmu_resize_hpt_commit(cpu, spapr, flags, shift);
}
diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c
index a0d1e12..97b2fc4 100644
--- a/hw/ppc/spapr_irq.c
+++ b/hw/ppc/spapr_irq.c
@@ -23,6 +23,8 @@
#include "trace.h"
+QEMU_BUILD_BUG_ON(SPAPR_IRQ_NR_IPIS > SPAPR_XIRQ_BASE);
+
static const TypeInfo spapr_intc_info = {
.name = TYPE_SPAPR_INTC,
.parent = TYPE_INTERFACE,
@@ -329,7 +331,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
int i;
dev = qdev_new(TYPE_SPAPR_XIVE);
- qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_XIRQ_BASE);
+ qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_IRQ_NR_IPIS);
/*
* 8 XIVE END structures per CPU. One for each available
* priority
@@ -356,7 +358,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp)
}
spapr->qirqs = qemu_allocate_irqs(spapr_set_irq, spapr,
- smc->nr_xirqs + SPAPR_XIRQ_BASE);
+ smc->nr_xirqs + SPAPR_IRQ_NR_IPIS);
/*
* Mostly we don't actually need this until reset, except that not
diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_vhyp_mmu.c
index fc1bbc0..b3dd8b3 100644
--- a/hw/ppc/spapr_softmmu.c
+++ b/hw/ppc/spapr_vhyp_mmu.c
@@ -1,3 +1,12 @@
+/*
+ * MMU hypercalls for the sPAPR (pseries) vHyp hypervisor that is used by TCG
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2010 David Gibson, IBM Corporation.
+ *
+ * SPDX-License-Identifier: MIT
+ */
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qemu/memalign.h"
@@ -369,7 +378,7 @@ static void cancel_hpt_prepare(SpaprMachineState *spapr)
free_pending_hpt(pending);
}
-target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu,
+target_ulong vhyp_mmu_resize_hpt_prepare(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong shift)
{
@@ -553,7 +562,7 @@ static int rehash_hpt(PowerPCCPU *cpu,
return H_SUCCESS;
}
-target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu,
+target_ulong vhyp_mmu_resize_hpt_commit(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong flags,
target_ulong shift)
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 157ea75..bf29bbf 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