* 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:
commit
91e3bf2e92
11
MAINTAINERS
11
MAINTAINERS
@ -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
|
||||
|
@ -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
|
||||
---------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'))
|
||||
|
@ -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
328
hw/misc/pca9554.c
Normal 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)
|
@ -32,6 +32,8 @@ config POWERNV
|
||||
select XIVE
|
||||
select FDT_PPC
|
||||
select PCI_POWERNV
|
||||
select PCA9552
|
||||
select PCA9554
|
||||
|
||||
config PPC405
|
||||
bool
|
||||
|
@ -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(
|
||||
|
131
hw/ppc/pnv.c
131
hw/ppc/pnv.c
@ -790,6 +790,7 @@ static void pnv_init(MachineState *machine)
|
||||
const char *bios_name = machine->firmware ?: FW_FILE_NAME;
|
||||
PnvMachineState *pnv = PNV_MACHINE(machine);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine);
|
||||
char *fw_filename;
|
||||
long fw_size;
|
||||
uint64_t chip_ram_start = 0;
|
||||
@ -979,6 +980,13 @@ static void pnv_init(MachineState *machine)
|
||||
*/
|
||||
pnv->powerdown_notifier.notify = pnv_powerdown_notify;
|
||||
qemu_register_powerdown_notifier(&pnv->powerdown_notifier);
|
||||
|
||||
/*
|
||||
* Create/Connect any machine-specific I2C devices
|
||||
*/
|
||||
if (pmc->i2c_init) {
|
||||
pmc->i2c_init(pnv);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1419,6 +1427,8 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
||||
|
||||
object_initialize_child(obj, "lpc", &chip9->lpc, TYPE_PNV9_LPC);
|
||||
|
||||
object_initialize_child(obj, "chiptod", &chip9->chiptod, TYPE_PNV9_CHIPTOD);
|
||||
|
||||
object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC);
|
||||
|
||||
object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE);
|
||||
@ -1565,6 +1575,19 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
||||
chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0",
|
||||
(uint64_t) PNV9_LPCM_BASE(chip));
|
||||
|
||||
/* ChipTOD */
|
||||
object_property_set_bool(OBJECT(&chip9->chiptod), "primary",
|
||||
chip->chip_id == 0, &error_abort);
|
||||
object_property_set_bool(OBJECT(&chip9->chiptod), "secondary",
|
||||
chip->chip_id == 1, &error_abort);
|
||||
object_property_set_link(OBJECT(&chip9->chiptod), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip9->chiptod), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_CHIPTOD_BASE,
|
||||
&chip9->chiptod.xscom_regs);
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
if (!qdev_realize(DEVICE(&chip9->occ), NULL, errp)) {
|
||||
return;
|
||||
@ -1677,9 +1700,13 @@ static void pnv_chip_power10_instance_init(Object *obj)
|
||||
"xive-fabric");
|
||||
object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI);
|
||||
object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC);
|
||||
object_initialize_child(obj, "chiptod", &chip10->chiptod,
|
||||
TYPE_PNV10_CHIPTOD);
|
||||
object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC);
|
||||
object_initialize_child(obj, "sbe", &chip10->sbe, TYPE_PNV10_SBE);
|
||||
object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER);
|
||||
object_initialize_child(obj, "n1-chiplet", &chip10->n1_chiplet,
|
||||
TYPE_PNV_N1_CHIPLET);
|
||||
|
||||
chip->num_pecs = pcc->num_pecs;
|
||||
|
||||
@ -1810,6 +1837,19 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0",
|
||||
(uint64_t) PNV10_LPCM_BASE(chip));
|
||||
|
||||
/* ChipTOD */
|
||||
object_property_set_bool(OBJECT(&chip10->chiptod), "primary",
|
||||
chip->chip_id == 0, &error_abort);
|
||||
object_property_set_bool(OBJECT(&chip10->chiptod), "secondary",
|
||||
chip->chip_id == 1, &error_abort);
|
||||
object_property_set_link(OBJECT(&chip10->chiptod), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
if (!qdev_realize(DEVICE(&chip10->chiptod), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_CHIPTOD_BASE,
|
||||
&chip10->chiptod.xscom_regs);
|
||||
|
||||
/* Create the simplified OCC model */
|
||||
if (!qdev_realize(DEVICE(&chip10->occ), NULL, errp)) {
|
||||
return;
|
||||
@ -1849,6 +1889,19 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(get_system_memory(), PNV10_HOMER_BASE(chip),
|
||||
&chip10->homer.regs);
|
||||
|
||||
/* N1 chiplet */
|
||||
if (!qdev_realize(DEVICE(&chip10->n1_chiplet), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_CHIPLET_CTRL_REGS_BASE,
|
||||
&chip10->n1_chiplet.nest_pervasive.xscom_ctrl_regs_mr);
|
||||
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_EQ_BASE,
|
||||
&chip10->n1_chiplet.xscom_pb_eq_mr);
|
||||
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_N1_PB_SCOM_ES_BASE,
|
||||
&chip10->n1_chiplet.xscom_pb_es_mr);
|
||||
|
||||
/* PHBs */
|
||||
pnv_chip_power10_phb_realize(chip, &local_err);
|
||||
if (local_err) {
|
||||
@ -1879,6 +1932,39 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in(DEVICE(&chip10->psi),
|
||||
PSIHB9_IRQ_SBE_I2C));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void pnv_rainier_i2c_init(PnvMachineState *pnv)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < pnv->num_chips; i++) {
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]);
|
||||
|
||||
/*
|
||||
* Add a PCA9552 I2C device for PCIe hotplug control
|
||||
* to engine 2, bus 1, address 0x63
|
||||
*/
|
||||
I2CSlave *dev = i2c_slave_create_simple(chip10->i2c[2].busses[1],
|
||||
"pca9552", 0x63);
|
||||
|
||||
/*
|
||||
* Connect PCA9552 GPIO pins 0-4 (SLOTx_EN) outputs to GPIO pins 5-9
|
||||
* (SLOTx_PG) inputs in order to fake the pgood state of PCIe slots
|
||||
* after hypervisor code sets a SLOTx_EN pin high.
|
||||
*/
|
||||
qdev_connect_gpio_out(DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(dev), 5));
|
||||
qdev_connect_gpio_out(DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(dev), 6));
|
||||
qdev_connect_gpio_out(DEVICE(dev), 2, qdev_get_gpio_in(DEVICE(dev), 7));
|
||||
qdev_connect_gpio_out(DEVICE(dev), 3, qdev_get_gpio_in(DEVICE(dev), 8));
|
||||
qdev_connect_gpio_out(DEVICE(dev), 4, qdev_get_gpio_in(DEVICE(dev), 9));
|
||||
|
||||
/*
|
||||
* Add a PCA9554 I2C device for cable card presence detection
|
||||
* to engine 2, bus 1, address 0x25
|
||||
*/
|
||||
i2c_slave_create_simple(chip10->i2c[2].busses[1], "pca9554", 0x25);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr)
|
||||
@ -2035,6 +2121,21 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data)
|
||||
dc->desc = "PowerNV Chip";
|
||||
}
|
||||
|
||||
PnvCore *pnv_chip_find_core(PnvChip *chip, uint32_t core_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chip->nr_cores; i++) {
|
||||
PnvCore *pc = chip->cores[i];
|
||||
CPUCore *cc = CPU_CORE(pc);
|
||||
|
||||
if (cc->core_id == core_id) {
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir)
|
||||
{
|
||||
int i, j;
|
||||
@ -2242,8 +2343,6 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
xfc->match_nvt = pnv_match_nvt;
|
||||
|
||||
mc->alias = "powernv";
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
@ -2251,7 +2350,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
@ -2263,10 +2362,11 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
{ TYPE_PNV_PHB_ROOT_PORT, "version", "5" },
|
||||
};
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
|
||||
compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
|
||||
|
||||
mc->alias = "powernv";
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
@ -2276,6 +2376,24 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
pnv_machine_p10_common_class_init(oc, data);
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER10";
|
||||
}
|
||||
|
||||
static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
|
||||
pnv_machine_p10_common_class_init(oc, data);
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier";
|
||||
pmc->i2c_init = pnv_rainier_i2c_init;
|
||||
}
|
||||
|
||||
static bool pnv_machine_get_hb(Object *obj, Error **errp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(obj);
|
||||
@ -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
586
hw/ppc/pnv_chiptod.c
Normal 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);
|
146
hw/ppc/pnv_i2c.c
146
hw/ppc/pnv_i2c.c
@ -22,136 +22,7 @@
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
/* I2C FIFO register */
|
||||
#define I2C_FIFO_REG 0x4
|
||||
#define I2C_FIFO PPC_BITMASK(0, 7)
|
||||
|
||||
/* I2C command register */
|
||||
#define I2C_CMD_REG 0x5
|
||||
#define I2C_CMD_WITH_START PPC_BIT(0)
|
||||
#define I2C_CMD_WITH_ADDR PPC_BIT(1)
|
||||
#define I2C_CMD_READ_CONT PPC_BIT(2)
|
||||
#define I2C_CMD_WITH_STOP PPC_BIT(3)
|
||||
#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */
|
||||
#define I2C_CMD_INTR_STEER_HOST 1
|
||||
#define I2C_CMD_INTR_STEER_OCC 2
|
||||
#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14)
|
||||
#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15)
|
||||
#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31)
|
||||
#define I2C_MAX_TFR_LEN 0xfff0ull
|
||||
|
||||
/* I2C mode register */
|
||||
#define I2C_MODE_REG 0x6
|
||||
#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15)
|
||||
#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21)
|
||||
#define I2C_MODE_ENHANCED PPC_BIT(28)
|
||||
#define I2C_MODE_DIAGNOSTIC PPC_BIT(29)
|
||||
#define I2C_MODE_PACING_ALLOW PPC_BIT(30)
|
||||
#define I2C_MODE_WRAP PPC_BIT(31)
|
||||
|
||||
/* I2C watermark register */
|
||||
#define I2C_WATERMARK_REG 0x7
|
||||
#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19)
|
||||
#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27)
|
||||
|
||||
/*
|
||||
* I2C interrupt mask and condition registers
|
||||
*
|
||||
* NB: The function of 0x9 and 0xa changes depending on whether you're reading
|
||||
* or writing to them. When read they return the interrupt condition bits
|
||||
* and on writes they update the interrupt mask register.
|
||||
*
|
||||
* The bit definitions are the same for all the interrupt registers.
|
||||
*/
|
||||
#define I2C_INTR_MASK_REG 0x8
|
||||
|
||||
#define I2C_INTR_RAW_COND_REG 0x9 /* read */
|
||||
#define I2C_INTR_MASK_OR_REG 0x9 /* write*/
|
||||
|
||||
#define I2C_INTR_COND_REG 0xa /* read */
|
||||
#define I2C_INTR_MASK_AND_REG 0xa /* write */
|
||||
|
||||
#define I2C_INTR_ALL PPC_BITMASK(16, 31)
|
||||
#define I2C_INTR_INVALID_CMD PPC_BIT(16)
|
||||
#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17)
|
||||
#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18)
|
||||
#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19)
|
||||
#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20)
|
||||
#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21)
|
||||
#define I2C_INTR_DATA_REQ PPC_BIT(22)
|
||||
#define I2C_INTR_CMD_COMP PPC_BIT(23)
|
||||
#define I2C_INTR_STOP_ERR PPC_BIT(24)
|
||||
#define I2C_INTR_I2C_BUSY PPC_BIT(25)
|
||||
#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26)
|
||||
#define I2C_INTR_SCL_EQ_1 PPC_BIT(28)
|
||||
#define I2C_INTR_SCL_EQ_0 PPC_BIT(29)
|
||||
#define I2C_INTR_SDA_EQ_1 PPC_BIT(30)
|
||||
#define I2C_INTR_SDA_EQ_0 PPC_BIT(31)
|
||||
|
||||
/* I2C status register */
|
||||
#define I2C_RESET_I2C_REG 0xb /* write */
|
||||
#define I2C_RESET_ERRORS 0xc
|
||||
#define I2C_STAT_REG 0xb /* read */
|
||||
#define I2C_STAT_INVALID_CMD PPC_BIT(0)
|
||||
#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1)
|
||||
#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2)
|
||||
#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3)
|
||||
#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4)
|
||||
#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5)
|
||||
#define I2C_STAT_DATA_REQ PPC_BIT(6)
|
||||
#define I2C_STAT_CMD_COMP PPC_BIT(7)
|
||||
#define I2C_STAT_STOP_ERR PPC_BIT(8)
|
||||
#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15)
|
||||
#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16)
|
||||
#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19)
|
||||
#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20)
|
||||
#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21)
|
||||
#define I2C_STAT_PORT_BUSY PPC_BIT(22)
|
||||
#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23)
|
||||
#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31)
|
||||
|
||||
#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \
|
||||
I2C_STAT_BKEND_OVERRUN_ERR | \
|
||||
I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \
|
||||
I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR)
|
||||
|
||||
|
||||
#define I2C_INTR_ACTIVE \
|
||||
((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ)
|
||||
|
||||
/* Pseudo-status used for timeouts */
|
||||
#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63)
|
||||
|
||||
/* I2C extended status register */
|
||||
#define I2C_EXTD_STAT_REG 0xc
|
||||
#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7)
|
||||
#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15)
|
||||
#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16)
|
||||
#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17)
|
||||
#define I2C_EXTD_STAT_S_SCL PPC_BIT(18)
|
||||
#define I2C_EXTD_STAT_S_SDA PPC_BIT(19)
|
||||
#define I2C_EXTD_STAT_M_SCL PPC_BIT(20)
|
||||
#define I2C_EXTD_STAT_M_SDA PPC_BIT(21)
|
||||
#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22)
|
||||
#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23)
|
||||
#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24)
|
||||
#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25)
|
||||
#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31)
|
||||
|
||||
/* I2C residual front end/back end length */
|
||||
#define I2C_RESIDUAL_LEN_REG 0xd
|
||||
#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15)
|
||||
#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31)
|
||||
|
||||
/* Port busy register */
|
||||
#define I2C_PORT_BUSY_REG 0xe
|
||||
#define I2C_SET_S_SCL_REG 0xd
|
||||
#define I2C_RESET_S_SCL_REG 0xf
|
||||
#define I2C_SET_S_SDA_REG 0x10
|
||||
#define I2C_RESET_S_SDA_REG 0x11
|
||||
|
||||
#define PNV_I2C_FIFO_SIZE 8
|
||||
#define PNV_I2C_MAX_BUSSES 64
|
||||
#include "hw/i2c/pnv_i2c_regs.h"
|
||||
|
||||
static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c)
|
||||
{
|
||||
@ -629,6 +500,19 @@ static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void *fdt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pnv_i2c_sys_reset(void *dev)
|
||||
{
|
||||
int port;
|
||||
PnvI2C *i2c = PNV_I2C(dev);
|
||||
|
||||
pnv_i2c_reset(dev);
|
||||
|
||||
/* reset all buses connected to this i2c controller */
|
||||
for (port = 0; port < i2c->num_busses; port++) {
|
||||
bus_cold_reset(BUS(i2c->busses[port]));
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_i2c_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvI2C *i2c = PNV_I2C(dev);
|
||||
@ -654,7 +538,7 @@ static void pnv_i2c_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
fifo8_create(&i2c->fifo, PNV_I2C_FIFO_SIZE);
|
||||
|
||||
qemu_register_reset(pnv_i2c_reset, dev);
|
||||
qemu_register_reset(pnv_i2c_sys_reset, dev);
|
||||
|
||||
qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1);
|
||||
}
|
||||
|
173
hw/ppc/pnv_n1_chiplet.c
Normal file
173
hw/ppc/pnv_n1_chiplet.c
Normal 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
208
hw/ppc/pnv_nest_pervasive.c
Normal 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);
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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
|
||||
|
143
include/hw/i2c/pnv_i2c_regs.h
Normal file
143
include/hw/i2c/pnv_i2c_regs.h
Normal 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 */
|
@ -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
36
include/hw/misc/pca9554.h
Normal 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
|
19
include/hw/misc/pca9554_regs.h
Normal file
19
include/hw/misc/pca9554_regs.h
Normal 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
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
53
include/hw/ppc/pnv_chiptod.h
Normal file
53
include/hw/ppc/pnv_chiptod.h
Normal 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 */
|
32
include/hw/ppc/pnv_n1_chiplet.h
Normal file
32
include/hw/ppc/pnv_n1_chiplet.h
Normal 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 */
|
32
include/hw/ppc/pnv_nest_pervasive.h
Normal file
32
include/hw/ppc/pnv_nest_pervasive.h
Normal 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 */
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 },
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
202
tests/avocado/ppc_hv_tests.py
Normal file
202
tests/avocado/ppc_hv_tests.py
Normal 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()
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'] : []) + \
|
||||
|
@ -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)
|
||||
|
491
tests/qtest/pnv-host-i2c-test.c
Normal file
491
tests/qtest/pnv-host-i2c-test.c
Normal 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();
|
||||
}
|
@ -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
80
tests/qtest/pnv-xscom.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user