* 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-----

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>
This commit is contained in:
Peter Maydell 2024-02-23 18:59:11 +00:00
commit 91e3bf2e92
54 changed files with 3318 additions and 352 deletions

View File

@ -1170,9 +1170,7 @@ R: Joel Stanley <joel@jms.id.au>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/*aspeed*
F: hw/misc/pca9552.c
F: include/hw/*/*aspeed*
F: include/hw/misc/pca9552*.h
F: hw/net/ftgmac100.c
F: include/hw/net/ftgmac100.h
F: docs/system/arm/aspeed.rst
@ -1526,6 +1524,7 @@ F: tests/qtest/libqos/*spapr*
F: tests/qtest/rtas*
F: tests/qtest/libqos/rtas*
F: tests/avocado/ppc_pseries.py
F: tests/avocado/ppc_hv_tests.py
PowerNV (Non-Virtualized)
M: Cédric Le Goater <clg@kaod.org>
@ -1543,6 +1542,14 @@ F: include/hw/pci-host/pnv*
F: pc-bios/skiboot.lid
F: tests/qtest/pnv*
pca955x
M: Glenn Miles <milesg@linux.vnet.ibm.com>
L: qemu-ppc@nongnu.org
L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/misc/pca955*.c
F: include/hw/misc/pca955*.h
virtex_ml507
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
L: qemu-ppc@nongnu.org

View File

@ -229,6 +229,14 @@ The Nios II architecture is orphan.
The machine is no longer in existence and has been long unmaintained
in QEMU. This also holds for the TC51828 16MiB flash that it uses.
``pseries-2.1`` up to ``pseries-2.11`` (since 9.0)
''''''''''''''''''''''''''''''''''''''''''''''''''
Older pseries machines before version 2.12 have undergone many changes
to correct issues, mostly regarding migration compatibility. These are
no longer maintained and removing them will make the code easier to
read and maintain. Use versions 2.12 and above as a replacement.
Backend options
---------------

View File

@ -1346,6 +1346,17 @@ the environment.
The definition of *large* is a bit arbitrary here, but it usually means an
asset which occupies at least 1GB of size on disk when uncompressed.
SPEED
^^^^^
Tests which have a long runtime will not be run unless ``SPEED=slow`` is
exported on the environment.
The definition of *long* is a bit arbitrary here, and it depends on the
usefulness of the test too. A unique test is worth spending more time on,
small variations on existing tests perhaps less so. As a rough guide,
a test or set of similar tests which take more than 100 seconds to
complete.
AVOCADO_ALLOW_UNTRUSTED_CODE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are tests which will boot a kernel image or firmware that can be

View File

@ -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

View File

@ -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'))

View File

@ -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[] = {

328
hw/misc/pca9554.c Normal file
View File

@ -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)

View File

@ -32,6 +32,8 @@ config POWERNV
select XIVE
select FDT_PPC
select PCI_POWERNV
select PCA9552
select PCA9554
config PPC405
bool

View File

@ -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(

View File

@ -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);
@ -2381,6 +2499,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,

586
hw/ppc/pnv_chiptod.c Normal file
View File

@ -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);

View File

@ -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);
}

173
hw/ppc/pnv_n1_chiplet.c Normal file
View File

@ -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);

208
hw/ppc/pnv_nest_pervasive.c Normal file
View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,143 @@
/*
* PowerNV I2C Controller Register Definitions
*
* Copyright (c) 2024, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PNV_I2C_REGS_H
#define PNV_I2C_REGS_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
#endif /* PNV_I2C_REGS_H */

View File

@ -30,7 +30,8 @@ struct PCA955xState {
uint8_t pointer;
uint8_t regs[PCA955X_NR_REGS];
qemu_irq gpio[PCA955X_PIN_COUNT_MAX];
qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX];
uint8_t ext_state[PCA955X_PIN_COUNT_MAX];
char *description; /* For debugging purpose only */
};

36
include/hw/misc/pca9554.h Normal file
View File

@ -0,0 +1,36 @@
/*
* PCA9554 I/O port
*
* Copyright (c) 2023, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PCA9554_H
#define PCA9554_H
#include "hw/i2c/i2c.h"
#include "qom/object.h"
#define TYPE_PCA9554 "pca9554"
typedef struct PCA9554State PCA9554State;
DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554,
TYPE_PCA9554)
#define PCA9554_NR_REGS 4
#define PCA9554_PIN_COUNT 8
struct PCA9554State {
/*< private >*/
I2CSlave i2c;
/*< public >*/
uint8_t len;
uint8_t pointer;
uint8_t regs[PCA9554_NR_REGS];
qemu_irq gpio_out[PCA9554_PIN_COUNT];
uint8_t ext_state[PCA9554_PIN_COUNT];
char *description; /* For debugging purpose only */
};
#endif

View File

@ -0,0 +1,19 @@
/*
* PCA9554 I/O port registers
*
* Copyright (c) 2023, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PCA9554_REGS_H
#define PCA9554_REGS_H
/*
* Bits [0:1] are used to address a specific register.
*/
#define PCA9554_INPUT 0 /* read only input register */
#define PCA9554_OUTPUT 1 /* read/write pin output state */
#define PCA9554_POLARITY 2 /* Set polarity of input register */
#define PCA9554_CONFIG 3 /* Set pins as inputs our ouputs */
#endif

View File

@ -28,6 +28,7 @@
#define TYPE_PNV_CHIP "pnv-chip"
typedef struct PnvCore PnvCore;
typedef struct PnvChip PnvChip;
typedef struct Pnv8Chip Pnv8Chip;
typedef struct Pnv9Chip Pnv9Chip;
@ -56,6 +57,7 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER9,
DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10,
TYPE_PNV_CHIP_POWER10)
PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id);
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
typedef struct PnvPHB PnvPHB;
@ -76,6 +78,7 @@ struct PnvMachineClass {
int compat_size;
void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt);
void (*i2c_init)(PnvMachineState *pnv);
};
struct PnvMachineState {

View File

@ -2,8 +2,10 @@
#define PPC_PNV_CHIP_H
#include "hw/pci-host/pnv_phb4.h"
#include "hw/ppc/pnv_chiptod.h"
#include "hw/ppc/pnv_core.h"
#include "hw/ppc/pnv_homer.h"
#include "hw/ppc/pnv_n1_chiplet.h"
#include "hw/ppc/pnv_lpc.h"
#include "hw/ppc/pnv_occ.h"
#include "hw/ppc/pnv_psi.h"
@ -78,6 +80,7 @@ struct Pnv9Chip {
PnvXive xive;
Pnv9Psi psi;
PnvLpcController lpc;
PnvChipTOD chiptod;
PnvOCC occ;
PnvSBE sbe;
PnvHomer homer;
@ -110,9 +113,11 @@ struct Pnv10Chip {
PnvXive2 xive;
Pnv9Psi psi;
PnvLpcController lpc;
PnvChipTOD chiptod;
PnvOCC occ;
PnvSBE sbe;
PnvHomer homer;
PnvN1Chiplet n1_chiplet;
uint32_t nr_quads;
PnvQuad *quads;

View File

@ -0,0 +1,53 @@
/*
* QEMU PowerPC PowerNV Emulation of some CHIPTOD behaviour
*
* Copyright (c) 2022-2023, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#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_error = 0,
tod_not_set = 7,
tod_running = 2,
tod_stopped = 1,
};
typedef struct PnvCore PnvCore;
struct PnvChipTOD {
DeviceState xd;
PnvChip *chip;
MemoryRegion xscom_regs;
bool primary;
bool secondary;
enum tod_state tod_state;
uint64_t tod_error;
uint64_t pss_mss_ctrl_reg;
PnvCore *slave_pc_target;
};
struct PnvChipTODClass {
DeviceClass parent_class;
void (*broadcast_ttype)(PnvChipTOD *sender, uint32_t trigger);
PnvCore *(*tx_ttype_target)(PnvChipTOD *chiptod, uint64_t val);
int xscom_size;
};
#endif /* PPC_PNV_CHIPTOD_H */

View File

@ -0,0 +1,32 @@
/*
* QEMU PowerPC N1 chiplet model
*
* Copyright (c) 2023, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PPC_PNV_N1_CHIPLET_H
#define PPC_PNV_N1_CHIPLET_H
#include "hw/ppc/pnv_nest_pervasive.h"
#define TYPE_PNV_N1_CHIPLET "pnv-N1-chiplet"
#define PNV_N1_CHIPLET(obj) OBJECT_CHECK(PnvN1Chiplet, (obj), TYPE_PNV_N1_CHIPLET)
typedef struct PnvPbScom {
uint64_t mode;
uint64_t hp_mode2_curr;
} PnvPbScom;
typedef struct PnvN1Chiplet {
DeviceState parent;
MemoryRegion xscom_pb_eq_mr;
MemoryRegion xscom_pb_es_mr;
PnvNestChipletPervasive nest_pervasive; /* common pervasive chiplet unit */
#define PNV_PB_SCOM_EQ_SIZE 8
PnvPbScom eq[PNV_PB_SCOM_EQ_SIZE];
#define PNV_PB_SCOM_ES_SIZE 4
PnvPbScom es[PNV_PB_SCOM_ES_SIZE];
} PnvN1Chiplet;
#endif /*PPC_PNV_N1_CHIPLET_H */

View File

@ -0,0 +1,32 @@
/*
* QEMU PowerPC nest pervasive common chiplet model
*
* Copyright (c) 2023, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PPC_PNV_NEST_CHIPLET_PERVASIVE_H
#define PPC_PNV_NEST_CHIPLET_PERVASIVE_H
#define TYPE_PNV_NEST_CHIPLET_PERVASIVE "pnv-nest-chiplet-pervasive"
#define PNV_NEST_CHIPLET_PERVASIVE(obj) OBJECT_CHECK(PnvNestChipletPervasive, (obj), TYPE_PNV_NEST_CHIPLET_PERVASIVE)
typedef struct PnvPervasiveCtrlRegs {
#define PNV_CPLT_CTRL_SIZE 6
uint64_t cplt_ctrl[PNV_CPLT_CTRL_SIZE];
uint64_t cplt_cfg0;
uint64_t cplt_cfg1;
uint64_t cplt_stat0;
uint64_t cplt_mask0;
uint64_t ctrl_protect_mode;
uint64_t ctrl_atomic_lock;
} PnvPervasiveCtrlRegs;
typedef struct PnvNestChipletPervasive {
DeviceState parent;
MemoryRegion xscom_ctrl_regs_mr;
PnvPervasiveCtrlRegs control_regs;
} PnvNestChipletPervasive;
#endif /*PPC_PNV_NEST_CHIPLET_PERVASIVE_H */

View File

@ -64,6 +64,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
@ -93,6 +96,9 @@ struct PnvXScomInterfaceClass {
#define PNV9_XSCOM_I2CM_BASE 0xa0000
#define PNV9_XSCOM_I2CM_SIZE 0x1000
#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
@ -155,6 +161,9 @@ struct PnvXScomInterfaceClass {
#define PNV10_XSCOM_I2CM_BASE PNV9_XSCOM_I2CM_BASE
#define PNV10_XSCOM_I2CM_SIZE PNV9_XSCOM_I2CM_SIZE
#define PNV10_XSCOM_CHIPTOD_BASE PNV9_XSCOM_CHIPTOD_BASE
#define PNV10_XSCOM_CHIPTOD_SIZE PNV9_XSCOM_CHIPTOD_SIZE
#define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE
#define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE
@ -170,6 +179,15 @@ struct PnvXScomInterfaceClass {
#define PNV10_XSCOM_XIVE2_BASE 0x2010800
#define PNV10_XSCOM_XIVE2_SIZE 0x400
#define PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE 0x3000000
#define PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE 0x400
#define PNV10_XSCOM_N1_PB_SCOM_EQ_BASE 0x3011000
#define PNV10_XSCOM_N1_PB_SCOM_EQ_SIZE 0x200
#define PNV10_XSCOM_N1_PB_SCOM_ES_BASE 0x3011300
#define PNV10_XSCOM_N1_PB_SCOM_ES_SIZE 0x100
#define PNV10_XSCOM_PEC_NEST_BASE 0x3011800 /* index goes downwards ... */
#define PNV10_XSCOM_PEC_NEST_SIZE 0x100

View File

@ -634,10 +634,13 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
target_ulong *args);
target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong vhyp_mmu_resize_hpt_prepare(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong shift);
target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, SpaprMachineState *spapr,
target_ulong flags, target_ulong shift);
target_ulong vhyp_mmu_resize_hpt_commit(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong flags,
target_ulong shift);
bool is_ram_address(SpaprMachineState *spapr, hwaddr addr);
void push_sregs_to_kvm_pr(SpaprMachineState *spapr);

View File

@ -14,9 +14,21 @@
#include "qom/object.h"
/*
* IRQ range offsets per device type
* The XIVE IRQ backend uses the same layout as the XICS backend but
* covers the full range of the IRQ number space. The IRQ numbers for
* the CPU IPIs are allocated at the bottom of this space, below 4K,
* to preserve compatibility with XICS which does not use that range.
*/
/*
* CPU IPI range (XIVE only)
*/
#define SPAPR_IRQ_IPI 0x0
#define SPAPR_IRQ_NR_IPIS 0x1000
/*
* IRQ range offsets per device type
*/
#define SPAPR_XIRQ_BASE XICS_IRQ_BASE /* 0x1000 */
#define SPAPR_IRQ_EPOW (SPAPR_XIRQ_BASE + 0x0000)

Binary file not shown.

View File

@ -1183,6 +1183,21 @@ DEXCR_ASPECT(SRAPD, 4)
DEXCR_ASPECT(NPHIE, 5)
DEXCR_ASPECT(PHIE, 6)
/*****************************************************************************/
/* PowerNV ChipTOD and TimeBase State Machine */
struct pnv_tod_tbst {
int tb_ready_for_tod; /* core TB ready to receive TOD from chiptod */
int tod_sent_to_tb; /* chiptod sent TOD to the core TB */
/*
* "Timers" for async TBST events are simulated by mfTFAC because TFAC
* is polled for such events. These are just used to ensure firmware
* performs the polling at least a few times.
*/
int tb_state_timer;
int tb_sync_pulse_timer;
};
/*****************************************************************************/
/* The whole PowerPC CPU context */
@ -1258,6 +1273,12 @@ 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 chiptod / timebase facility state. */
/* Would be nice to put these into PnvCore */
struct pnv_tod_tbst pnv_tod_tbst;
#endif
#endif
/* Other registers */
@ -1750,8 +1771,8 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_USPRG5 (0x105)
#define SPR_USPRG6 (0x106)
#define SPR_USPRG7 (0x107)
#define SPR_VTBL (0x10C)
#define SPR_VTBU (0x10D)
#define SPR_TBL (0x10C)
#define SPR_TBU (0x10D)
#define SPR_SPRG0 (0x110)
#define SPR_SPRG1 (0x111)
#define SPR_SPRG2 (0x112)
@ -1764,8 +1785,8 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_SPRG7 (0x117)
#define SPR_ASR (0x118)
#define SPR_EAR (0x11A)
#define SPR_TBL (0x11C)
#define SPR_TBU (0x11D)
#define SPR_WR_TBL (0x11C)
#define SPR_WR_TBU (0x11D)
#define SPR_TBU40 (0x11E)
#define SPR_SVR (0x11E)
#define SPR_BOOKE_PIR (0x11E)
@ -2648,6 +2669,34 @@ enum {
HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23),
};
/* TFMR */
enum {
TFMR_CONTROL_MASK = PPC_BITMASK(0, 24),
TFMR_MASK_HMI = PPC_BIT(10),
TFMR_TB_ECLIPZ = PPC_BIT(14),
TFMR_LOAD_TOD_MOD = PPC_BIT(16),
TFMR_MOVE_CHIP_TOD_TO_TB = PPC_BIT(18),
TFMR_CLEAR_TB_ERRORS = PPC_BIT(24),
TFMR_STATUS_MASK = PPC_BITMASK(25, 63),
TFMR_TBST_ENCODED = PPC_BITMASK(28, 31), /* TBST = TB State */
TFMR_TBST_LAST = PPC_BITMASK(32, 35), /* Previous TBST */
TFMR_TB_ENABLED = PPC_BIT(40),
TFMR_TB_VALID = PPC_BIT(41),
TFMR_TB_SYNC_OCCURED = PPC_BIT(42),
TFMR_FIRMWARE_CONTROL_ERROR = PPC_BIT(46),
};
/* TFMR TBST (Time Base State Machine). */
enum {
TBST_RESET = 0x0,
TBST_SEND_TOD_MOD = 0x1,
TBST_NOT_SET = 0x2,
TBST_SYNC_WAIT = 0x6,
TBST_GET_TOD = 0x7,
TBST_TB_RUNNING = 0x8,
TBST_TB_ERROR = 0x9,
};
/*****************************************************************************/
#define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300))

View File

@ -5062,7 +5062,7 @@ static void register_970_hid_sprs(CPUPPCState *env)
static void register_970_hior_sprs(CPUPPCState *env)
{
spr_register(env, SPR_HIOR, "SPR_HIOR",
spr_register(env, SPR_HIOR, "HIOR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_hior, &spr_write_hior,
0x00000000);
@ -5070,11 +5070,11 @@ static void register_970_hior_sprs(CPUPPCState *env)
static void register_book3s_ctrl_sprs(CPUPPCState *env)
{
spr_register(env, SPR_CTRL, "SPR_CTRL",
spr_register(env, SPR_CTRL, "CTRL",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, &spr_write_CTRL,
0x00000000);
spr_register(env, SPR_UCTRL, "SPR_UCTRL",
spr_register(env, SPR_UCTRL, "UCTRL",
&spr_read_ureg, SPR_NOACCESS,
&spr_read_ureg, SPR_NOACCESS,
0x00000000);
@ -5465,7 +5465,7 @@ static void register_book3s_purr_sprs(CPUPPCState *env)
static void register_power6_dbg_sprs(CPUPPCState *env)
{
#if !defined(CONFIG_USER_ONLY)
spr_register(env, SPR_CFAR, "SPR_CFAR",
spr_register(env, SPR_CFAR, "CFAR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_cfar, &spr_write_cfar,
0x00000000);
@ -5483,7 +5483,7 @@ static void register_power5p_common_sprs(CPUPPCState *env)
static void register_power6_common_sprs(CPUPPCState *env)
{
#if !defined(CONFIG_USER_ONLY)
spr_register_kvm(env, SPR_DSCR, "SPR_DSCR",
spr_register_kvm(env, SPR_DSCR, "DSCR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_DSCR, 0x00000000);
@ -5695,7 +5695,7 @@ static void register_power8_book4_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_ACOP, 0);
/* PID is only in BookE in ISA v2.07 */
spr_register_kvm(env, SPR_BOOKS_PID, "PID",
spr_register_kvm(env, SPR_BOOKS_PID, "PIDR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_pidr,
KVM_REG_PPC_PID, 0);
@ -5716,7 +5716,7 @@ static void register_power7_book4_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_ACOP, 0);
/* PID is only in BookE in ISA v2.06 */
spr_register_kvm(env, SPR_BOOKS_PID, "PID",
spr_register_kvm(env, SPR_BOOKS_PID, "PIDR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic32,
KVM_REG_PPC_PID, 0);
@ -5750,7 +5750,7 @@ static void register_power9_mmu_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic,
0x0000000000000000);
/* PID is part of the BookS ISA from v3.0 */
spr_register_kvm(env, SPR_BOOKS_PID, "PID",
spr_register_kvm(env, SPR_BOOKS_PID, "PIDR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_pidr,
KVM_REG_PPC_PID, 0);
@ -5791,7 +5791,7 @@ static void register_power10_dexcr_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic32,
0);
spr_register(env, SPR_UDEXCR, "DEXCR",
spr_register(env, SPR_UDEXCR, "UDEXCR",
&spr_read_dexcr_ureg, SPR_NOACCESS,
&spr_read_dexcr_ureg, SPR_NOACCESS,
0);
@ -5802,7 +5802,7 @@ static void register_power10_dexcr_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic32,
0);
spr_register(env, SPR_UHDEXCR, "HDEXCR",
spr_register(env, SPR_UHDEXCR, "UHDEXCR",
&spr_read_dexcr_ureg, SPR_NOACCESS,
&spr_read_dexcr_ureg, SPR_NOACCESS,
0);

View File

@ -1312,6 +1312,10 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
{
CPUPPCState *env = &cpu->env;
if (!(env->insns_flags2 & PPC2_ISA310)) {
return false;
}
if (!tcg_enabled()) {
/*
* This does not load instructions and set the prefix bit correctly
@ -1322,6 +1326,15 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
}
switch (excp) {
case POWERPC_EXCP_MCHECK:
if (!(env->error_code & PPC_BIT(42))) {
/*
* Fetch attempt caused a machine check, so attempting to fetch
* again would cause a recursive machine check.
*/
return false;
}
break;
case POWERPC_EXCP_HDSI:
/* HDSI PRTABLE_FAULT has the originating access type in error_code */
if ((env->spr[SPR_HDSISR] & DSISR_PRTABLE_FAULT) &&
@ -1332,10 +1345,10 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
* instruction at NIP would cause recursive faults with the same
* translation).
*/
break;
return false;
}
/* fall through */
case POWERPC_EXCP_MCHECK:
break;
case POWERPC_EXCP_DSI:
case POWERPC_EXCP_DSEG:
case POWERPC_EXCP_ALIGN:
@ -1346,17 +1359,13 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
case POWERPC_EXCP_VPU:
case POWERPC_EXCP_VSXU:
case POWERPC_EXCP_FU:
case POWERPC_EXCP_HV_FU: {
uint32_t insn = ppc_ldl_code(env, env->nip);
if (is_prefix_insn(env, insn)) {
return true;
}
case POWERPC_EXCP_HV_FU:
break;
}
default:
break;
return false;
}
return false;
return is_prefix_insn(env, ppc_ldl_code(env, env->nip));
}
#else
static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp)
@ -3224,6 +3233,7 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
switch (env->excp_model) {
#if defined(TARGET_PPC64)
case POWERPC_EXCP_POWER8:
case POWERPC_EXCP_POWER9:
case POWERPC_EXCP_POWER10:
/*
@ -3245,6 +3255,10 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
env->error_code |= PPC_BIT(42);
} else { /* Fetch */
/*
* is_prefix_insn_excp() tests !PPC_BIT(42) to avoid fetching
* the instruction, so that must always be clear for fetches.
*/
env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45);
}
break;

View File

@ -394,7 +394,32 @@ static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n)
}
len = TARGET_LONG_SIZE;
gdb_get_regl(buf, env->spr[reg]);
/* Handle those SPRs that are not part of the env->spr[] array */
target_ulong val;
switch (reg) {
#if defined(TARGET_PPC64)
case SPR_CFAR:
val = env->cfar;
break;
#endif
case SPR_HDEC:
val = cpu_ppc_load_hdecr(env);
break;
case SPR_TBL:
val = cpu_ppc_load_tbl(env);
break;
case SPR_TBU:
val = cpu_ppc_load_tbu(env);
break;
case SPR_DECR:
val = cpu_ppc_load_decr(env);
break;
default:
val = env->spr[reg];
}
gdb_get_regl(buf, val);
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len);
return len;
}
@ -411,7 +436,18 @@ static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
len = TARGET_LONG_SIZE;
ppc_maybe_bswap_register(env, mem_buf, len);
env->spr[reg] = ldn_p(mem_buf, len);
/* Handle those SPRs that are not part of the env->spr[] array */
target_ulong val = ldn_p(mem_buf, len);
switch (reg) {
#if defined(TARGET_PPC64)
case SPR_CFAR:
env->cfar = val;
break;
#endif
default:
env->spr[reg] = val;
}
return len;
}

View File

@ -460,22 +460,41 @@ void register_generic_sprs(PowerPCCPU *cpu)
}
/* Time base */
spr_register(env, SPR_VTBL, "TBL",
#if defined(TARGET_PPC64)
spr_register(env, SPR_TBL, "TB",
#else
spr_register(env, SPR_TBL, "TBL",
#endif
&spr_read_tbl, SPR_NOACCESS,
&spr_read_tbl, SPR_NOACCESS,
0x00000000);
spr_register(env, SPR_TBL, "TBL",
&spr_read_tbl, SPR_NOACCESS,
&spr_read_tbl, &spr_write_tbl,
0x00000000);
spr_register(env, SPR_VTBU, "TBU",
spr_register(env, SPR_TBU, "TBU",
&spr_read_tbu, SPR_NOACCESS,
&spr_read_tbu, SPR_NOACCESS,
0x00000000);
spr_register(env, SPR_TBU, "TBU",
&spr_read_tbu, SPR_NOACCESS,
&spr_read_tbu, &spr_write_tbu,
0x00000000);
#ifndef CONFIG_USER_ONLY
if (env->has_hv_mode) {
spr_register_hv(env, SPR_WR_TBL, "TBL",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, &spr_write_tbl,
0x00000000);
spr_register_hv(env, SPR_WR_TBU, "TBU",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, &spr_write_tbu,
0x00000000);
} else {
spr_register(env, SPR_WR_TBL, "TBL",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, &spr_write_tbl,
0x00000000);
spr_register(env, SPR_WR_TBU, "TBU",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, &spr_write_tbu,
0x00000000);
}
#endif
}
void register_non_embedded_sprs(CPUPPCState *env)
@ -490,7 +509,7 @@ void register_non_embedded_sprs(CPUPPCState *env)
&spr_read_generic, &spr_write_generic,
KVM_REG_PPC_DAR, 0x00000000);
/* Timer */
spr_register(env, SPR_DECR, "DECR",
spr_register(env, SPR_DECR, "DEC",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_decr, &spr_write_decr,
0x00000000);

View File

@ -749,12 +749,29 @@ target_ulong helper_4xx_tlbre_lo(CPUPPCState *env, target_ulong entry)
return ret;
}
static void ppcemb_tlb_flush(CPUState *cs, ppcemb_tlb_t *tlb)
{
unsigned mmu_idx = 0;
if (tlb->prot & 0xf) {
mmu_idx |= 0x1;
}
if ((tlb->prot >> 4) & 0xf) {
mmu_idx |= 0x2;
}
if (tlb->attr & 1) {
mmu_idx <<= 2;
}
tlb_flush_range_by_mmuidx(cs, tlb->EPN, tlb->size, mmu_idx,
TARGET_LONG_BITS);
}
void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry,
target_ulong val)
{
CPUState *cs = env_cpu(env);
ppcemb_tlb_t *tlb;
target_ulong page, end;
qemu_log_mask(CPU_LOG_MMU, "%s entry %d val " TARGET_FMT_lx "\n",
__func__, (int)entry,
@ -762,14 +779,11 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry,
entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb.tlbe[entry];
/* Invalidate previous TLB (if it's valid) */
if (tlb->prot & PAGE_VALID) {
end = tlb->EPN + tlb->size;
if ((tlb->prot & PAGE_VALID) && tlb->PID == env->spr[SPR_40x_PID]) {
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start "
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
(int)entry, tlb->EPN, end);
for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
tlb_flush_page(cs, page);
}
(int)entry, tlb->EPN, tlb->EPN + tlb->size);
ppcemb_tlb_flush(cs, tlb);
}
tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT)
& PPC4XX_TLBHI_SIZE_MASK);
@ -803,27 +817,25 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry,
tlb->prot & PAGE_WRITE ? 'w' : '-',
tlb->prot & PAGE_EXEC ? 'x' : '-',
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
/* Invalidate new TLB (if valid) */
if (tlb->prot & PAGE_VALID) {
end = tlb->EPN + tlb->size;
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate TLB %d start "
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
(int)entry, tlb->EPN, end);
for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
tlb_flush_page(cs, page);
}
}
}
void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry,
target_ulong val)
{
CPUState *cs = env_cpu(env);
ppcemb_tlb_t *tlb;
qemu_log_mask(CPU_LOG_MMU, "%s entry %i val " TARGET_FMT_lx "\n",
__func__, (int)entry, val);
entry &= PPC4XX_TLB_ENTRY_MASK;
tlb = &env->tlb.tlbe[entry];
/* Invalidate previous TLB (if it's valid) */
if ((tlb->prot & PAGE_VALID) && tlb->PID == env->spr[SPR_40x_PID]) {
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start "
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
(int)entry, tlb->EPN, tlb->EPN + tlb->size);
ppcemb_tlb_flush(cs, tlb);
}
tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK;
tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK;
tlb->prot = PAGE_READ;
@ -841,8 +853,6 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry,
tlb->prot & PAGE_WRITE ? 'w' : '-',
tlb->prot & PAGE_EXEC ? 'x' : '-',
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
}
target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address)
@ -850,54 +860,61 @@ target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address)
return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]);
}
static bool mmubooke_pid_match(CPUPPCState *env, ppcemb_tlb_t *tlb)
{
if (tlb->PID == env->spr[SPR_BOOKE_PID]) {
return true;
}
if (!env->nb_pids) {
return false;
}
if (env->spr[SPR_BOOKE_PID1] && tlb->PID == env->spr[SPR_BOOKE_PID1]) {
return true;
}
if (env->spr[SPR_BOOKE_PID2] && tlb->PID == env->spr[SPR_BOOKE_PID2]) {
return true;
}
return false;
}
/* PowerPC 440 TLB management */
void helper_440_tlbwe(CPUPPCState *env, uint32_t word, target_ulong entry,
target_ulong value)
{
ppcemb_tlb_t *tlb;
target_ulong EPN, RPN, size;
int do_flush_tlbs;
qemu_log_mask(CPU_LOG_MMU, "%s word %d entry %d value " TARGET_FMT_lx "\n",
__func__, word, (int)entry, value);
do_flush_tlbs = 0;
entry &= 0x3F;
tlb = &env->tlb.tlbe[entry];
/* Invalidate previous TLB (if it's valid) */
if ((tlb->prot & PAGE_VALID) && mmubooke_pid_match(env, tlb)) {
qemu_log_mask(CPU_LOG_MMU, "%s: invalidate old TLB %d start "
TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__,
(int)entry, tlb->EPN, tlb->EPN + tlb->size);
ppcemb_tlb_flush(env_cpu(env), tlb);
}
switch (word) {
default:
/* Just here to please gcc */
case 0:
EPN = value & 0xFFFFFC00;
if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN) {
do_flush_tlbs = 1;
}
tlb->EPN = EPN;
size = booke_tlb_to_page_size((value >> 4) & 0xF);
if ((tlb->prot & PAGE_VALID) && tlb->size < size) {
do_flush_tlbs = 1;
}
tlb->size = size;
tlb->EPN = value & 0xFFFFFC00;
tlb->size = booke_tlb_to_page_size((value >> 4) & 0xF);
tlb->attr &= ~0x1;
tlb->attr |= (value >> 8) & 1;
if (value & 0x200) {
tlb->prot |= PAGE_VALID;
} else {
if (tlb->prot & PAGE_VALID) {
tlb->prot &= ~PAGE_VALID;
do_flush_tlbs = 1;
}
tlb->prot &= ~PAGE_VALID;
}
tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF;
if (do_flush_tlbs) {
tlb_flush(env_cpu(env));
}
break;
case 1:
RPN = value & 0xFFFFFC0F;
if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN) {
tlb_flush(env_cpu(env));
}
tlb->RPN = RPN;
tlb->RPN = value & 0xFFFFFC0F;
break;
case 2:
tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00);

View File

@ -103,7 +103,11 @@ const MonitorDef monitor_defs[] = {
{ "xer", 0, &monitor_get_xer },
{ "msr", offsetof(CPUPPCState, msr) },
{ "tbu", 0, &monitor_get_tbu, },
#if defined(TARGET_PPC64)
{ "tb", 0, &monitor_get_tbl, },
#else
{ "tbl", 0, &monitor_get_tbl, },
#endif
{ NULL },
};

View File

@ -28,18 +28,3 @@ void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
void destroy_ppc_opcodes(PowerPCCPU *cpu)
{
}
target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong shift)
{
g_assert_not_reached();
}
target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong flags,
target_ulong shift)
{
g_assert_not_reached();
}

View File

@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "hw/ppc/ppc.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "qemu/log.h"
@ -59,19 +60,55 @@ target_ulong helper_load_purr(CPUPPCState *env)
void helper_store_purr(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_purr(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_purr(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_purr(cenv, val);
}
}
#endif
#if !defined(CONFIG_USER_ONLY)
void helper_store_tbl(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_tbl(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_tbl(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_tbl(cenv, val);
}
}
void helper_store_tbu(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_tbu(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_tbu(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_tbu(cenv, val);
}
}
void helper_store_atbl(CPUPPCState *env, target_ulong val)
@ -101,17 +138,53 @@ target_ulong helper_load_hdecr(CPUPPCState *env)
void helper_store_hdecr(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_hdecr(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_hdecr(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_hdecr(cenv, val);
}
}
void helper_store_vtb(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_vtb(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_vtb(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_vtb(cenv, val);
}
}
void helper_store_tbu40(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_tbu40(env, val);
CPUState *cs = env_cpu(env);
CPUState *ccs;
uint32_t nr_threads = cs->nr_threads;
if (nr_threads == 1 || !(env->flags & POWERPC_FLAG_SMT_1LPAR)) {
cpu_ppc_store_tbu40(env, val);
return;
}
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cpu_ppc_store_tbu40(cenv, val);
}
}
target_ulong helper_load_40x_pit(CPUPPCState *env)
@ -145,15 +218,233 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val)
}
#if defined(TARGET_PPC64)
/* POWER processor Timebase Facility */
/*
* POWER processor Timebase Facility
*/
/*
* The TBST is the timebase state machine, which is a per-core machine that
* is used to synchronize the core TB with the ChipTOD. States 3,4,5 are
* not used in POWER8/9/10.
*
* The state machine gets driven by writes to TFMR SPR from the core, and
* by signals from the ChipTOD. The state machine table for common
* transitions is as follows (according to hardware specs, not necessarily
* this implementation):
*
* | Cur | Event | New |
* +----------------+----------------------------------+-----+
* | 0 RESET | TFMR |= LOAD_TOD_MOD | 1 |
* | 1 SEND_TOD_MOD | "immediate transition" | 2 |
* | 2 NOT_SET | mttbu/mttbu40/mttbl | 2 |
* | 2 NOT_SET | TFMR |= MOVE_CHIP_TOD_TO_TB | 6 |
* | 6 SYNC_WAIT | "sync pulse from ChipTOD" | 7 |
* | 7 GET_TOD | ChipTOD xscom MOVE_TOD_TO_TB_REG | 8 |
* | 8 TB_RUNNING | mttbu/mttbu40 | 8 |
* | 8 TB_RUNNING | TFMR |= LOAD_TOD_MOD | 1 |
* | 8 TB_RUNNING | mttbl | 9 |
* | 9 TB_ERROR | TFMR |= CLEAR_TB_ERRORS | 0 |
*
* - LOAD_TOD_MOD will also move states 2,6 to state 1, omitted from table
* because it's not a typical init flow.
*
* - The ERROR state can be entered from most/all other states on invalid
* states (e.g., if some TFMR control bit is set from a state where it's
* not listed to cause a transition away from), omitted to avoid clutter.
*
* Note: mttbl causes a timebase error because this inevitably causes
* ticks to be lost and TB to become unsynchronized, whereas TB can be
* adjusted using mttbu* without losing ticks. mttbl behaviour is not
* modelled.
*
* Note: the TB state machine does not actually cause any real TB adjustment!
* TB starts out synchronized across all vCPUs (hardware threads) in
* QMEU, so for now the purpose of the TBST and ChipTOD model is simply
* to step through firmware initialisation sequences.
*/
static unsigned int tfmr_get_tb_state(uint64_t tfmr)
{
return (tfmr & TFMR_TBST_ENCODED) >> (63 - 31);
}
static uint64_t tfmr_new_tb_state(uint64_t tfmr, unsigned int tbst)
{
tfmr &= ~TFMR_TBST_LAST;
tfmr |= (tfmr & TFMR_TBST_ENCODED) >> 4; /* move state to last state */
tfmr &= ~TFMR_TBST_ENCODED;
tfmr |= (uint64_t)tbst << (63 - 31); /* move new state to state */
if (tbst == TBST_TB_RUNNING) {
tfmr |= TFMR_TB_VALID;
} else {
tfmr &= ~TFMR_TB_VALID;
}
return tfmr;
}
static void write_tfmr(CPUPPCState *env, target_ulong val)
{
CPUState *cs = env_cpu(env);
if (cs->nr_threads == 1) {
env->spr[SPR_TFMR] = val;
} else {
CPUState *ccs;
THREAD_SIBLING_FOREACH(cs, ccs) {
CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
cenv->spr[SPR_TFMR] = val;
}
}
}
static void tb_state_machine_step(CPUPPCState *env)
{
uint64_t tfmr = env->spr[SPR_TFMR];
unsigned int tbst = tfmr_get_tb_state(tfmr);
if (!(tfmr & TFMR_TB_ECLIPZ) || tbst == TBST_TB_ERROR) {
return;
}
if (env->pnv_tod_tbst.tb_sync_pulse_timer) {
env->pnv_tod_tbst.tb_sync_pulse_timer--;
} else {
tfmr |= TFMR_TB_SYNC_OCCURED;
write_tfmr(env, tfmr);
}
if (env->pnv_tod_tbst.tb_state_timer) {
env->pnv_tod_tbst.tb_state_timer--;
return;
}
if (tfmr & TFMR_LOAD_TOD_MOD) {
tfmr &= ~TFMR_LOAD_TOD_MOD;
if (tbst == TBST_GET_TOD) {
tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
} else {
tfmr = tfmr_new_tb_state(tfmr, TBST_SEND_TOD_MOD);
/* State seems to transition immediately */
tfmr = tfmr_new_tb_state(tfmr, TBST_NOT_SET);
}
} else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
if (tbst == TBST_SYNC_WAIT) {
tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD);
env->pnv_tod_tbst.tb_state_timer = 3;
} else if (tbst == TBST_GET_TOD) {
if (env->pnv_tod_tbst.tod_sent_to_tb) {
tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING);
tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
env->pnv_tod_tbst.tb_ready_for_tod = 0;
env->pnv_tod_tbst.tod_sent_to_tb = 0;
}
} else {
qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
"state machine in invalid state 0x%x\n", tbst);
tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
env->pnv_tod_tbst.tb_ready_for_tod = 0;
}
}
write_tfmr(env, tfmr);
}
target_ulong helper_load_tfmr(CPUPPCState *env)
{
return env->spr[SPR_TFMR];
tb_state_machine_step(env);
return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ;
}
void helper_store_tfmr(CPUPPCState *env, target_ulong val)
{
env->spr[SPR_TFMR] = val;
uint64_t tfmr = env->spr[SPR_TFMR];
uint64_t clear_on_write;
unsigned int tbst = tfmr_get_tb_state(tfmr);
if (!(val & TFMR_TB_ECLIPZ)) {
qemu_log_mask(LOG_UNIMP, "TFMR non-ECLIPZ mode not implemented\n");
tfmr &= ~TFMR_TBST_ENCODED;
tfmr &= ~TFMR_TBST_LAST;
goto out;
}
/* Update control bits */
tfmr = (tfmr & ~TFMR_CONTROL_MASK) | (val & TFMR_CONTROL_MASK);
/* Several bits are clear-on-write, only one is implemented so far */
clear_on_write = val & TFMR_FIRMWARE_CONTROL_ERROR;
tfmr &= ~clear_on_write;
/*
* mtspr always clears this. The sync pulse timer makes it come back
* after the second mfspr.
*/
tfmr &= ~TFMR_TB_SYNC_OCCURED;
env->pnv_tod_tbst.tb_sync_pulse_timer = 1;
if (ppc_cpu_tir(env_archcpu(env)) != 0 &&
(val & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB))) {
qemu_log_mask(LOG_UNIMP, "TFMR timebase state machine can only be "
"driven by thread 0\n");
goto out;
}
if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) ==
(TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) {
qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: LOAD_TOD_MOD and "
"MOVE_CHIP_TOD_TO_TB both set\n");
tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
env->pnv_tod_tbst.tb_ready_for_tod = 0;
goto out;
}
if (tfmr & TFMR_CLEAR_TB_ERRORS) {
/*
* Workbook says TFMR_CLEAR_TB_ERRORS should be written twice.
* This is not simulated/required here.
*/
tfmr = tfmr_new_tb_state(tfmr, TBST_RESET);
tfmr &= ~TFMR_CLEAR_TB_ERRORS;
tfmr &= ~TFMR_LOAD_TOD_MOD;
tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */
env->pnv_tod_tbst.tb_ready_for_tod = 0;
env->pnv_tod_tbst.tod_sent_to_tb = 0;
goto out;
}
if (tbst == TBST_TB_ERROR) {
qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: mtspr TFMR in TB_ERROR"
" state\n");
tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
return;
}
if (tfmr & TFMR_LOAD_TOD_MOD) {
/* Wait for an arbitrary 3 mfspr until the next state transition. */
env->pnv_tod_tbst.tb_state_timer = 3;
} else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
if (tbst == TBST_NOT_SET) {
tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT);
env->pnv_tod_tbst.tb_ready_for_tod = 1;
env->pnv_tod_tbst.tb_state_timer = 3; /* arbitrary */
} else {
qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
"not in TB not set state 0x%x\n",
tbst);
tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
env->pnv_tod_tbst.tb_ready_for_tod = 0;
}
}
out:
write_tfmr(env, tfmr);
}
#endif

View File

@ -247,13 +247,24 @@ static inline bool gen_serialize(DisasContext *ctx)
return true;
}
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
#if !defined(CONFIG_USER_ONLY)
#if defined(TARGET_PPC64)
static inline bool gen_serialize_core(DisasContext *ctx)
{
if (ctx->flags & POWERPC_FLAG_SMT) {
return gen_serialize(ctx);
}
return true;
}
#endif
static inline bool gen_serialize_core_lpar(DisasContext *ctx)
{
#if defined(TARGET_PPC64)
if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) {
return gen_serialize(ctx);
}
#endif
return true;
}
#endif
@ -667,12 +678,20 @@ void spr_read_atbu(DisasContext *ctx, int gprn, int sprn)
#if !defined(CONFIG_USER_ONLY)
void spr_write_tbl(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_tbl(tcg_env, cpu_gpr[gprn]);
}
void spr_write_tbu(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_tbu(tcg_env, cpu_gpr[gprn]);
}
@ -696,6 +715,9 @@ void spr_read_purr(DisasContext *ctx, int gprn, int sprn)
void spr_write_purr(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_purr(tcg_env, cpu_gpr[gprn]);
}
@ -709,6 +731,9 @@ void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn)
void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_hdecr(tcg_env, cpu_gpr[gprn]);
}
@ -721,12 +746,18 @@ void spr_read_vtb(DisasContext *ctx, int gprn, int sprn)
void spr_write_vtb(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_vtb(tcg_env, cpu_gpr[gprn]);
}
void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core_lpar(ctx)) {
return;
}
translator_io_start(&ctx->base);
gen_helper_store_tbu40(tcg_env, cpu_gpr[gprn]);
}
@ -1220,11 +1251,18 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn)
void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn)
{
/* Reading TFMR can cause it to be updated, so serialize threads here too */
if (!gen_serialize_core(ctx)) {
return;
}
gen_helper_load_tfmr(cpu_gpr[gprn], tcg_env);
}
void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn)
{
if (!gen_serialize_core(ctx)) {
return;
}
gen_helper_store_tfmr(tcg_env, cpu_gpr[gprn]);
}

View File

@ -2268,7 +2268,7 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ,
static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired)
{
if (paired || a->rt >= 32) {
if (paired || a->rt < 32) {
REQUIRE_VSX(ctx);
} else {
REQUIRE_VECTOR(ctx);

View File

@ -93,18 +93,25 @@ class BootLinuxPPC64(LinuxTest):
timeout = 360
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
def test_pseries_tcg(self):
"""
:avocado: tags=machine:pseries
:avocado: tags=accel:tcg
:avocado: tags=flaky
"""
self.require_accelerator("tcg")
self.vm.add_args("-accel", "tcg")
self.launch_and_wait(set_up_ssh_connection=False)
def test_pseries_kvm(self):
"""
:avocado: tags=machine:pseries
:avocado: tags=accel:kvm
"""
self.require_accelerator("kvm")
self.vm.add_args("-accel", "kvm")
self.vm.add_args("-machine", "cap-ccf-assist=off")
self.launch_and_wait(set_up_ssh_connection=False)
class BootLinuxS390X(LinuxTest):
"""
@ -113,13 +120,11 @@ class BootLinuxS390X(LinuxTest):
timeout = 240
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
def test_s390_ccw_virtio_tcg(self):
"""
:avocado: tags=machine:s390-ccw-virtio
:avocado: tags=accel:tcg
:avocado: tags=flaky
"""
self.require_accelerator("tcg")
self.vm.add_args("-accel", "tcg")

View File

@ -1368,7 +1368,8 @@ class BootLinuxConsole(LinuxKernelTest):
self.wait_for_console_pattern("CPU: " + proc + " generation processor")
self.wait_for_console_pattern("zImage starting: loaded")
self.wait_for_console_pattern("Run /init as init process")
self.wait_for_console_pattern("Creating 1 MTD partitions")
# Device detection output driven by udev probing is sometimes cut off
# from console output, suspect S14silence-console init script.
def test_ppc_powernv8(self):
"""
@ -1386,6 +1387,14 @@ class BootLinuxConsole(LinuxKernelTest):
"""
self.do_test_ppc64_powernv('P9')
def test_ppc_powernv10(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:powernv10
:avocado: tags=accel:tcg
"""
self.do_test_ppc64_powernv('P10')
def test_ppc_g3beige(self):
"""
:avocado: tags=arch:ppc

View File

@ -123,7 +123,6 @@ class PPC64(MigrationTest):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:pseries
:avocado: tags=cpu:power9_v2.0
"""
def test_migration_with_tcp_localhost(self):

View File

@ -0,0 +1,202 @@
# Tests that specifically try to exercise hypervisor features of the
# target machines. powernv supports the Power hypervisor ISA, and
# pseries supports the nested-HV hypervisor spec.
#
# Copyright (c) 2023 IBM Corporation
#
# This work is licensed under the terms of the GNU GPL, version 2 or
# later. See the COPYING file in the top-level directory.
from avocado import skipIf, skipUnless
from avocado.utils import archive
from avocado_qemu import QemuSystemTest
from avocado_qemu import wait_for_console_pattern, exec_command
import os
import time
import subprocess
deps = ["xorriso"] # dependent tools needed in the test setup/box.
def which(tool):
""" looks up the full path for @tool, returns None if not found
or if @tool does not have executable permissions.
"""
paths=os.getenv('PATH')
for p in paths.split(os.path.pathsep):
p = os.path.join(p, tool)
if os.path.exists(p) and os.access(p, os.X_OK):
return p
return None
def missing_deps():
""" returns True if any of the test dependent tools are absent.
"""
for dep in deps:
if which(dep) is None:
return True
return False
# Alpine is a light weight distro that supports QEMU. These tests boot
# that on the machine then run a QEMU guest inside it in KVM mode,
# that runs the same Alpine distro image.
# QEMU packages are downloaded and installed on each test. That's not a
# large download, but it may be more polite to create qcow2 image with
# QEMU already installed and use that.
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck due to console handling problem')
@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited')
@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps))
class HypervisorTest(QemuSystemTest):
timeout = 1000
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
panic_message = 'Kernel panic - not syncing'
good_message = 'VFS: Cannot open root device'
def extract_from_iso(self, iso, path):
"""
Extracts a file from an iso file into the test workdir
:param iso: path to the iso file
:param path: path within the iso file of the file to be extracted
:returns: path of the extracted file
"""
filename = os.path.basename(path)
cwd = os.getcwd()
os.chdir(self.workdir)
with open(filename, "w") as outfile:
cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename)
subprocess.run(cmd.split(),
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
os.chdir(cwd)
# Return complete path to extracted file. Because callers to
# extract_from_iso() specify 'path' with a leading slash, it is
# necessary to use os.path.relpath() as otherwise os.path.join()
# interprets it as an absolute path and drops the self.workdir part.
return os.path.normpath(os.path.join(self.workdir, filename))
def setUp(self):
super().setUp()
iso_url = ('https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso')
# Alpine use sha256 so I recalculated this myself
iso_sha256 = 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff'
iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha256,
algorithm = "sha256")
self.iso_path = iso_path
self.vmlinuz = self.extract_from_iso(iso_path, '/boot/vmlinuz-lts')
self.initramfs = self.extract_from_iso(iso_path, '/boot/initramfs-lts')
def do_start_alpine(self):
self.vm.set_console()
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE
self.vm.add_args("-kernel", self.vmlinuz)
self.vm.add_args("-initrd", self.initramfs)
self.vm.add_args("-smp", "4", "-m", "2g")
self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none,id=drive0")
self.vm.launch()
wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
exec_command(self, 'root')
wait_for_console_pattern(self, 'localhost login:')
wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
exec_command(self, 'setup-alpine -qe')
wait_for_console_pattern(self, 'Updating repository indexes... done.')
def do_stop_alpine(self):
exec_command(self, 'poweroff')
wait_for_console_pattern(self, 'alpine:~#')
self.vm.wait()
def do_setup_kvm(self):
exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories')
wait_for_console_pattern(self, 'alpine:~#')
exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories')
wait_for_console_pattern(self, 'alpine:~#')
exec_command(self, 'apk update')
wait_for_console_pattern(self, 'alpine:~#')
exec_command(self, 'apk add qemu-system-ppc64')
wait_for_console_pattern(self, 'alpine:~#')
exec_command(self, 'modprobe kvm-hv')
wait_for_console_pattern(self, 'alpine:~#')
# This uses the host's block device as the source file for guest block
# device for install media. This is a bit hacky but allows reuse of the
# iso without having a passthrough filesystem configured.
def do_test_kvm(self, hpt=False):
if hpt:
append = 'disable_radix'
else:
append = ''
exec_command(self, 'qemu-system-ppc64 -nographic -smp 2 -m 1g '
'-machine pseries,x-vof=on,accel=kvm '
'-machine cap-cfpc=broken,cap-sbbc=broken,'
'cap-ibs=broken,cap-ccf-assist=off '
'-drive file=/dev/nvme0n1,format=raw,readonly=on '
'-initrd /media/nvme0n1/boot/initramfs-lts '
'-kernel /media/nvme0n1/boot/vmlinuz-lts '
'-append \'usbcore.nousb ' + append + '\'')
# Alpine 3.18 kernel seems to crash in XHCI USB driver.
wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18')
exec_command(self, 'root')
wait_for_console_pattern(self, 'localhost login:')
wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.')
exec_command(self, 'poweroff >& /dev/null')
wait_for_console_pattern(self, 'localhost:~#')
wait_for_console_pattern(self, 'reboot: Power down')
time.sleep(1)
exec_command(self, '')
wait_for_console_pattern(self, 'alpine:~#')
def test_hv_pseries(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:pseries
:avocado: tags=accel:tcg
"""
self.require_accelerator("tcg")
self.vm.add_args("-accel", "tcg,thread=multi")
self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0')
self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on")
self.do_start_alpine()
self.do_setup_kvm()
self.do_test_kvm()
self.do_stop_alpine()
def test_hv_pseries_kvm(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:pseries
:avocado: tags=accel:kvm
"""
self.require_accelerator("kvm")
self.vm.add_args("-accel", "kvm")
self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0')
self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on,cap-ccf-assist=off")
self.do_start_alpine()
self.do_setup_kvm()
self.do_test_kvm()
self.do_stop_alpine()
def test_hv_powernv(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:powernv
:avocado: tags=accel:tcg
"""
self.require_accelerator("tcg")
self.vm.add_args("-accel", "tcg,thread=multi")
self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0',
'-device', 'e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0',
'-netdev', 'user,id=net0,hostfwd=::20022-:22,hostname=alpine')
self.do_start_alpine()
self.do_setup_kvm()
self.do_test_kvm()
self.do_test_kvm(True)
self.do_stop_alpine()

View File

@ -12,11 +12,11 @@ from avocado_qemu import wait_for_console_pattern
class powernvMachine(QemuSystemTest):
timeout = 90
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
panic_message = 'Kernel panic - not syncing'
good_message = 'VFS: Cannot open root device'
def do_test_linux_boot(self):
def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE):
self.require_accelerator("tcg")
kernel_url = ('https://archives.fedoraproject.org/pub/archive'
'/fedora-secondary/releases/29/Everything/ppc64le/os'
@ -25,9 +25,8 @@ class powernvMachine(QemuSystemTest):
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
self.vm.set_console()
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0'
self.vm.add_args('-kernel', kernel_path,
'-append', kernel_command_line)
'-append', command_line)
self.vm.launch()
def test_linux_boot(self):
@ -54,6 +53,22 @@ class powernvMachine(QemuSystemTest):
wait_for_console_pattern(self, console_pattern, self.panic_message)
wait_for_console_pattern(self, self.good_message, self.panic_message)
def test_linux_smp_hpt_boot(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:powernv
:avocado: tags=accel:tcg
"""
self.vm.add_args('-smp', '4')
self.do_test_linux_boot(self.KERNEL_COMMON_COMMAND_LINE +
'disable_radix')
console_pattern = 'smp: Brought up 1 node, 4 CPUs'
wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu',
self.panic_message)
wait_for_console_pattern(self, console_pattern, self.panic_message)
wait_for_console_pattern(self, self.good_message, self.panic_message)
def test_linux_smt_boot(self):
"""
:avocado: tags=arch:ppc64

View File

@ -12,11 +12,11 @@ from avocado_qemu import wait_for_console_pattern
class pseriesMachine(QemuSystemTest):
timeout = 90
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 '
panic_message = 'Kernel panic - not syncing'
good_message = 'VFS: Cannot open root device'
def do_test_ppc64_linux_boot(self):
def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE):
kernel_url = ('https://archives.fedoraproject.org/pub/archive'
'/fedora-secondary/releases/29/Everything/ppc64le/os'
'/ppc/ppc64/vmlinuz')
@ -24,7 +24,6 @@ class pseriesMachine(QemuSystemTest):
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
self.vm.set_console()
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0'
self.vm.add_args('-kernel', kernel_path,
'-append', kernel_command_line)
self.vm.launch()
@ -62,6 +61,21 @@ class pseriesMachine(QemuSystemTest):
wait_for_console_pattern(self, console_pattern, self.panic_message)
wait_for_console_pattern(self, self.good_message, self.panic_message)
def test_ppc64_linux_hpt_smp_boot(self):
"""
:avocado: tags=arch:ppc64
:avocado: tags=machine:pseries
"""
self.vm.add_args('-smp', '4')
self.do_test_ppc64_linux_boot(self.KERNEL_COMMON_COMMAND_LINE +
'disable_radix')
console_pattern = 'smp: Brought up 1 node, 4 CPUs'
wait_for_console_pattern(self, 'hash-mmu: Initializing hash mmu',
self.panic_message)
wait_for_console_pattern(self, console_pattern, self.panic_message)
wait_for_console_pattern(self, self.good_message, self.panic_message)
def test_ppc64_linux_smt_boot(self):
"""
:avocado: tags=arch:ppc64

View File

@ -167,6 +167,7 @@ qtests_ppc64 = \
qtests_ppc + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \
(slirp.found() ? ['pxe-test'] : []) + \
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \

View File

@ -60,7 +60,7 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(value, ==, 0x55);
value = i2c_get8(i2cdev, PCA9552_INPUT0);
g_assert_cmphex(value, ==, 0x0);
g_assert_cmphex(value, ==, 0xFF);
pca9552_init(i2cdev);
@ -68,13 +68,13 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
g_assert_cmphex(value, ==, 0x54);
value = i2c_get8(i2cdev, PCA9552_INPUT0);
g_assert_cmphex(value, ==, 0x01);
g_assert_cmphex(value, ==, 0xFE);
value = i2c_get8(i2cdev, PCA9552_LS3);
g_assert_cmphex(value, ==, 0x54);
value = i2c_get8(i2cdev, PCA9552_INPUT1);
g_assert_cmphex(value, ==, 0x10);
g_assert_cmphex(value, ==, 0xEF);
}
static void pca9552_register_nodes(void)

View File

@ -0,0 +1,491 @@
/*
* QTest testcase for PowerNV 10 Host I2C Communications
*
* Copyright (c) 2023, IBM Corporation.
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "libqtest.h"
#include "hw/misc/pca9554_regs.h"
#include "hw/misc/pca9552_regs.h"
#include "pnv-xscom.h"
#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit))
#define PPC_BIT32(bit) (0x80000000 >> (bit))
#define PPC_BIT8(bit) (0x80 >> (bit))
#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
PPC_BIT32(bs))
#define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1)
#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m))
#define SETFIELD(m, v, val) \
(((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
#define PNV10_XSCOM_I2CM_BASE 0xa0000
#define PNV10_XSCOM_I2CM_SIZE 0x1000
#include "hw/i2c/pnv_i2c_regs.h"
typedef struct {
QTestState *qts;
const PnvChip *chip;
int engine;
} PnvI2cCtlr;
typedef struct {
PnvI2cCtlr *ctlr;
int port;
uint8_t addr;
} PnvI2cDev;
static uint64_t pnv_i2c_xscom_addr(PnvI2cCtlr *ctlr, uint32_t reg)
{
return pnv_xscom_addr(ctlr->chip, PNV10_XSCOM_I2CM_BASE +
(PNV10_XSCOM_I2CM_SIZE * ctlr->engine) + reg);
}
static uint64_t pnv_i2c_xscom_read(PnvI2cCtlr *ctlr, uint32_t reg)
{
return qtest_readq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg));
}
static void pnv_i2c_xscom_write(PnvI2cCtlr *ctlr, uint32_t reg, uint64_t val)
{
qtest_writeq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg), val);
}
/* Write len bytes from buf to i2c device with given addr and port */
static void pnv_i2c_send(PnvI2cDev *dev, const uint8_t *buf, uint16_t len)
{
int byte_num;
uint64_t reg64;
/* select requested port */
reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be);
reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port);
pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64);
/* check status for cmd complete and bus idle */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
I2C_STAT_CMD_COMP);
/* Send start, with stop, with address and len bytes of data */
reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | I2C_CMD_WITH_STOP;
reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr);
reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len);
pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64);
/* check status for errors */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0);
/* write data bytes to fifo register */
for (byte_num = 0; byte_num < len; byte_num++) {
reg64 = SETFIELD(I2C_FIFO, 0ull, buf[byte_num]);
pnv_i2c_xscom_write(dev->ctlr, I2C_FIFO_REG, reg64);
}
/* check status for cmd complete and bus idle */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
I2C_STAT_CMD_COMP);
}
/* Recieve len bytes into buf from i2c device with given addr and port */
static void pnv_i2c_recv(PnvI2cDev *dev, uint8_t *buf, uint16_t len)
{
int byte_num;
uint64_t reg64;
/* select requested port */
reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be);
reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port);
pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64);
/* check status for cmd complete and bus idle */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
I2C_STAT_CMD_COMP);
/* Send start, with stop, with address and len bytes of data */
reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR |
I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE;
reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr);
reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len);
pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64);
/* check status for errors */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0);
/* Read data bytes from fifo register */
for (byte_num = 0; byte_num < len; byte_num++) {
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_FIFO_REG);
buf[byte_num] = GETFIELD(I2C_FIFO, reg64);
}
/* check status for cmd complete and bus idle */
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
I2C_STAT_CMD_COMP);
}
static void pnv_i2c_pca9554_default_cfg(PnvI2cDev *dev)
{
uint8_t buf[2];
/* input register bits are not inverted */
buf[0] = PCA9554_POLARITY;
buf[1] = 0;
pnv_i2c_send(dev, buf, 2);
/* All pins are inputs */
buf[0] = PCA9554_CONFIG;
buf[1] = 0xff;
pnv_i2c_send(dev, buf, 2);
/* Output value for when pins are outputs */
buf[0] = PCA9554_OUTPUT;
buf[1] = 0xff;
pnv_i2c_send(dev, buf, 2);
}
static void pnv_i2c_pca9554_set_pin(PnvI2cDev *dev, int pin, bool high)
{
uint8_t send_buf[2];
uint8_t recv_buf[2];
uint8_t mask = 0x1 << pin;
uint8_t new_value = ((high) ? 1 : 0) << pin;
/* read current OUTPUT value */
send_buf[0] = PCA9554_OUTPUT;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
/* write new OUTPUT value */
send_buf[1] = (recv_buf[0] & ~mask) | new_value;
pnv_i2c_send(dev, send_buf, 2);
/* Update config bit for output */
send_buf[0] = PCA9554_CONFIG;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
send_buf[1] = recv_buf[0] & ~mask;
pnv_i2c_send(dev, send_buf, 2);
}
static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev)
{
uint8_t send_buf[1];
uint8_t recv_buf[1];
uint8_t inputs;
send_buf[0] = PCA9554_INPUT;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
inputs = recv_buf[0];
return inputs;
}
static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev)
{
uint8_t recv_buf[1];
uint8_t send_buf[2];
send_buf[0] = PCA9554_POLARITY;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
send_buf[1] = recv_buf[0] ^ 0xff;
pnv_i2c_send(dev, send_buf, 2);
}
static void pnv_i2c_pca9554_default_inputs(PnvI2cDev *dev)
{
uint8_t pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0xff);
}
/* Check that setting pin values and polarity changes inputs as expected */
static void pnv_i2c_pca554_set_pins(PnvI2cDev *dev)
{
uint8_t pin_values;
pnv_i2c_pca9554_set_pin(dev, 0, 0);
pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0xfe);
pnv_i2c_pca9554_flip_polarity(dev);
pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0x01);
pnv_i2c_pca9554_set_pin(dev, 2, 0);
pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0x05);
pnv_i2c_pca9554_flip_polarity(dev);
pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0xfa);
pnv_i2c_pca9554_default_cfg(dev);
pin_values = pnv_i2c_pca9554_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0xff);
}
static void pnv_i2c_pca9552_default_cfg(PnvI2cDev *dev)
{
uint8_t buf[2];
/* configure pwm/psc regs */
buf[0] = PCA9552_PSC0;
buf[1] = 0xff;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_PWM0;
buf[1] = 0x80;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_PSC1;
buf[1] = 0xff;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_PWM1;
buf[1] = 0x80;
pnv_i2c_send(dev, buf, 2);
/* configure all pins as inputs */
buf[0] = PCA9552_LS0;
buf[1] = 0x55;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_LS1;
buf[1] = 0x55;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_LS2;
buf[1] = 0x55;
pnv_i2c_send(dev, buf, 2);
buf[0] = PCA9552_LS3;
buf[1] = 0x55;
pnv_i2c_send(dev, buf, 2);
}
static void pnv_i2c_pca9552_set_pin(PnvI2cDev *dev, int pin, bool high)
{
uint8_t send_buf[2];
uint8_t recv_buf[2];
uint8_t reg = PCA9552_LS0 + (pin / 4);
uint8_t shift = (pin % 4) * 2;
uint8_t mask = ~(0x3 << shift);
uint8_t new_value = ((high) ? 1 : 0) << shift;
/* read current LSx value */
send_buf[0] = reg;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
/* write new value to LSx */
send_buf[1] = (recv_buf[0] & mask) | new_value;
pnv_i2c_send(dev, send_buf, 2);
}
static uint16_t pnv_i2c_pca9552_read_pins(PnvI2cDev *dev)
{
uint8_t send_buf[2];
uint8_t recv_buf[2];
uint16_t inputs;
send_buf[0] = PCA9552_INPUT0;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
inputs = recv_buf[0];
send_buf[0] = PCA9552_INPUT1;
pnv_i2c_send(dev, send_buf, 1);
pnv_i2c_recv(dev, recv_buf, 1);
inputs |= recv_buf[0] << 8;
return inputs;
}
static void pnv_i2c_pca9552_default_inputs(PnvI2cDev *dev)
{
uint16_t pin_values = pnv_i2c_pca9552_read_pins(dev);
g_assert_cmphex(pin_values, ==, 0xffff);
}
/*
* Set pins 0-4 one at a time and verify that pins 5-9 are
* set to the same value
*/
static void pnv_i2c_pca552_set_pins(PnvI2cDev *dev)
{
uint16_t pin_values;
/* set pin 0 low */
pnv_i2c_pca9552_set_pin(dev, 0, 0);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* pins 0 and 5 should be low */
g_assert_cmphex(pin_values, ==, 0xffde);
/* set pin 1 low */
pnv_i2c_pca9552_set_pin(dev, 1, 0);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* pins 0, 1, 5 and 6 should be low */
g_assert_cmphex(pin_values, ==, 0xff9c);
/* set pin 2 low */
pnv_i2c_pca9552_set_pin(dev, 2, 0);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* pins 0, 1, 2, 5, 6 and 7 should be low */
g_assert_cmphex(pin_values, ==, 0xff18);
/* set pin 3 low */
pnv_i2c_pca9552_set_pin(dev, 3, 0);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* pins 0, 1, 2, 3, 5, 6, 7 and 8 should be low */
g_assert_cmphex(pin_values, ==, 0xfe10);
/* set pin 4 low */
pnv_i2c_pca9552_set_pin(dev, 4, 0);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* pins 0, 1, 2, 3, 5, 6, 7, 8 and 9 should be low */
g_assert_cmphex(pin_values, ==, 0xfc00);
/* reset all pins to the high state */
pnv_i2c_pca9552_default_cfg(dev);
pin_values = pnv_i2c_pca9552_read_pins(dev);
/* verify all pins went back to the high state */
g_assert_cmphex(pin_values, ==, 0xffff);
}
static void reset_engine(PnvI2cCtlr *ctlr)
{
pnv_i2c_xscom_write(ctlr, I2C_RESET_I2C_REG, 0);
}
static void check_i2cm_por_regs(QTestState *qts, const PnvChip *chip)
{
int engine;
for (engine = 0; engine < chip->num_i2c; engine++) {
PnvI2cCtlr ctlr;
ctlr.qts = qts;
ctlr.chip = chip;
ctlr.engine = engine;
/* Check version in Extended Status Register */
uint64_t value = pnv_i2c_xscom_read(&ctlr, I2C_EXTD_STAT_REG);
g_assert_cmphex(value & I2C_EXTD_STAT_I2C_VERSION, ==, 0x1700000000);
/* Check for command complete and bus idle in Status Register */
value = pnv_i2c_xscom_read(&ctlr, I2C_STAT_REG);
g_assert_cmphex(value & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP),
==,
I2C_STAT_CMD_COMP);
}
}
static void reset_all(QTestState *qts, const PnvChip *chip)
{
int engine;
for (engine = 0; engine < chip->num_i2c; engine++) {
PnvI2cCtlr ctlr;
ctlr.qts = qts;
ctlr.chip = chip;
ctlr.engine = engine;
reset_engine(&ctlr);
pnv_i2c_xscom_write(&ctlr, I2C_MODE_REG, 0x02be040000000000);
}
}
static void test_host_i2c(const void *data)
{
const PnvChip *chip = data;
QTestState *qts;
const char *machine = "powernv8";
PnvI2cCtlr ctlr;
PnvI2cDev pca9552;
PnvI2cDev pca9554;
if (chip->chip_type == PNV_CHIP_POWER9) {
machine = "powernv9";
} else if (chip->chip_type == PNV_CHIP_POWER10) {
machine = "powernv10-rainier";
}
qts = qtest_initf("-M %s -smp %d,cores=1,threads=%d -nographic "
"-nodefaults -serial mon:stdio -S "
"-d guest_errors",
machine, SMT, SMT);
/* Check the I2C master status registers after POR */
check_i2cm_por_regs(qts, chip);
/* Now do a forced "immediate" reset on all engines */
reset_all(qts, chip);
/* Check that the status values are still good */
check_i2cm_por_regs(qts, chip);
/* P9 doesn't have any i2c devices attached at this time */
if (chip->chip_type != PNV_CHIP_POWER10) {
qtest_quit(qts);
return;
}
/* Initialize for a P10 pca9552 hotplug device */
ctlr.qts = qts;
ctlr.chip = chip;
ctlr.engine = 2;
pca9552.ctlr = &ctlr;
pca9552.port = 1;
pca9552.addr = 0x63;
/* Set all pca9552 pins as inputs */
pnv_i2c_pca9552_default_cfg(&pca9552);
/* Check that all pins of the pca9552 are high */
pnv_i2c_pca9552_default_inputs(&pca9552);
/* perform individual pin tests */
pnv_i2c_pca552_set_pins(&pca9552);
/* Initialize for a P10 pca9554 CableCard Presence detection device */
pca9554.ctlr = &ctlr;
pca9554.port = 1;
pca9554.addr = 0x25;
/* Set all pca9554 pins as inputs */
pnv_i2c_pca9554_default_cfg(&pca9554);
/* Check that all pins of the pca9554 are high */
pnv_i2c_pca9554_default_inputs(&pca9554);
/* perform individual pin tests */
pnv_i2c_pca554_set_pins(&pca9554);
qtest_quit(qts);
}
static void add_test(const char *name, void (*test)(const void *data))
{
int i;
for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) {
char *tname = g_strdup_printf("pnv-xscom/%s/%s", name,
pnv_chips[i].cpu_model);
qtest_add_data_func(tname, &pnv_chips[i], test);
g_free(tname);
}
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
add_test("host-i2c", test_host_i2c);
return g_test_run();
}

View File

@ -10,66 +10,7 @@
#include "libqtest.h"
typedef enum PnvChipType {
PNV_CHIP_POWER8E, /* AKA Murano (default) */
PNV_CHIP_POWER8, /* AKA Venice */
PNV_CHIP_POWER8NVL, /* AKA Naples */
PNV_CHIP_POWER9, /* AKA Nimbus */
PNV_CHIP_POWER10,
} PnvChipType;
typedef struct PnvChip {
PnvChipType chip_type;
const char *cpu_model;
uint64_t xscom_base;
uint64_t cfam_id;
uint32_t first_core;
} PnvChip;
static const PnvChip pnv_chips[] = {
{
.chip_type = PNV_CHIP_POWER8,
.cpu_model = "POWER8",
.xscom_base = 0x0003fc0000000000ull,
.cfam_id = 0x220ea04980000000ull,
.first_core = 0x1,
}, {
.chip_type = PNV_CHIP_POWER8NVL,
.cpu_model = "POWER8NVL",
.xscom_base = 0x0003fc0000000000ull,
.cfam_id = 0x120d304980000000ull,
.first_core = 0x1,
},
{
.chip_type = PNV_CHIP_POWER9,
.cpu_model = "POWER9",
.xscom_base = 0x000603fc00000000ull,
.cfam_id = 0x220d104900008000ull,
.first_core = 0x0,
},
{
.chip_type = PNV_CHIP_POWER10,
.cpu_model = "POWER10",
.xscom_base = 0x000603fc00000000ull,
.cfam_id = 0x120da04900008000ull,
.first_core = 0x0,
},
};
static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba)
{
uint64_t addr = chip->xscom_base;
if (chip->chip_type == PNV_CHIP_POWER10) {
addr |= ((uint64_t) pcba << 3);
} else if (chip->chip_type == PNV_CHIP_POWER9) {
addr |= ((uint64_t) pcba << 3);
} else {
addr |= (((uint64_t) pcba << 4) & ~0xffull) |
(((uint64_t) pcba << 3) & 0x78);
}
return addr;
}
#include "pnv-xscom.h"
static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip,
uint32_t pcba)

80
tests/qtest/pnv-xscom.h Normal file
View File

@ -0,0 +1,80 @@
/*
* PowerNV XSCOM Bus
*
* Copyright (c) 2024, IBM Corporation.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PNV_XSCOM_H
#define PNV_XSCOM_H
#define SMT 4 /* some tests will break if less than 4 */
typedef enum PnvChipType {
PNV_CHIP_POWER8E, /* AKA Murano (default) */
PNV_CHIP_POWER8, /* AKA Venice */
PNV_CHIP_POWER8NVL, /* AKA Naples */
PNV_CHIP_POWER9, /* AKA Nimbus */
PNV_CHIP_POWER10,
} PnvChipType;
typedef struct PnvChip {
PnvChipType chip_type;
const char *cpu_model;
uint64_t xscom_base;
uint64_t cfam_id;
uint32_t first_core;
uint32_t num_i2c;
} PnvChip;
static const PnvChip pnv_chips[] = {
{
.chip_type = PNV_CHIP_POWER8,
.cpu_model = "POWER8",
.xscom_base = 0x0003fc0000000000ull,
.cfam_id = 0x220ea04980000000ull,
.first_core = 0x1,
.num_i2c = 0,
}, {
.chip_type = PNV_CHIP_POWER8NVL,
.cpu_model = "POWER8NVL",
.xscom_base = 0x0003fc0000000000ull,
.cfam_id = 0x120d304980000000ull,
.first_core = 0x1,
.num_i2c = 0,
},
{
.chip_type = PNV_CHIP_POWER9,
.cpu_model = "POWER9",
.xscom_base = 0x000603fc00000000ull,
.cfam_id = 0x220d104900008000ull,
.first_core = 0x0,
.num_i2c = 4,
},
{
.chip_type = PNV_CHIP_POWER10,
.cpu_model = "POWER10",
.xscom_base = 0x000603fc00000000ull,
.cfam_id = 0x120da04900008000ull,
.first_core = 0x0,
.num_i2c = 4,
},
};
static inline uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba)
{
uint64_t addr = chip->xscom_base;
if (chip->chip_type == PNV_CHIP_POWER10) {
addr |= ((uint64_t) pcba << 3);
} else if (chip->chip_type == PNV_CHIP_POWER9) {
addr |= ((uint64_t) pcba << 3);
} else {
addr |= (((uint64_t) pcba << 4) & ~0xffull) |
(((uint64_t) pcba << 3) & 0x78);
}
return addr;
}
#endif /* PNV_XSCOM_H */