mirror of https://github.com/xemu-project/xemu.git
ppc patch queue for 2022-08-31:
In the first 7.2 queue we have changes in the powernv pnv-phb handling, the start of the QOMification of the ppc405 model, the removal of the taihu machine, a new SLOF image and others. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCYw/AFgAKCRA82cqW3gMx ZI6XAP0d8m6r1JqKXPSfCwVYy+AfrwY7oZWYbeTqdamK6xHcUQD+JyCcFcogY4Vz YwvHLd9W2cqvoWiZ4tmkK4Mb0Xt0Xg4= =0uL/ -----END PGP SIGNATURE----- Merge tag 'pull-ppc-20220831' of https://gitlab.com/danielhb/qemu into staging ppc patch queue for 2022-08-31: In the first 7.2 queue we have changes in the powernv pnv-phb handling, the start of the QOMification of the ppc405 model, the removal of the taihu machine, a new SLOF image and others. # -----BEGIN PGP SIGNATURE----- # # iHUEABYKAB0WIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCYw/AFgAKCRA82cqW3gMx # ZI6XAP0d8m6r1JqKXPSfCwVYy+AfrwY7oZWYbeTqdamK6xHcUQD+JyCcFcogY4Vz # YwvHLd9W2cqvoWiZ4tmkK4Mb0Xt0Xg4= # =0uL/ # -----END PGP SIGNATURE----- # gpg: Signature made Wed 31 Aug 2022 16:09:58 EDT # gpg: using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164 # gpg: Good signature from "Daniel Henrique Barboza <danielhb413@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: 17EB FF99 23D0 1800 AF28 3819 3CD9 CA96 DE03 3164 * tag 'pull-ppc-20220831' of https://gitlab.com/danielhb/qemu: (60 commits) ppc4xx: Fix code style problems reported by checkpatch ppc/ppc4xx: Fix sdram trace events hw/ppc/Kconfig: Move imply before select hw/ppc/sam460ex: Remove PPC405 dependency from sam460ex ppc405: Move machine specific code to ppc405_boards.c ppc/ppc405: QOM'ify FPGA ppc/ppc405: Use an explicit I2C object hw/intc/ppc-uic: Convert ppc-uic to a PPC4xx DCR device ppc/ppc405: Use an embedded PPCUIC model in SoC state ppc4xx: Rename ppc405-ebc to ppc4xx-ebc ppc4xx: Move EBC model to ppc4xx_devs.c ppc4xx: Rename ppc405-plb to ppc4xx-plb ppc4xx: Move PLB model to ppc4xx_devs.c ppc/ppc405: QOM'ify MAL ppc/ppc405: QOM'ify PLB ppc/ppc405: QOM'ify POB ppc/ppc405: QOM'ify OPBA ppc/ppc405: QOM'ify EBC ppc/ppc405: QOM'ify DMA ppc/ppc405: QOM'ify GPIO ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
7dd9d7e0bd
|
@ -1282,7 +1282,7 @@ F: hw/openrisc/openrisc_sim.c
|
|||
|
||||
PowerPC Machines
|
||||
----------------
|
||||
405 (ref405ep and taihu)
|
||||
405 (ref405ep)
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Orphan
|
||||
F: hw/ppc/ppc405_boards.c
|
||||
|
|
|
@ -233,15 +233,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name
|
|||
better reflects the way this property affects all random data within
|
||||
the device tree blob, not just the ``kaslr-seed`` node.
|
||||
|
||||
PPC 405 ``taihu`` machine (since 7.0)
|
||||
'''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The PPC 405 CPU is a system-on-a-chip, so all 405 machines are very similar,
|
||||
except for some external periphery. However, the periphery of the ``taihu``
|
||||
machine is hardly emulated at all (e.g. neither the LCD nor the USB part had
|
||||
been implemented), so there is not much value added by this board. Use the
|
||||
``ref405ep`` machine instead.
|
||||
|
||||
``pc-i440fx-1.4`` up to ``pc-i440fx-1.7`` (since 7.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
|
|
@ -668,6 +668,12 @@ Aspeed ``swift-bmc`` machine (removed in 7.0)
|
|||
This machine was removed because it was unused. Alternative AST2500 based
|
||||
OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``.
|
||||
|
||||
ppc ``taihu`` machine (removed in 7.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This machine was removed because it was partially emulated and 405
|
||||
machines are very similar. Use the ``ref405ep`` machine instead.
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -6,5 +6,4 @@ Embedded family boards
|
|||
- ``ppce500`` generic paravirt e500 platform
|
||||
- ``ref405ep`` ref405ep
|
||||
- ``sam460ex`` aCube Sam460ex
|
||||
- ``taihu`` taihu
|
||||
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
|
||||
|
|
|
@ -62,7 +62,7 @@ Booting via ``-kernel`` supports the following:
|
|||
+-------------------+-------------------+------------------+
|
||||
| vmlinux LE | ✓ | ✓ |
|
||||
+-------------------+-------------------+------------------+
|
||||
| zImage.pseries BE | x | ✓¹ |
|
||||
| zImage.pseries BE | ✓¹ | ✓¹ |
|
||||
+-------------------+-------------------+------------------+
|
||||
| zImage.pseries LE | ✓ | ✓ |
|
||||
+-------------------+-------------------+------------------+
|
||||
|
|
|
@ -214,18 +214,35 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
|
|||
p->frac_lo &= ~round_mask;
|
||||
}
|
||||
} else if (unlikely(exp >= exp_max)) {
|
||||
flags |= float_flag_overflow | float_flag_inexact;
|
||||
if (overflow_norm) {
|
||||
flags |= float_flag_overflow;
|
||||
if (s->rebias_overflow) {
|
||||
exp -= fmt->exp_re_bias;
|
||||
} else if (overflow_norm) {
|
||||
flags |= float_flag_inexact;
|
||||
exp = exp_max - 1;
|
||||
frac_allones(p);
|
||||
p->frac_lo &= ~round_mask;
|
||||
} else {
|
||||
flags |= float_flag_inexact;
|
||||
p->cls = float_class_inf;
|
||||
exp = exp_max;
|
||||
frac_clear(p);
|
||||
}
|
||||
}
|
||||
frac_shr(p, frac_shift);
|
||||
} else if (unlikely(s->rebias_underflow)) {
|
||||
flags |= float_flag_underflow;
|
||||
exp += fmt->exp_re_bias;
|
||||
if (p->frac_lo & round_mask) {
|
||||
flags |= float_flag_inexact;
|
||||
if (frac_addi(p, p, inc)) {
|
||||
frac_shr(p, 1);
|
||||
p->frac_hi |= DECOMPOSED_IMPLICIT_BIT;
|
||||
exp++;
|
||||
}
|
||||
p->frac_lo &= ~round_mask;
|
||||
}
|
||||
frac_shr(p, frac_shift);
|
||||
} else if (s->flush_to_zero) {
|
||||
flags |= float_flag_output_denormal;
|
||||
p->cls = float_class_zero;
|
||||
|
|
|
@ -521,6 +521,7 @@ typedef struct {
|
|||
typedef struct {
|
||||
int exp_size;
|
||||
int exp_bias;
|
||||
int exp_re_bias;
|
||||
int exp_max;
|
||||
int frac_size;
|
||||
int frac_shift;
|
||||
|
@ -532,6 +533,7 @@ typedef struct {
|
|||
#define FLOAT_PARAMS_(E) \
|
||||
.exp_size = E, \
|
||||
.exp_bias = ((1 << E) - 1) >> 1, \
|
||||
.exp_re_bias = (1 << (E - 1)) + (1 << (E - 2)), \
|
||||
.exp_max = (1 << E) - 1
|
||||
|
||||
#define FLOAT_PARAMS(E, F) \
|
||||
|
|
|
@ -25,11 +25,8 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "hw/intc/ppc-uic.h"
|
||||
#include "hw/irq.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
enum {
|
||||
DCR_UICSR = 0x000,
|
||||
|
@ -105,10 +102,9 @@ static void ppcuic_trigger_irq(PPCUIC *uic)
|
|||
|
||||
static void ppcuic_set_irq(void *opaque, int irq_num, int level)
|
||||
{
|
||||
PPCUIC *uic;
|
||||
PPCUIC *uic = opaque;
|
||||
uint32_t mask, sr;
|
||||
|
||||
uic = opaque;
|
||||
mask = 1U << (31 - irq_num);
|
||||
LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
|
||||
" mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
|
||||
|
@ -144,10 +140,9 @@ static void ppcuic_set_irq(void *opaque, int irq_num, int level)
|
|||
|
||||
static uint32_t dcr_read_uic(void *opaque, int dcrn)
|
||||
{
|
||||
PPCUIC *uic;
|
||||
PPCUIC *uic = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
uic = opaque;
|
||||
dcrn -= uic->dcr_base;
|
||||
switch (dcrn) {
|
||||
case DCR_UICSR:
|
||||
|
@ -192,9 +187,8 @@ static uint32_t dcr_read_uic(void *opaque, int dcrn)
|
|||
|
||||
static void dcr_write_uic(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
PPCUIC *uic;
|
||||
PPCUIC *uic = opaque;
|
||||
|
||||
uic = opaque;
|
||||
dcrn -= uic->dcr_base;
|
||||
LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
|
||||
switch (dcrn) {
|
||||
|
@ -251,19 +245,12 @@ static void ppc_uic_reset(DeviceState *dev)
|
|||
static void ppc_uic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PPCUIC *uic = PPC_UIC(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
PowerPCCPU *cpu;
|
||||
int i;
|
||||
|
||||
if (!uic->cpu) {
|
||||
/* This is a programming error in the code using this device */
|
||||
error_setg(errp, "ppc-uic 'cpu' link property was not set");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu = POWERPC_CPU(uic->cpu);
|
||||
for (i = 0; i < DCR_UICMAX; i++) {
|
||||
ppc_dcr_register(&cpu->env, uic->dcr_base + i, uic,
|
||||
ppc4xx_dcr_register(dcr, uic->dcr_base + i, uic,
|
||||
&dcr_read_uic, &dcr_write_uic);
|
||||
}
|
||||
|
||||
|
@ -273,7 +260,6 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
static Property ppc_uic_properties[] = {
|
||||
DEFINE_PROP_LINK("cpu", PPCUIC, cpu, TYPE_CPU, CPUState *),
|
||||
DEFINE_PROP_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0),
|
||||
DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
|
@ -308,7 +294,7 @@ static void ppc_uic_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
static const TypeInfo ppc_uic_info = {
|
||||
.name = TYPE_PPC_UIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(PPCUIC),
|
||||
.class_init = ppc_uic_class_init,
|
||||
};
|
||||
|
|
|
@ -35,5 +35,6 @@ specific_ss.add(when: 'CONFIG_PCI_POWERNV', if_true: files(
|
|||
'pnv_phb3_msi.c',
|
||||
'pnv_phb3_pbcq.c',
|
||||
'pnv_phb4.c',
|
||||
'pnv_phb4_pec.c'
|
||||
'pnv_phb4_pec.c',
|
||||
'pnv_phb.c',
|
||||
))
|
||||
|
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV Proxy PHB model
|
||||
*
|
||||
* Copyright (c) 2022, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci-host/pnv_phb.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/pci-host/pnv_phb4.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qom/object.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
|
||||
/*
|
||||
* Set the QOM parent and parent bus of an object child. If the device
|
||||
* state associated with the child has an id, use it as QOM id.
|
||||
* Otherwise use object_typename[index] as QOM id.
|
||||
*
|
||||
* This helper does both operations at the same time because seting
|
||||
* a new QOM child will erase the bus parent of the device. This happens
|
||||
* because object_unparent() will call object_property_del_child(),
|
||||
* which in turn calls the property release callback prop->release if
|
||||
* it's defined. In our case this callback is set to
|
||||
* object_finalize_child_property(), which was assigned during the
|
||||
* first object_property_add_child() call. This callback will end up
|
||||
* calling device_unparent(), and this function removes the device
|
||||
* from its parent bus.
|
||||
*
|
||||
* The QOM and parent bus to be set aren´t necessarily related, so
|
||||
* let's receive both as arguments.
|
||||
*/
|
||||
static bool pnv_parent_fixup(Object *parent, BusState *parent_bus,
|
||||
Object *child, int index,
|
||||
Error **errp)
|
||||
{
|
||||
g_autofree char *default_id =
|
||||
g_strdup_printf("%s[%d]", object_get_typename(child), index);
|
||||
const char *dev_id = DEVICE(child)->id;
|
||||
|
||||
if (child->parent == parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
object_ref(child);
|
||||
object_unparent(child);
|
||||
object_property_add_child(parent, dev_id ? dev_id : default_id, child);
|
||||
object_unref(child);
|
||||
|
||||
if (!qdev_set_parent_bus(DEVICE(child), parent_bus, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* User created devices won't have the initial setup that default
|
||||
* devices have. This setup consists of assigning a parent device
|
||||
* (chip for PHB3, PEC for PHB4/5) that will be the QOM/bus parent
|
||||
* of the PHB.
|
||||
*/
|
||||
static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp)
|
||||
{
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
PnvChip *chip = pnv_get_chip(pnv, phb->chip_id);
|
||||
Object *parent = NULL;
|
||||
|
||||
if (!chip) {
|
||||
error_setg(errp, "invalid chip id: %d", phb->chip_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
parent = pnv_chip_add_phb(chip, phb, errp);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reparent user created devices to the chip to build
|
||||
* correctly the device tree. pnv_xscom_dt() needs every
|
||||
* PHB to be a child of the chip to build the DT correctly.
|
||||
*/
|
||||
if (!pnv_parent_fixup(parent, qdev_get_parent_bus(DEVICE(chip)),
|
||||
OBJECT(phb), phb->phb_id, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pnv_phb_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPHB *phb = PNV_PHB(dev);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
g_autofree char *phb_typename = NULL;
|
||||
|
||||
if (!phb->version) {
|
||||
error_setg(errp, "version not specified");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phb->version) {
|
||||
case 3:
|
||||
phb_typename = g_strdup(TYPE_PNV_PHB3);
|
||||
break;
|
||||
case 4:
|
||||
phb_typename = g_strdup(TYPE_PNV_PHB4);
|
||||
break;
|
||||
case 5:
|
||||
phb_typename = g_strdup(TYPE_PNV_PHB5);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
phb->backend = object_new(phb_typename);
|
||||
object_property_add_child(OBJECT(dev), "phb-backend", phb->backend);
|
||||
|
||||
/* Passthrough child device properties to the proxy device */
|
||||
object_property_set_uint(phb->backend, "index", phb->phb_id, errp);
|
||||
object_property_set_uint(phb->backend, "chip-id", phb->chip_id, errp);
|
||||
object_property_set_link(phb->backend, "phb-base", OBJECT(phb), errp);
|
||||
|
||||
/*
|
||||
* Handle user created devices. User devices will not have a
|
||||
* pointer to a chip (PHB3) and a PEC (PHB4/5).
|
||||
*/
|
||||
if (!phb->chip && !phb->pec) {
|
||||
if (!pnv_phb_user_device_init(phb, errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (phb->version == 3) {
|
||||
object_property_set_link(phb->backend, "chip",
|
||||
OBJECT(phb->chip), errp);
|
||||
} else {
|
||||
object_property_set_link(phb->backend, "pec", OBJECT(phb->pec), errp);
|
||||
}
|
||||
|
||||
if (!qdev_realize(DEVICE(phb->backend), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (phb->version == 3) {
|
||||
pnv_phb3_bus_init(dev, PNV_PHB3(phb->backend));
|
||||
} else {
|
||||
pnv_phb4_bus_init(dev, PNV_PHB4(phb->backend));
|
||||
}
|
||||
|
||||
if (defaults_enabled()) {
|
||||
PCIDevice *root = pci_new(PCI_DEVFN(0, 0), TYPE_PNV_PHB_ROOT_PORT);
|
||||
|
||||
pci_realize_and_unref(root, pci->bus, errp);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *pnv_phb_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PnvPHB *phb = PNV_PHB(host_bridge);
|
||||
|
||||
snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x",
|
||||
phb->chip_id, phb->phb_id);
|
||||
return phb->bus_path;
|
||||
}
|
||||
|
||||
static Property pnv_phb_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPHB, phb_id, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPHB, chip_id, 0),
|
||||
DEFINE_PROP_UINT32("version", PnvPHB, version, 0),
|
||||
|
||||
DEFINE_PROP_LINK("chip", PnvPHB, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
|
||||
DEFINE_PROP_LINK("pec", PnvPHB, pec, TYPE_PNV_PHB4_PEC,
|
||||
PnvPhb4PecState *),
|
||||
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_phb_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = pnv_phb_root_bus_path;
|
||||
dc->realize = pnv_phb_realize;
|
||||
device_class_set_props(dc, pnv_phb_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = true;
|
||||
}
|
||||
|
||||
static void pnv_phb_root_port_reset(DeviceState *dev)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev);
|
||||
PCIDevice *d = PCI_DEVICE(dev);
|
||||
uint8_t *conf = d->config;
|
||||
|
||||
rpc->parent_reset(dev);
|
||||
|
||||
if (phb_rp->version == 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* PHB4 and later requires these extra reset steps */
|
||||
pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
|
||||
PCI_IO_RANGE_MASK & 0xff);
|
||||
pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
|
||||
PCI_IO_RANGE_MASK & 0xff);
|
||||
pci_set_word(conf + PCI_MEMORY_BASE, 0);
|
||||
pci_set_word(conf + PCI_MEMORY_LIMIT, 0xfff0);
|
||||
pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0x1);
|
||||
pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1);
|
||||
pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */
|
||||
pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff);
|
||||
pci_config_set_interrupt_pin(conf, 0);
|
||||
}
|
||||
|
||||
static void pnv_phb_root_port_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev);
|
||||
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(dev));
|
||||
PCIDevice *pci = PCI_DEVICE(dev);
|
||||
uint16_t device_id = 0;
|
||||
Error *local_err = NULL;
|
||||
int chip_id, index;
|
||||
|
||||
/*
|
||||
* 'index' will be used both as a PCIE slot value and to calculate
|
||||
* QOM id. 'chip_id' is going to be used as PCIE chassis for the
|
||||
* root port.
|
||||
*/
|
||||
chip_id = object_property_get_int(OBJECT(bus), "chip-id", &error_fatal);
|
||||
index = object_property_get_int(OBJECT(bus), "phb-id", &error_fatal);
|
||||
|
||||
/* Set unique chassis/slot values for the root port */
|
||||
qdev_prop_set_uint8(dev, "chassis", chip_id);
|
||||
qdev_prop_set_uint16(dev, "slot", index);
|
||||
|
||||
/*
|
||||
* User created root ports are QOM parented to one of
|
||||
* the peripheral containers but it's already at the right
|
||||
* parent bus. Change the QOM parent to be the same as the
|
||||
* parent bus it's already assigned to.
|
||||
*/
|
||||
if (!pnv_parent_fixup(OBJECT(bus), BUS(bus), OBJECT(dev),
|
||||
index, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phb_rp->version) {
|
||||
case 3:
|
||||
device_id = PNV_PHB3_DEVICE_ID;
|
||||
break;
|
||||
case 4:
|
||||
device_id = PNV_PHB4_DEVICE_ID;
|
||||
break;
|
||||
case 5:
|
||||
device_id = PNV_PHB5_DEVICE_ID;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
pci_config_set_device_id(pci->config, device_id);
|
||||
pci_config_set_interrupt_pin(pci->config, 0);
|
||||
}
|
||||
|
||||
static Property pnv_phb_root_port_properties[] = {
|
||||
DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0),
|
||||
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
dc->desc = "IBM PHB PCIE Root Port";
|
||||
|
||||
device_class_set_props(dc, pnv_phb_root_port_properties);
|
||||
device_class_set_parent_realize(dc, pnv_phb_root_port_realize,
|
||||
&rpc->parent_realize);
|
||||
device_class_set_parent_reset(dc, pnv_phb_root_port_reset,
|
||||
&rpc->parent_reset);
|
||||
dc->reset = &pnv_phb_root_port_reset;
|
||||
dc->user_creatable = true;
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_IBM;
|
||||
/* device_id will be written during realize() */
|
||||
k->device_id = 0;
|
||||
k->revision = 0;
|
||||
|
||||
rpc->exp_offset = 0x48;
|
||||
rpc->aer_offset = 0x100;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb_type_info = {
|
||||
.name = TYPE_PNV_PHB,
|
||||
.parent = TYPE_PCIE_HOST_BRIDGE,
|
||||
.instance_size = sizeof(PnvPHB),
|
||||
.class_init = pnv_phb_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo pnv_phb_root_port_info = {
|
||||
.name = TYPE_PNV_PHB_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(PnvPHBRootPort),
|
||||
.class_init = pnv_phb_root_port_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_phb_type_info);
|
||||
type_register_static(&pnv_phb_root_port_info);
|
||||
}
|
||||
|
||||
type_init(pnv_phb_register_types)
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV Proxy PHB model
|
||||
*
|
||||
* Copyright (c) 2022, IBM Corporation.
|
||||
*
|
||||
* This code is licensed under the GPL version 2 or later. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef PCI_HOST_PNV_PHB_H
|
||||
#define PCI_HOST_PNV_PHB_H
|
||||
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
typedef struct PnvChip PnvChip;
|
||||
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
||||
|
||||
struct PnvPHB {
|
||||
PCIExpressHost parent_obj;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
uint32_t version;
|
||||
char bus_path[8];
|
||||
|
||||
PnvChip *chip;
|
||||
|
||||
PnvPhb4PecState *pec;
|
||||
|
||||
/* The PHB backend (PnvPHB3, PnvPHB4 ...) being used */
|
||||
Object *backend;
|
||||
};
|
||||
|
||||
#define TYPE_PNV_PHB "pnv-phb"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB, PNV_PHB)
|
||||
|
||||
/*
|
||||
* PHB PCIe Root port
|
||||
*/
|
||||
#define PNV_PHB3_DEVICE_ID 0x03dc
|
||||
#define PNV_PHB4_DEVICE_ID 0x04c1
|
||||
#define PNV_PHB5_DEVICE_ID 0x0652
|
||||
|
||||
typedef struct PnvPHBRootPort {
|
||||
PCIESlot parent_obj;
|
||||
|
||||
uint32_t version;
|
||||
} PnvPHBRootPort;
|
||||
|
||||
#define TYPE_PNV_PHB_ROOT_PORT "pnv-phb-root-port"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHBRootPort, PNV_PHB_ROOT_PORT)
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB_H */
|
|
@ -11,6 +11,7 @@
|
|||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci-host/pnv_phb3_regs.h"
|
||||
#include "hw/pci-host/pnv_phb.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
|
@ -26,7 +27,7 @@
|
|||
|
||||
static PCIDevice *pnv_phb3_find_cfg_dev(PnvPHB3 *phb)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||
uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3];
|
||||
uint8_t bus, devfn;
|
||||
|
||||
|
@ -590,7 +591,7 @@ void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size)
|
|||
uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size)
|
||||
{
|
||||
PnvPHB3 *phb = opaque;
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||
uint64_t val;
|
||||
|
||||
if ((off & 0xfffc) == PHB_CONFIG_DATA) {
|
||||
|
@ -986,10 +987,36 @@ static void pnv_phb3_instance_init(Object *obj)
|
|||
|
||||
}
|
||||
|
||||
void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
|
||||
/*
|
||||
* PHB3 doesn't support IO space. However, qemu gets very upset if
|
||||
* we don't have an IO region to anchor IO BARs onto so we just
|
||||
* initialize one which we never hook up to anything
|
||||
*/
|
||||
memory_region_init(&phb->pci_io, OBJECT(phb), "pci-io", 0x10000);
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio",
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev,
|
||||
dev->id ? dev->id : NULL,
|
||||
pnv_phb3_set_irq, pnv_phb3_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB3_ROOT_BUS);
|
||||
|
||||
object_property_set_int(OBJECT(pci->bus), "phb-id", phb->phb_id,
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id,
|
||||
&error_abort);
|
||||
|
||||
pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb);
|
||||
}
|
||||
|
||||
static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPHB3 *phb = PNV_PHB3(dev);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||
int i;
|
||||
|
||||
|
@ -1034,26 +1061,6 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
|||
/* Controller Registers */
|
||||
memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb,
|
||||
"phb3-regs", 0x1000);
|
||||
|
||||
/*
|
||||
* PHB3 doesn't support IO space. However, qemu gets very upset if
|
||||
* we don't have an IO region to anchor IO BARs onto so we just
|
||||
* initialize one which we never hook up to anything
|
||||
*/
|
||||
memory_region_init(&phb->pci_io, OBJECT(phb), "pci-io", 0x10000);
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), "pci-mmio",
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev,
|
||||
dev->id ? dev->id : NULL,
|
||||
pnv_phb3_set_irq, pnv_phb3_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB3_ROOT_BUS);
|
||||
|
||||
pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb);
|
||||
|
||||
pnv_phb_attach_root_port(pci, TYPE_PNV_PHB3_ROOT_PORT,
|
||||
phb->phb_id, phb->chip_id);
|
||||
}
|
||||
|
||||
void pnv_phb3_update_regions(PnvPHB3 *phb)
|
||||
|
@ -1078,47 +1085,80 @@ void pnv_phb3_update_regions(PnvPHB3 *phb)
|
|||
pnv_phb3_check_all_m64s(phb);
|
||||
}
|
||||
|
||||
static const char *pnv_phb3_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PnvPHB3 *phb = PNV_PHB3(host_bridge);
|
||||
|
||||
snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x",
|
||||
phb->chip_id, phb->phb_id);
|
||||
return phb->bus_path;
|
||||
}
|
||||
|
||||
static Property pnv_phb3_properties[] = {
|
||||
DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0),
|
||||
DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0),
|
||||
DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *),
|
||||
DEFINE_PROP_LINK("phb-base", PnvPHB3, phb_base, TYPE_PNV_PHB, PnvPHB *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_phb3_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = pnv_phb3_root_bus_path;
|
||||
dc->realize = pnv_phb3_realize;
|
||||
device_class_set_props(dc, pnv_phb3_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb3_type_info = {
|
||||
.name = TYPE_PNV_PHB3,
|
||||
.parent = TYPE_PCIE_HOST_BRIDGE,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvPHB3),
|
||||
.class_init = pnv_phb3_class_init,
|
||||
.instance_init = pnv_phb3_instance_init,
|
||||
};
|
||||
|
||||
static void pnv_phb3_root_bus_get_prop(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
PnvPHB3RootBus *bus = PNV_PHB3_ROOT_BUS(obj);
|
||||
uint64_t value = 0;
|
||||
|
||||
if (strcmp(name, "phb-id") == 0) {
|
||||
value = bus->phb_id;
|
||||
} else {
|
||||
value = bus->chip_id;
|
||||
}
|
||||
|
||||
visit_type_size(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void pnv_phb3_root_bus_set_prop(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
|
||||
{
|
||||
PnvPHB3RootBus *bus = PNV_PHB3_ROOT_BUS(obj);
|
||||
uint64_t value;
|
||||
|
||||
if (!visit_type_size(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(name, "phb-id") == 0) {
|
||||
bus->phb_id = value;
|
||||
} else {
|
||||
bus->chip_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
BusClass *k = BUS_CLASS(klass);
|
||||
|
||||
object_class_property_add(klass, "phb-id", "int",
|
||||
pnv_phb3_root_bus_get_prop,
|
||||
pnv_phb3_root_bus_set_prop,
|
||||
NULL, NULL);
|
||||
|
||||
object_class_property_add(klass, "chip-id", "int",
|
||||
pnv_phb3_root_bus_get_prop,
|
||||
pnv_phb3_root_bus_set_prop,
|
||||
NULL, NULL);
|
||||
|
||||
/*
|
||||
* PHB3 has only a single root complex. Enforce the limit on the
|
||||
* parent bus
|
||||
|
@ -1132,51 +1172,9 @@ static const TypeInfo pnv_phb3_root_bus_info = {
|
|||
.class_init = pnv_phb3_root_bus_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PCIDevice *pci = PCI_DEVICE(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
pci_config_set_interrupt_pin(pci->config, 0);
|
||||
}
|
||||
|
||||
static void pnv_phb3_root_port_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
dc->desc = "IBM PHB3 PCIE Root Port";
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_phb3_root_port_realize,
|
||||
&rpc->parent_realize);
|
||||
dc->user_creatable = false;
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_IBM;
|
||||
k->device_id = 0x03dc;
|
||||
k->revision = 0;
|
||||
|
||||
rpc->exp_offset = 0x48;
|
||||
rpc->aer_offset = 0x100;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb3_root_port_info = {
|
||||
.name = TYPE_PNV_PHB3_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(PnvPHB3RootPort),
|
||||
.class_init = pnv_phb3_root_port_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb3_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_phb3_root_bus_info);
|
||||
type_register_static(&pnv_phb3_root_port_info);
|
||||
type_register_static(&pnv_phb3_type_info);
|
||||
type_register_static(&pnv_phb3_iommu_memory_region_info);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
static PCIDevice *pnv_phb4_find_cfg_dev(PnvPHB4 *phb)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||
uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3];
|
||||
uint8_t bus, devfn;
|
||||
|
||||
|
@ -129,7 +129,7 @@ static uint64_t pnv_phb4_config_read(PnvPHB4 *phb, unsigned off,
|
|||
static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off,
|
||||
unsigned size, uint64_t val)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||
PCIDevice *pdev;
|
||||
|
||||
if (size != 4) {
|
||||
|
@ -150,7 +150,7 @@ static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off,
|
|||
static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off,
|
||||
unsigned size)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||
PCIDevice *pdev;
|
||||
uint64_t val;
|
||||
|
||||
|
@ -1528,11 +1528,42 @@ static void pnv_phb4_instance_init(Object *obj)
|
|||
object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE);
|
||||
}
|
||||
|
||||
void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb)
|
||||
{
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
char name[32];
|
||||
|
||||
/*
|
||||
* PHB4 doesn't support IO space. However, qemu gets very upset if
|
||||
* we don't have an IO region to anchor IO BARs onto so we just
|
||||
* initialize one which we never hook up to anything
|
||||
*/
|
||||
snprintf(name, sizeof(name), "phb4-%d.%d-pci-io", phb->chip_id,
|
||||
phb->phb_id);
|
||||
memory_region_init(&phb->pci_io, OBJECT(phb), name, 0x10000);
|
||||
|
||||
snprintf(name, sizeof(name), "phb4-%d.%d-pci-mmio", phb->chip_id,
|
||||
phb->phb_id);
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), name,
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, dev->id ? dev->id : NULL,
|
||||
pnv_phb4_set_irq, pnv_phb4_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB4_ROOT_BUS);
|
||||
|
||||
object_property_set_int(OBJECT(pci->bus), "phb-id", phb->phb_id,
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id,
|
||||
&error_abort);
|
||||
|
||||
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
|
||||
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
|
||||
}
|
||||
|
||||
static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvPHB4 *phb = PNV_PHB4(dev);
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(phb->pec);
|
||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
||||
XiveSource *xsrc = &phb->xsrc;
|
||||
int nr_irqs;
|
||||
char name[32];
|
||||
|
@ -1546,32 +1577,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
|||
memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb4_reg_ops, phb,
|
||||
name, 0x2000);
|
||||
|
||||
/*
|
||||
* PHB4 doesn't support IO space. However, qemu gets very upset if
|
||||
* we don't have an IO region to anchor IO BARs onto so we just
|
||||
* initialize one which we never hook up to anything
|
||||
*/
|
||||
|
||||
snprintf(name, sizeof(name), "phb4-%d.%d-pci-io", phb->chip_id,
|
||||
phb->phb_id);
|
||||
memory_region_init(&phb->pci_io, OBJECT(phb), name, 0x10000);
|
||||
|
||||
snprintf(name, sizeof(name), "phb4-%d.%d-pci-mmio", phb->chip_id,
|
||||
phb->phb_id);
|
||||
memory_region_init(&phb->pci_mmio, OBJECT(phb), name,
|
||||
PCI_MMIO_TOTAL_SIZE);
|
||||
|
||||
pci->bus = pci_register_root_bus(dev, dev->id,
|
||||
pnv_phb4_set_irq, pnv_phb4_map_irq, phb,
|
||||
&phb->pci_mmio, &phb->pci_io,
|
||||
0, 4, TYPE_PNV_PHB4_ROOT_BUS);
|
||||
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
|
||||
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
|
||||
|
||||
/* Add a single Root port if running with defaults */
|
||||
pnv_phb_attach_root_port(pci, pecc->rp_model,
|
||||
phb->phb_id, phb->chip_id);
|
||||
|
||||
/* Setup XIVE Source */
|
||||
if (phb->big_phb) {
|
||||
nr_irqs = PNV_PHB4_MAX_INTs;
|
||||
|
@ -1591,16 +1596,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
|||
pnv_phb4_xscom_realize(phb);
|
||||
}
|
||||
|
||||
static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PnvPHB4 *phb = PNV_PHB4(host_bridge);
|
||||
|
||||
snprintf(phb->bus_path, sizeof(phb->bus_path), "00%02x:%02x",
|
||||
phb->chip_id, phb->phb_id);
|
||||
return phb->bus_path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Address base trigger mode (POWER10)
|
||||
*
|
||||
|
@ -1685,19 +1680,17 @@ static Property pnv_phb4_properties[] = {
|
|||
DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0),
|
||||
DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC,
|
||||
PnvPhb4PecState *),
|
||||
DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass);
|
||||
|
||||
hc->root_bus_path = pnv_phb4_root_bus_path;
|
||||
dc->realize = pnv_phb4_realize;
|
||||
device_class_set_props(dc, pnv_phb4_properties);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->user_creatable = false;
|
||||
|
||||
xfc->notify = pnv_phb4_xive_notify;
|
||||
|
@ -1705,7 +1698,7 @@ static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
|||
|
||||
static const TypeInfo pnv_phb4_type_info = {
|
||||
.name = TYPE_PNV_PHB4,
|
||||
.parent = TYPE_PCIE_HOST_BRIDGE,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_init = pnv_phb4_instance_init,
|
||||
.instance_size = sizeof(PnvPHB4),
|
||||
.class_init = pnv_phb4_class_init,
|
||||
|
@ -1721,10 +1714,55 @@ static const TypeInfo pnv_phb5_type_info = {
|
|||
.instance_size = sizeof(PnvPHB4),
|
||||
};
|
||||
|
||||
static void pnv_phb4_root_bus_get_prop(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
PnvPHB4RootBus *bus = PNV_PHB4_ROOT_BUS(obj);
|
||||
uint64_t value = 0;
|
||||
|
||||
if (strcmp(name, "phb-id") == 0) {
|
||||
value = bus->phb_id;
|
||||
} else {
|
||||
value = bus->chip_id;
|
||||
}
|
||||
|
||||
visit_type_size(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void pnv_phb4_root_bus_set_prop(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
|
||||
{
|
||||
PnvPHB4RootBus *bus = PNV_PHB4_ROOT_BUS(obj);
|
||||
uint64_t value;
|
||||
|
||||
if (!visit_type_size(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(name, "phb-id") == 0) {
|
||||
bus->phb_id = value;
|
||||
} else {
|
||||
bus->chip_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
BusClass *k = BUS_CLASS(klass);
|
||||
|
||||
object_class_property_add(klass, "phb-id", "int",
|
||||
pnv_phb4_root_bus_get_prop,
|
||||
pnv_phb4_root_bus_set_prop,
|
||||
NULL, NULL);
|
||||
|
||||
object_class_property_add(klass, "chip-id", "int",
|
||||
pnv_phb4_root_bus_get_prop,
|
||||
pnv_phb4_root_bus_set_prop,
|
||||
NULL, NULL);
|
||||
|
||||
/*
|
||||
* PHB4 has only a single root complex. Enforce the limit on the
|
||||
* parent bus
|
||||
|
@ -1738,94 +1776,9 @@ static const TypeInfo pnv_phb4_root_bus_info = {
|
|||
.class_init = pnv_phb4_root_bus_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb4_root_port_reset(DeviceState *dev)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
PCIDevice *d = PCI_DEVICE(dev);
|
||||
uint8_t *conf = d->config;
|
||||
|
||||
rpc->parent_reset(dev);
|
||||
|
||||
pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
|
||||
PCI_IO_RANGE_MASK & 0xff);
|
||||
pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
|
||||
PCI_IO_RANGE_MASK & 0xff);
|
||||
pci_set_word(conf + PCI_MEMORY_BASE, 0);
|
||||
pci_set_word(conf + PCI_MEMORY_LIMIT, 0xfff0);
|
||||
pci_set_word(conf + PCI_PREF_MEMORY_BASE, 0x1);
|
||||
pci_set_word(conf + PCI_PREF_MEMORY_LIMIT, 0xfff1);
|
||||
pci_set_long(conf + PCI_PREF_BASE_UPPER32, 0x1); /* Hack */
|
||||
pci_set_long(conf + PCI_PREF_LIMIT_UPPER32, 0xffffffff);
|
||||
pci_config_set_interrupt_pin(conf, 0);
|
||||
}
|
||||
|
||||
static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
rpc->parent_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_phb4_root_port_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
|
||||
|
||||
dc->desc = "IBM PHB4 PCIE Root Port";
|
||||
dc->user_creatable = false;
|
||||
|
||||
device_class_set_parent_realize(dc, pnv_phb4_root_port_realize,
|
||||
&rpc->parent_realize);
|
||||
device_class_set_parent_reset(dc, pnv_phb4_root_port_reset,
|
||||
&rpc->parent_reset);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_IBM;
|
||||
k->device_id = PNV_PHB4_DEVICE_ID;
|
||||
k->revision = 0;
|
||||
|
||||
rpc->exp_offset = 0x48;
|
||||
rpc->aer_offset = 0x100;
|
||||
|
||||
dc->reset = &pnv_phb4_root_port_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb4_root_port_info = {
|
||||
.name = TYPE_PNV_PHB4_ROOT_PORT,
|
||||
.parent = TYPE_PCIE_ROOT_PORT,
|
||||
.instance_size = sizeof(PnvPHB4RootPort),
|
||||
.class_init = pnv_phb4_root_port_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb5_root_port_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "IBM PHB5 PCIE Root Port";
|
||||
dc->user_creatable = false;
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_IBM;
|
||||
k->device_id = PNV_PHB5_DEVICE_ID;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb5_root_port_info = {
|
||||
.name = TYPE_PNV_PHB5_ROOT_PORT,
|
||||
.parent = TYPE_PNV_PHB4_ROOT_PORT,
|
||||
.instance_size = sizeof(PnvPHB4RootPort),
|
||||
.class_init = pnv_phb5_root_port_class_init,
|
||||
};
|
||||
|
||||
static void pnv_phb4_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_phb4_root_bus_info);
|
||||
type_register_static(&pnv_phb5_root_port_info);
|
||||
type_register_static(&pnv_phb4_root_port_info);
|
||||
type_register_static(&pnv_phb4_type_info);
|
||||
type_register_static(&pnv_phb5_type_info);
|
||||
type_register_static(&pnv_phb4_iommu_memory_region_info);
|
||||
|
|
|
@ -115,8 +115,7 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
|
|||
int stack_no,
|
||||
Error **errp)
|
||||
{
|
||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
||||
PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type));
|
||||
PnvPHB *phb = PNV_PHB(qdev_new(TYPE_PNV_PHB));
|
||||
int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no);
|
||||
|
||||
object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb));
|
||||
|
@ -147,8 +146,10 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp)
|
|||
pec->num_phbs = pecc->num_phbs[pec->index];
|
||||
|
||||
/* Create PHBs if running with defaults */
|
||||
for (i = 0; i < pec->num_phbs; i++) {
|
||||
pnv_pec_default_phb_realize(pec, i, errp);
|
||||
if (defaults_enabled()) {
|
||||
for (i = 0; i < pec->num_phbs; i++) {
|
||||
pnv_pec_default_phb_realize(pec, i, errp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the XSCOM regions for the PEC registers */
|
||||
|
@ -261,7 +262,6 @@ static void pnv_pec_class_init(ObjectClass *klass, void *data)
|
|||
pecc->version = PNV_PHB4_VERSION;
|
||||
pecc->phb_type = TYPE_PNV_PHB4;
|
||||
pecc->num_phbs = pnv_pec_num_phbs;
|
||||
pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_pec_type_info = {
|
||||
|
@ -314,7 +314,6 @@ static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data)
|
|||
pecc->version = PNV_PHB5_VERSION;
|
||||
pecc->phb_type = TYPE_PNV_PHB5;
|
||||
pecc->num_phbs = pnv_phb5_pec_num_stacks;
|
||||
pecc->rp_model = TYPE_PNV_PHB5_ROOT_PORT;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_phb5_pec_type_info = {
|
||||
|
|
|
@ -58,7 +58,6 @@ config PPC4XX
|
|||
|
||||
config SAM460EX
|
||||
bool
|
||||
select PPC405
|
||||
select PFLASH_CFI01
|
||||
select IDE_SII3112
|
||||
select M41T80
|
||||
|
@ -72,6 +71,7 @@ config SAM460EX
|
|||
|
||||
config PEGASOS2
|
||||
bool
|
||||
imply ATI_VGA
|
||||
select MV64361
|
||||
select VT82C686
|
||||
select IDE_VIA
|
||||
|
@ -79,7 +79,6 @@ config PEGASOS2
|
|||
select VOF
|
||||
# This should come with VT82C686
|
||||
select ACPI_X86
|
||||
imply ATI_VGA
|
||||
|
||||
config PREP
|
||||
bool
|
||||
|
|
|
@ -46,6 +46,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
|
|||
'pnv_lpc.c',
|
||||
'pnv_psi.c',
|
||||
'pnv_occ.c',
|
||||
'pnv_sbe.c',
|
||||
'pnv_bmc.c',
|
||||
'pnv_homer.c',
|
||||
'pnv_pnor.c',
|
||||
|
|
186
hw/ppc/pnv.c
186
hw/ppc/pnv.c
|
@ -43,6 +43,7 @@
|
|||
#include "hw/ipmi/ipmi.h"
|
||||
#include "target/ppc/mmu-hash64.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci-host/pnv_phb.h"
|
||||
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
@ -280,6 +281,75 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir,
|
|||
g_free(reg);
|
||||
}
|
||||
|
||||
static PnvPhb4PecState *pnv_phb4_get_pec(PnvChip *chip, PnvPHB4 *phb,
|
||||
Error **errp)
|
||||
{
|
||||
PnvPHB *phb_base = phb->phb_base;
|
||||
PnvPhb4PecState *pecs = NULL;
|
||||
int chip_id = phb->chip_id;
|
||||
int index = phb->phb_id;
|
||||
int i, j;
|
||||
|
||||
if (phb_base->version == 4) {
|
||||
Pnv9Chip *chip9 = PNV9_CHIP(chip);
|
||||
|
||||
pecs = chip9->pecs;
|
||||
} else if (phb_base->version == 5) {
|
||||
Pnv10Chip *chip10 = PNV10_CHIP(chip);
|
||||
|
||||
pecs = chip10->pecs;
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
for (i = 0; i < chip->num_pecs; i++) {
|
||||
/*
|
||||
* For each PEC, check the amount of phbs it supports
|
||||
* and see if the given phb4 index matches an index.
|
||||
*/
|
||||
PnvPhb4PecState *pec = &pecs[i];
|
||||
|
||||
for (j = 0; j < pec->num_phbs; j++) {
|
||||
if (index == pnv_phb4_pec_get_phb_id(pec, j)) {
|
||||
return pec;
|
||||
}
|
||||
}
|
||||
}
|
||||
error_setg(errp,
|
||||
"pnv-phb4 chip-id %d index %d didn't match any existing PEC",
|
||||
chip_id, index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a PnvPHB to the chip. Returns the parent obj of the
|
||||
* PHB which varies with each version (phb version 3 is parented
|
||||
* by the chip, version 4 and 5 are parented by the PEC
|
||||
* device).
|
||||
*
|
||||
* TODO: for version 3 we're still parenting the PHB with the
|
||||
* chip. We should parent with a (so far not implemented)
|
||||
* PHB3 PEC device.
|
||||
*/
|
||||
Object *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp)
|
||||
{
|
||||
if (phb->version == 3) {
|
||||
Pnv8Chip *chip8 = PNV8_CHIP(chip);
|
||||
|
||||
phb->chip = chip;
|
||||
|
||||
chip8->phbs[chip8->num_phbs] = phb;
|
||||
chip8->num_phbs++;
|
||||
|
||||
return OBJECT(chip);
|
||||
}
|
||||
|
||||
phb->pec = pnv_phb4_get_pec(chip, PNV_PHB4(phb->backend), errp);
|
||||
|
||||
return OBJECT(phb->pec);
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
|
||||
{
|
||||
static const char compat[] = "ibm,power8-xscom\0ibm,xscom";
|
||||
|
@ -660,7 +730,8 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon)
|
|||
ics_pic_print_info(&chip8->psi.ics, mon);
|
||||
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
PnvPHB3 *phb3 = &chip8->phbs[i];
|
||||
PnvPHB *phb = chip8->phbs[i];
|
||||
PnvPHB3 *phb3 = PNV_PHB3(phb->backend);
|
||||
|
||||
pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
|
||||
ics_pic_print_info(&phb3->lsis, mon);
|
||||
|
@ -670,11 +741,14 @@ static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon)
|
|||
static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque)
|
||||
{
|
||||
Monitor *mon = opaque;
|
||||
PnvPHB4 *phb4 = (PnvPHB4 *) object_dynamic_cast(child, TYPE_PNV_PHB4);
|
||||
PnvPHB *phb = (PnvPHB *) object_dynamic_cast(child, TYPE_PNV_PHB);
|
||||
|
||||
if (phb4) {
|
||||
pnv_phb4_pic_print_info(phb4, mon);
|
||||
if (!phb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), mon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -714,7 +788,7 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type)
|
|||
PowerPCCPUClass *ppc =
|
||||
POWERPC_CPU_CLASS(object_class_by_name(cpu_type));
|
||||
|
||||
return ppc_default->pvr_match(ppc_default, ppc->pvr);
|
||||
return ppc_default->pvr_match(ppc_default, ppc->pvr, false);
|
||||
}
|
||||
|
||||
static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq)
|
||||
|
@ -1146,10 +1220,22 @@ static void pnv_chip_power8_instance_init(Object *obj)
|
|||
|
||||
object_initialize_child(obj, "homer", &chip8->homer, TYPE_PNV8_HOMER);
|
||||
|
||||
chip8->num_phbs = pcc->num_phbs;
|
||||
if (defaults_enabled()) {
|
||||
chip8->num_phbs = pcc->num_phbs;
|
||||
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3);
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
Object *phb = object_new(TYPE_PNV_PHB);
|
||||
|
||||
/*
|
||||
* We need the chip to parent the PHB to allow the DT
|
||||
* to build correctly (via pnv_xscom_dt()).
|
||||
*
|
||||
* TODO: the PHB should be parented by a PEC device that, at
|
||||
* this moment, is not modelled powernv8/phb3.
|
||||
*/
|
||||
object_property_add_child(obj, "phb[*]", phb);
|
||||
chip8->phbs[i] = PNV_PHB(phb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1183,30 +1269,6 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach a root port device.
|
||||
*
|
||||
* 'index' will be used both as a PCIE slot value and to calculate
|
||||
* QOM id. 'chip_id' is going to be used as PCIE chassis for the
|
||||
* root port.
|
||||
*/
|
||||
void pnv_phb_attach_root_port(PCIHostState *pci, const char *name,
|
||||
int index, int chip_id)
|
||||
{
|
||||
PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name);
|
||||
g_autofree char *default_id = g_strdup_printf("%s[%d]", name, index);
|
||||
const char *dev_id = DEVICE(root)->id;
|
||||
|
||||
object_property_add_child(OBJECT(pci->bus), dev_id ? dev_id : default_id,
|
||||
OBJECT(root));
|
||||
|
||||
/* Set unique chassis/slot values for the root port */
|
||||
qdev_prop_set_uint8(DEVICE(root), "chassis", chip_id);
|
||||
qdev_prop_set_uint16(DEVICE(root), "slot", index);
|
||||
|
||||
pci_realize_and_unref(root, pci->bus, &error_fatal);
|
||||
}
|
||||
|
||||
static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
|
||||
|
@ -1287,9 +1349,9 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
|||
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
|
||||
&chip8->homer.regs);
|
||||
|
||||
/* PHB3 controllers */
|
||||
/* PHB controllers */
|
||||
for (i = 0; i < chip8->num_phbs; i++) {
|
||||
PnvPHB3 *phb = &chip8->phbs[i];
|
||||
PnvPHB *phb = chip8->phbs[i];
|
||||
|
||||
object_property_set_int(OBJECT(phb), "index", i, &error_fatal);
|
||||
object_property_set_int(OBJECT(phb), "chip-id", chip->chip_id,
|
||||
|
@ -1397,6 +1459,8 @@ static void pnv_chip_power9_instance_init(Object *obj)
|
|||
|
||||
object_initialize_child(obj, "occ", &chip9->occ, TYPE_PNV9_OCC);
|
||||
|
||||
object_initialize_child(obj, "sbe", &chip9->sbe, TYPE_PNV9_SBE);
|
||||
|
||||
object_initialize_child(obj, "homer", &chip9->homer, TYPE_PNV9_HOMER);
|
||||
|
||||
/* Number of PECs is the chip default */
|
||||
|
@ -1549,6 +1613,17 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp)
|
|||
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
|
||||
&chip9->occ.sram_regs);
|
||||
|
||||
/* SBE */
|
||||
if (!qdev_realize(DEVICE(&chip9->sbe), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_SBE_CTRL_BASE,
|
||||
&chip9->sbe.xscom_ctrl_regs);
|
||||
pnv_xscom_add_subregion(chip, PNV9_XSCOM_SBE_MBOX_BASE,
|
||||
&chip9->sbe.xscom_mbox_regs);
|
||||
qdev_connect_gpio_out(DEVICE(&chip9->sbe), 0, qdev_get_gpio_in(
|
||||
DEVICE(&chip9->psi), PSIHB9_IRQ_PSU));
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
|
@ -1613,6 +1688,7 @@ static void pnv_chip_power10_instance_init(Object *obj)
|
|||
object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI);
|
||||
object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC);
|
||||
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);
|
||||
|
||||
chip->num_pecs = pcc->num_pecs;
|
||||
|
@ -1754,6 +1830,17 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp)
|
|||
PNV10_OCC_SENSOR_BASE(chip),
|
||||
&chip10->occ.sram_regs);
|
||||
|
||||
/* SBE */
|
||||
if (!qdev_realize(DEVICE(&chip10->sbe), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_SBE_CTRL_BASE,
|
||||
&chip10->sbe.xscom_ctrl_regs);
|
||||
pnv_xscom_add_subregion(chip, PNV10_XSCOM_SBE_MBOX_BASE,
|
||||
&chip10->sbe.xscom_mbox_regs);
|
||||
qdev_connect_gpio_out(DEVICE(&chip10->sbe), 0, qdev_get_gpio_in(
|
||||
DEVICE(&chip10->psi), PSIHB9_IRQ_PSU));
|
||||
|
||||
/* HOMER */
|
||||
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
|
||||
&error_abort);
|
||||
|
@ -1957,7 +2044,8 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
|||
}
|
||||
|
||||
for (j = 0; j < chip8->num_phbs; j++) {
|
||||
PnvPHB3 *phb3 = &chip8->phbs[j];
|
||||
PnvPHB *phb = chip8->phbs[j];
|
||||
PnvPHB3 *phb3 = PNV_PHB3(phb->backend);
|
||||
|
||||
if (ics_valid_irq(&phb3->lsis, irq)) {
|
||||
return &phb3->lsis;
|
||||
|
@ -1995,7 +2083,8 @@ static void pnv_ics_resend(XICSFabric *xi)
|
|||
ics_resend(&chip8->psi.ics);
|
||||
|
||||
for (j = 0; j < chip8->num_phbs; j++) {
|
||||
PnvPHB3 *phb3 = &chip8->phbs[j];
|
||||
PnvPHB *phb = chip8->phbs[j];
|
||||
PnvPHB3 *phb3 = PNV_PHB3(phb->backend);
|
||||
|
||||
ics_resend(&phb3->lsis);
|
||||
ics_resend(ICS(&phb3->msis));
|
||||
|
@ -2095,8 +2184,14 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data)
|
|||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv";
|
||||
|
||||
static GlobalProperty phb_compat[] = {
|
||||
{ TYPE_PNV_PHB, "version", "3" },
|
||||
{ TYPE_PNV_PHB_ROOT_PORT, "version", "3" },
|
||||
};
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER8";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
|
||||
compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
|
||||
|
||||
xic->icp_get = pnv_icp_get;
|
||||
xic->ics_get = pnv_ics_get;
|
||||
|
@ -2104,6 +2199,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data)
|
|||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -2113,8 +2210,15 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
|||
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv9\0ibm,powernv";
|
||||
|
||||
static GlobalProperty phb_compat[] = {
|
||||
{ TYPE_PNV_PHB, "version", "4" },
|
||||
{ TYPE_PNV_PHB_ROOT_PORT, "version", "4" },
|
||||
};
|
||||
|
||||
mc->desc = "IBM PowerNV (Non-Virtualized) POWER9";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0");
|
||||
compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat));
|
||||
|
||||
xfc->match_nvt = pnv_match_nvt;
|
||||
|
||||
mc->alias = "powernv";
|
||||
|
@ -2122,6 +2226,8 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
|||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -2131,14 +2237,22 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
|
|||
XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc);
|
||||
static const char compat[] = "qemu,powernv10\0ibm,powernv";
|
||||
|
||||
static GlobalProperty phb_compat[] = {
|
||||
{ TYPE_PNV_PHB, "version", "5" },
|
||||
{ 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));
|
||||
|
||||
pmc->compat = compat;
|
||||
pmc->compat_size = sizeof(compat);
|
||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||
|
||||
xfc->match_nvt = pnv10_xive_match_nvt;
|
||||
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB);
|
||||
}
|
||||
|
||||
static bool pnv_machine_get_hb(Object *obj, Error **errp)
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV Emulation of some SBE behaviour
|
||||
*
|
||||
* Copyright (c) 2022, IBM Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "target/ppc/cpu.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/ppc/pnv.h"
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/ppc/pnv_sbe.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
* Most register and command definitions come from skiboot.
|
||||
*
|
||||
* xscom addresses are adjusted to be relative to xscom subregion bases
|
||||
*/
|
||||
|
||||
/*
|
||||
* SBE MBOX register address
|
||||
* Reg 0 - 3 : Host to send command packets to SBE
|
||||
* Reg 4 - 7 : SBE to send response packets to Host
|
||||
*/
|
||||
#define PSU_HOST_SBE_MBOX_REG0 0x00000000
|
||||
#define PSU_HOST_SBE_MBOX_REG1 0x00000001
|
||||
#define PSU_HOST_SBE_MBOX_REG2 0x00000002
|
||||
#define PSU_HOST_SBE_MBOX_REG3 0x00000003
|
||||
#define PSU_HOST_SBE_MBOX_REG4 0x00000004
|
||||
#define PSU_HOST_SBE_MBOX_REG5 0x00000005
|
||||
#define PSU_HOST_SBE_MBOX_REG6 0x00000006
|
||||
#define PSU_HOST_SBE_MBOX_REG7 0x00000007
|
||||
#define PSU_SBE_DOORBELL_REG_RW 0x00000010
|
||||
#define PSU_SBE_DOORBELL_REG_AND 0x00000011
|
||||
#define PSU_SBE_DOORBELL_REG_OR 0x00000012
|
||||
#define PSU_HOST_DOORBELL_REG_RW 0x00000013
|
||||
#define PSU_HOST_DOORBELL_REG_AND 0x00000014
|
||||
#define PSU_HOST_DOORBELL_REG_OR 0x00000015
|
||||
|
||||
/*
|
||||
* Doorbell register to trigger SBE interrupt. Set by OPAL to inform
|
||||
* the SBE about a waiting message in the Host/SBE mailbox registers
|
||||
*/
|
||||
#define HOST_SBE_MSG_WAITING PPC_BIT(0)
|
||||
|
||||
/*
|
||||
* Doorbell register for host bridge interrupt. Set by the SBE to inform
|
||||
* host about a response message in the Host/SBE mailbox registers
|
||||
*/
|
||||
#define SBE_HOST_RESPONSE_WAITING PPC_BIT(0)
|
||||
#define SBE_HOST_MSG_READ PPC_BIT(1)
|
||||
#define SBE_HOST_STOP15_EXIT PPC_BIT(2)
|
||||
#define SBE_HOST_RESET PPC_BIT(3)
|
||||
#define SBE_HOST_PASSTHROUGH PPC_BIT(4)
|
||||
#define SBE_HOST_TIMER_EXPIRY PPC_BIT(14)
|
||||
#define SBE_HOST_RESPONSE_MASK (PPC_BITMASK(0, 4) | \
|
||||
SBE_HOST_TIMER_EXPIRY)
|
||||
|
||||
/* SBE Control Register */
|
||||
#define SBE_CONTROL_REG_RW 0x00000000
|
||||
|
||||
/* SBE interrupt s0/s1 bits */
|
||||
#define SBE_CONTROL_REG_S0 PPC_BIT(14)
|
||||
#define SBE_CONTROL_REG_S1 PPC_BIT(15)
|
||||
|
||||
struct sbe_msg {
|
||||
uint64_t reg[4];
|
||||
};
|
||||
|
||||
static uint64_t pnv_sbe_power9_xscom_ctrl_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
uint32_t offset = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
switch (offset) {
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
|
||||
trace_pnv_sbe_xscom_ctrl_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pnv_sbe_power9_xscom_ctrl_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
trace_pnv_sbe_xscom_ctrl_write(addr, val);
|
||||
|
||||
switch (offset) {
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_sbe_power9_xscom_ctrl_ops = {
|
||||
.read = pnv_sbe_power9_xscom_ctrl_read,
|
||||
.write = pnv_sbe_power9_xscom_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_sbe_set_host_doorbell(PnvSBE *sbe, uint64_t val)
|
||||
{
|
||||
val &= SBE_HOST_RESPONSE_MASK; /* Is this right? What does HW do? */
|
||||
sbe->host_doorbell = val;
|
||||
|
||||
trace_pnv_sbe_reg_set_host_doorbell(val);
|
||||
qemu_set_irq(sbe->psi_irq, !!val);
|
||||
}
|
||||
|
||||
/* SBE Target Type */
|
||||
#define SBE_TARGET_TYPE_PROC 0x00
|
||||
#define SBE_TARGET_TYPE_EX 0x01
|
||||
#define SBE_TARGET_TYPE_PERV 0x02
|
||||
#define SBE_TARGET_TYPE_MCS 0x03
|
||||
#define SBE_TARGET_TYPE_EQ 0x04
|
||||
#define SBE_TARGET_TYPE_CORE 0x05
|
||||
|
||||
/* SBE MBOX command class */
|
||||
#define SBE_MCLASS_FIRST 0xD1
|
||||
#define SBE_MCLASS_CORE_STATE 0xD1
|
||||
#define SBE_MCLASS_SCOM 0xD2
|
||||
#define SBE_MCLASS_RING 0xD3
|
||||
#define SBE_MCLASS_TIMER 0xD4
|
||||
#define SBE_MCLASS_MPIPL 0xD5
|
||||
#define SBE_MCLASS_SECURITY 0xD6
|
||||
#define SBE_MCLASS_GENERIC 0xD7
|
||||
#define SBE_MCLASS_LAST 0xD7
|
||||
|
||||
/*
|
||||
* Commands are provided in xxyy form where:
|
||||
* - xx : command class
|
||||
* - yy : command
|
||||
*
|
||||
* Both request and response message uses same seq ID,
|
||||
* command class and command.
|
||||
*/
|
||||
#define SBE_CMD_CTRL_DEADMAN_LOOP 0xD101
|
||||
#define SBE_CMD_MULTI_SCOM 0xD201
|
||||
#define SBE_CMD_PUT_RING_FORM_IMAGE 0xD301
|
||||
#define SBE_CMD_CONTROL_TIMER 0xD401
|
||||
#define SBE_CMD_GET_ARCHITECTED_REG 0xD501
|
||||
#define SBE_CMD_CLR_ARCHITECTED_REG 0xD502
|
||||
#define SBE_CMD_SET_UNSEC_MEM_WINDOW 0xD601
|
||||
#define SBE_CMD_GET_SBE_FFDC 0xD701
|
||||
#define SBE_CMD_GET_CAPABILITY 0xD702
|
||||
#define SBE_CMD_READ_SBE_SEEPROM 0xD703
|
||||
#define SBE_CMD_SET_FFDC_ADDR 0xD704
|
||||
#define SBE_CMD_QUIESCE_SBE 0xD705
|
||||
#define SBE_CMD_SET_FABRIC_ID_MAP 0xD706
|
||||
#define SBE_CMD_STASH_MPIPL_CONFIG 0xD707
|
||||
|
||||
/* SBE MBOX control flags */
|
||||
|
||||
/* Generic flags */
|
||||
#define SBE_CMD_CTRL_RESP_REQ 0x0100
|
||||
#define SBE_CMD_CTRL_ACK_REQ 0x0200
|
||||
|
||||
/* Deadman loop */
|
||||
#define CTRL_DEADMAN_LOOP_START 0x0001
|
||||
#define CTRL_DEADMAN_LOOP_STOP 0x0002
|
||||
|
||||
/* Control timer */
|
||||
#define CONTROL_TIMER_START 0x0001
|
||||
#define CONTROL_TIMER_STOP 0x0002
|
||||
|
||||
/* Stash MPIPL config */
|
||||
#define SBE_STASH_KEY_SKIBOOT_BASE 0x03
|
||||
|
||||
static void sbe_timer(void *opaque)
|
||||
{
|
||||
PnvSBE *sbe = opaque;
|
||||
|
||||
trace_pnv_sbe_cmd_timer_expired();
|
||||
|
||||
pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_TIMER_EXPIRY);
|
||||
}
|
||||
|
||||
static void do_sbe_msg(PnvSBE *sbe)
|
||||
{
|
||||
struct sbe_msg msg;
|
||||
uint16_t cmd, ctrl_flags, seq_id;
|
||||
int i;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
msg.reg[i] = sbe->mbox[i];
|
||||
}
|
||||
|
||||
cmd = msg.reg[0];
|
||||
seq_id = msg.reg[0] >> 16;
|
||||
ctrl_flags = msg.reg[0] >> 32;
|
||||
|
||||
trace_pnv_sbe_msg_recv(cmd, seq_id, ctrl_flags);
|
||||
|
||||
if (ctrl_flags & SBE_CMD_CTRL_ACK_REQ) {
|
||||
pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_MSG_READ);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case SBE_CMD_CONTROL_TIMER:
|
||||
if (ctrl_flags & CONTROL_TIMER_START) {
|
||||
uint64_t us = msg.reg[1];
|
||||
trace_pnv_sbe_cmd_timer_start(us);
|
||||
timer_mod(sbe->timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + us);
|
||||
}
|
||||
if (ctrl_flags & CONTROL_TIMER_STOP) {
|
||||
trace_pnv_sbe_cmd_timer_stop();
|
||||
timer_del(sbe->timer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SBE Unimplemented command: 0x%x\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void pnv_sbe_set_sbe_doorbell(PnvSBE *sbe, uint64_t val)
|
||||
{
|
||||
val &= HOST_SBE_MSG_WAITING;
|
||||
sbe->sbe_doorbell = val;
|
||||
|
||||
if (val & HOST_SBE_MSG_WAITING) {
|
||||
sbe->sbe_doorbell &= ~HOST_SBE_MSG_WAITING;
|
||||
do_sbe_msg(sbe);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pnv_sbe_power9_xscom_mbox_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PnvSBE *sbe = PNV_SBE(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
uint64_t val = 0;
|
||||
|
||||
if (offset <= PSU_HOST_SBE_MBOX_REG7) {
|
||||
uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0;
|
||||
val = sbe->mbox[idx];
|
||||
} else {
|
||||
switch (offset) {
|
||||
case PSU_SBE_DOORBELL_REG_RW:
|
||||
val = sbe->sbe_doorbell;
|
||||
break;
|
||||
case PSU_HOST_DOORBELL_REG_RW:
|
||||
val = sbe->host_doorbell;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
trace_pnv_sbe_xscom_mbox_read(addr, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void pnv_sbe_power9_xscom_mbox_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PnvSBE *sbe = PNV_SBE(opaque);
|
||||
uint32_t offset = addr >> 3;
|
||||
|
||||
trace_pnv_sbe_xscom_mbox_write(addr, val);
|
||||
|
||||
if (offset <= PSU_HOST_SBE_MBOX_REG7) {
|
||||
uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0;
|
||||
sbe->mbox[idx] = val;
|
||||
} else {
|
||||
switch (offset) {
|
||||
case PSU_SBE_DOORBELL_REG_RW:
|
||||
pnv_sbe_set_sbe_doorbell(sbe, val);
|
||||
break;
|
||||
case PSU_SBE_DOORBELL_REG_AND:
|
||||
pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell & val);
|
||||
break;
|
||||
case PSU_SBE_DOORBELL_REG_OR:
|
||||
pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell | val);
|
||||
break;
|
||||
|
||||
case PSU_HOST_DOORBELL_REG_RW:
|
||||
pnv_sbe_set_host_doorbell(sbe, val);
|
||||
break;
|
||||
case PSU_HOST_DOORBELL_REG_AND:
|
||||
pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell & val);
|
||||
break;
|
||||
case PSU_HOST_DOORBELL_REG_OR:
|
||||
pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | val);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
|
||||
HWADDR_PRIx "\n", addr >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = {
|
||||
.read = pnv_sbe_power9_xscom_mbox_read,
|
||||
.write = pnv_sbe_power9_xscom_mbox_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_sbe_power9_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvSBEClass *psc = PNV_SBE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "PowerNV SBE Controller (POWER9)";
|
||||
psc->xscom_ctrl_size = PNV9_XSCOM_SBE_CTRL_SIZE;
|
||||
psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops;
|
||||
psc->xscom_mbox_size = PNV9_XSCOM_SBE_MBOX_SIZE;
|
||||
psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_sbe_power9_type_info = {
|
||||
.name = TYPE_PNV9_SBE,
|
||||
.parent = TYPE_PNV_SBE,
|
||||
.instance_size = sizeof(PnvSBE),
|
||||
.class_init = pnv_sbe_power9_class_init,
|
||||
};
|
||||
|
||||
static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PnvSBEClass *psc = PNV_SBE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "PowerNV SBE Controller (POWER10)";
|
||||
psc->xscom_ctrl_size = PNV10_XSCOM_SBE_CTRL_SIZE;
|
||||
psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops;
|
||||
psc->xscom_mbox_size = PNV10_XSCOM_SBE_MBOX_SIZE;
|
||||
psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_sbe_power10_type_info = {
|
||||
.name = TYPE_PNV10_SBE,
|
||||
.parent = TYPE_PNV9_SBE,
|
||||
.class_init = pnv_sbe_power10_class_init,
|
||||
};
|
||||
|
||||
static void pnv_sbe_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PnvSBE *sbe = PNV_SBE(dev);
|
||||
PnvSBEClass *psc = PNV_SBE_GET_CLASS(sbe);
|
||||
|
||||
/* XScom regions for SBE registers */
|
||||
pnv_xscom_region_init(&sbe->xscom_ctrl_regs, OBJECT(dev),
|
||||
psc->xscom_ctrl_ops, sbe, "xscom-sbe-ctrl",
|
||||
psc->xscom_ctrl_size);
|
||||
pnv_xscom_region_init(&sbe->xscom_mbox_regs, OBJECT(dev),
|
||||
psc->xscom_mbox_ops, sbe, "xscom-sbe-mbox",
|
||||
psc->xscom_mbox_size);
|
||||
|
||||
qdev_init_gpio_out(DEVICE(dev), &sbe->psi_irq, 1);
|
||||
|
||||
sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe);
|
||||
}
|
||||
|
||||
static void pnv_sbe_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = pnv_sbe_realize;
|
||||
dc->desc = "PowerNV SBE Controller";
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo pnv_sbe_type_info = {
|
||||
.name = TYPE_PNV_SBE,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(PnvSBE),
|
||||
.class_init = pnv_sbe_class_init,
|
||||
.class_size = sizeof(PnvSBEClass),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void pnv_sbe_register_types(void)
|
||||
{
|
||||
type_register_static(&pnv_sbe_type_info);
|
||||
type_register_static(&pnv_sbe_power9_type_info);
|
||||
type_register_static(&pnv_sbe_power10_type_info);
|
||||
}
|
||||
|
||||
type_init(pnv_sbe_register_types);
|
|
@ -295,6 +295,9 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset,
|
|||
_FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size)));
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
|
||||
if (chip->chip_id == 0) {
|
||||
_FDT((fdt_setprop(fdt, xscom_offset, "primary", NULL, 0)));
|
||||
}
|
||||
|
||||
args.fdt = fdt;
|
||||
args.xscom_offset = xscom_offset;
|
||||
|
|
198
hw/ppc/ppc405.h
198
hw/ppc/ppc405.h
|
@ -25,54 +25,168 @@
|
|||
#ifndef PPC405_H
|
||||
#define PPC405_H
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
#include "hw/intc/ppc-uic.h"
|
||||
#include "hw/i2c/ppc4xx_i2c.h"
|
||||
|
||||
#define PPC405EP_SDRAM_BASE 0x00000000
|
||||
#define PPC405EP_NVRAM_BASE 0xF0000000
|
||||
#define PPC405EP_FPGA_BASE 0xF0300000
|
||||
#define PPC405EP_SRAM_BASE 0xFFF00000
|
||||
#define PPC405EP_SRAM_SIZE (512 * KiB)
|
||||
#define PPC405EP_FLASH_BASE 0xFFF80000
|
||||
/* PLB to OPB bridge */
|
||||
#define TYPE_PPC405_POB "ppc405-pob"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405PobState, PPC405_POB);
|
||||
struct Ppc405PobState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
/* Bootinfo as set-up by u-boot */
|
||||
typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
|
||||
struct ppc4xx_bd_info_t {
|
||||
uint32_t bi_memstart;
|
||||
uint32_t bi_memsize;
|
||||
uint32_t bi_flashstart;
|
||||
uint32_t bi_flashsize;
|
||||
uint32_t bi_flashoffset; /* 0x10 */
|
||||
uint32_t bi_sramstart;
|
||||
uint32_t bi_sramsize;
|
||||
uint32_t bi_bootflags;
|
||||
uint32_t bi_ipaddr; /* 0x20 */
|
||||
uint8_t bi_enetaddr[6];
|
||||
uint16_t bi_ethspeed;
|
||||
uint32_t bi_intfreq;
|
||||
uint32_t bi_busfreq; /* 0x30 */
|
||||
uint32_t bi_baudrate;
|
||||
uint8_t bi_s_version[4];
|
||||
uint8_t bi_r_version[32];
|
||||
uint32_t bi_procfreq;
|
||||
uint32_t bi_plb_busfreq;
|
||||
uint32_t bi_pci_busfreq;
|
||||
uint8_t bi_pci_enetaddr[6];
|
||||
uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */
|
||||
uint32_t bi_opbfreq;
|
||||
uint32_t bi_iic_fast[2];
|
||||
uint32_t bear;
|
||||
uint32_t besr0;
|
||||
uint32_t besr1;
|
||||
};
|
||||
|
||||
/* PowerPC 405 core */
|
||||
ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size);
|
||||
/* OPB arbitrer */
|
||||
#define TYPE_PPC405_OPBA "ppc405-opba"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OpbaState, PPC405_OPBA);
|
||||
struct Ppc405OpbaState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
void ppc4xx_plb_init(CPUPPCState *env);
|
||||
void ppc405_ebc_init(CPUPPCState *env);
|
||||
MemoryRegion io;
|
||||
uint8_t cr;
|
||||
uint8_t pr;
|
||||
};
|
||||
|
||||
PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
||||
MemoryRegion ram_memories[2],
|
||||
hwaddr ram_bases[2],
|
||||
hwaddr ram_sizes[2],
|
||||
uint32_t sysclk, DeviceState **uicdev,
|
||||
int do_init);
|
||||
/* DMA controller */
|
||||
#define TYPE_PPC405_DMA "ppc405-dma"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405DmaState, PPC405_DMA);
|
||||
struct Ppc405DmaState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
qemu_irq irqs[4];
|
||||
uint32_t cr[4];
|
||||
uint32_t ct[4];
|
||||
uint32_t da[4];
|
||||
uint32_t sa[4];
|
||||
uint32_t sg[4];
|
||||
uint32_t sr;
|
||||
uint32_t sgc;
|
||||
uint32_t slp;
|
||||
uint32_t pol;
|
||||
};
|
||||
|
||||
/* GPIO */
|
||||
#define TYPE_PPC405_GPIO "ppc405-gpio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GpioState, PPC405_GPIO);
|
||||
struct Ppc405GpioState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion io;
|
||||
uint32_t or;
|
||||
uint32_t tcr;
|
||||
uint32_t osrh;
|
||||
uint32_t osrl;
|
||||
uint32_t tsrh;
|
||||
uint32_t tsrl;
|
||||
uint32_t odr;
|
||||
uint32_t ir;
|
||||
uint32_t rr1;
|
||||
uint32_t isr1h;
|
||||
uint32_t isr1l;
|
||||
};
|
||||
|
||||
/* On Chip Memory */
|
||||
#define TYPE_PPC405_OCM "ppc405-ocm"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OcmState, PPC405_OCM);
|
||||
struct Ppc405OcmState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
MemoryRegion ram;
|
||||
MemoryRegion isarc_ram;
|
||||
MemoryRegion dsarc_ram;
|
||||
uint32_t isarc;
|
||||
uint32_t isacntl;
|
||||
uint32_t dsarc;
|
||||
uint32_t dsacntl;
|
||||
};
|
||||
|
||||
/* General purpose timers */
|
||||
#define TYPE_PPC405_GPT "ppc405-gpt"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405GptState, PPC405_GPT);
|
||||
struct Ppc405GptState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
|
||||
int64_t tb_offset;
|
||||
uint32_t tb_freq;
|
||||
QEMUTimer *timer;
|
||||
qemu_irq irqs[5];
|
||||
uint32_t oe;
|
||||
uint32_t ol;
|
||||
uint32_t im;
|
||||
uint32_t is;
|
||||
uint32_t ie;
|
||||
uint32_t comp[5];
|
||||
uint32_t mask[5];
|
||||
};
|
||||
|
||||
#define TYPE_PPC405_CPC "ppc405-cpc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405CpcState, PPC405_CPC);
|
||||
|
||||
enum {
|
||||
PPC405EP_CPU_CLK = 0,
|
||||
PPC405EP_PLB_CLK = 1,
|
||||
PPC405EP_OPB_CLK = 2,
|
||||
PPC405EP_EBC_CLK = 3,
|
||||
PPC405EP_MAL_CLK = 4,
|
||||
PPC405EP_PCI_CLK = 5,
|
||||
PPC405EP_UART0_CLK = 6,
|
||||
PPC405EP_UART1_CLK = 7,
|
||||
PPC405EP_CLK_NB = 8,
|
||||
};
|
||||
|
||||
struct Ppc405CpcState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
uint32_t sysclk;
|
||||
clk_setup_t clk_setup[PPC405EP_CLK_NB];
|
||||
uint32_t boot;
|
||||
uint32_t epctl;
|
||||
uint32_t pllmr[2];
|
||||
uint32_t ucr;
|
||||
uint32_t srr;
|
||||
uint32_t jtagid;
|
||||
uint32_t pci;
|
||||
/* Clock and power management */
|
||||
uint32_t er;
|
||||
uint32_t fr;
|
||||
uint32_t sr;
|
||||
};
|
||||
|
||||
#define TYPE_PPC405_SOC "ppc405-soc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405SoCState, PPC405_SOC);
|
||||
|
||||
struct Ppc405SoCState {
|
||||
/* Private */
|
||||
DeviceState parent_obj;
|
||||
|
||||
/* Public */
|
||||
MemoryRegion ram_banks[2];
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
bool do_dram_init;
|
||||
|
||||
MemoryRegion *dram_mr;
|
||||
hwaddr ram_size;
|
||||
|
||||
PowerPCCPU cpu;
|
||||
PPCUIC uic;
|
||||
Ppc405CpcState cpc;
|
||||
Ppc405GptState gpt;
|
||||
Ppc405OcmState ocm;
|
||||
Ppc405GpioState gpio;
|
||||
Ppc405DmaState dma;
|
||||
PPC4xxI2CState i2c;
|
||||
Ppc4xxEbcState ebc;
|
||||
Ppc405OpbaState opba;
|
||||
Ppc405PobState pob;
|
||||
Ppc4xxPlbState plb;
|
||||
Ppc4xxMalState mal;
|
||||
};
|
||||
|
||||
#endif /* PPC405_H */
|
||||
|
|
|
@ -48,97 +48,24 @@
|
|||
#define KERNEL_LOAD_ADDR 0x01000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
#define PPC405EP_SDRAM_BASE 0x00000000
|
||||
#define PPC405EP_SRAM_BASE 0xFFF00000
|
||||
#define PPC405EP_SRAM_SIZE (512 * KiB)
|
||||
|
||||
#define USE_FLASH_BIOS
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PPC405EP reference board (IBM) */
|
||||
/* Standalone board with:
|
||||
* - PowerPC 405EP CPU
|
||||
* - SDRAM (0x00000000)
|
||||
* - Flash (0xFFF80000)
|
||||
* - SRAM (0xFFF00000)
|
||||
* - NVRAM (0xF0000000)
|
||||
* - FPGA (0xF0300000)
|
||||
*/
|
||||
typedef struct ref405ep_fpga_t ref405ep_fpga_t;
|
||||
struct ref405ep_fpga_t {
|
||||
uint8_t reg0;
|
||||
uint8_t reg1;
|
||||
#define TYPE_PPC405_MACHINE MACHINE_TYPE_NAME("ppc405")
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405MachineState, PPC405_MACHINE);
|
||||
|
||||
struct Ppc405MachineState {
|
||||
/* Private */
|
||||
MachineState parent_obj;
|
||||
/* Public */
|
||||
|
||||
Ppc405SoCState soc;
|
||||
};
|
||||
|
||||
static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
uint32_t ret;
|
||||
|
||||
fpga = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
ret = fpga->reg0;
|
||||
break;
|
||||
case 0x1:
|
||||
ret = fpga->reg1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
|
||||
fpga = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
/* Read only */
|
||||
break;
|
||||
case 0x1:
|
||||
fpga->reg1 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ref405ep_fpga_ops = {
|
||||
.read = ref405ep_fpga_readb,
|
||||
.write = ref405ep_fpga_writeb,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 1,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void ref405ep_fpga_reset (void *opaque)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
|
||||
fpga = opaque;
|
||||
fpga->reg0 = 0x00;
|
||||
fpga->reg1 = 0x0F;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
MemoryRegion *fpga_memory = g_new(MemoryRegion, 1);
|
||||
|
||||
fpga = g_new0(ref405ep_fpga_t, 1);
|
||||
memory_region_init_io(fpga_memory, NULL, &ref405ep_fpga_ops, fpga,
|
||||
"fpga", 0x00000100);
|
||||
memory_region_add_subregion(sysmem, base, fpga_memory);
|
||||
qemu_register_reset(&ref405ep_fpga_reset, fpga);
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU reset handler when booting directly from a loaded kernel
|
||||
*/
|
||||
/* CPU reset handler when booting directly from a loaded kernel */
|
||||
static struct boot_info {
|
||||
uint32_t entry;
|
||||
uint32_t bdloc;
|
||||
|
@ -169,6 +96,126 @@ static void main_cpu_reset(void *opaque)
|
|||
env->nip = bi->entry;
|
||||
}
|
||||
|
||||
/* Bootinfo as set-up by u-boot */
|
||||
typedef struct {
|
||||
uint32_t bi_memstart;
|
||||
uint32_t bi_memsize;
|
||||
uint32_t bi_flashstart;
|
||||
uint32_t bi_flashsize;
|
||||
uint32_t bi_flashoffset; /* 0x10 */
|
||||
uint32_t bi_sramstart;
|
||||
uint32_t bi_sramsize;
|
||||
uint32_t bi_bootflags;
|
||||
uint32_t bi_ipaddr; /* 0x20 */
|
||||
uint8_t bi_enetaddr[6];
|
||||
uint16_t bi_ethspeed;
|
||||
uint32_t bi_intfreq;
|
||||
uint32_t bi_busfreq; /* 0x30 */
|
||||
uint32_t bi_baudrate;
|
||||
uint8_t bi_s_version[4];
|
||||
uint8_t bi_r_version[32];
|
||||
uint32_t bi_procfreq;
|
||||
uint32_t bi_plb_busfreq;
|
||||
uint32_t bi_pci_busfreq;
|
||||
uint8_t bi_pci_enetaddr[6];
|
||||
uint8_t bi_pci_enetaddr2[6]; /* PPC405EP specific */
|
||||
uint32_t bi_opbfreq;
|
||||
uint32_t bi_iic_fast[2];
|
||||
} ppc4xx_bd_info_t;
|
||||
|
||||
static void ppc405_set_default_bootinfo(ppc4xx_bd_info_t *bd,
|
||||
ram_addr_t ram_size)
|
||||
{
|
||||
memset(bd, 0, sizeof(*bd));
|
||||
|
||||
bd->bi_memstart = PPC405EP_SDRAM_BASE;
|
||||
bd->bi_memsize = ram_size;
|
||||
bd->bi_sramstart = PPC405EP_SRAM_BASE;
|
||||
bd->bi_sramsize = PPC405EP_SRAM_SIZE;
|
||||
bd->bi_bootflags = 0;
|
||||
bd->bi_intfreq = 133333333;
|
||||
bd->bi_busfreq = 33333333;
|
||||
bd->bi_baudrate = 115200;
|
||||
bd->bi_s_version[0] = 'Q';
|
||||
bd->bi_s_version[1] = 'M';
|
||||
bd->bi_s_version[2] = 'U';
|
||||
bd->bi_s_version[3] = '\0';
|
||||
bd->bi_r_version[0] = 'Q';
|
||||
bd->bi_r_version[1] = 'E';
|
||||
bd->bi_r_version[2] = 'M';
|
||||
bd->bi_r_version[3] = 'U';
|
||||
bd->bi_r_version[4] = '\0';
|
||||
bd->bi_procfreq = 133333333;
|
||||
bd->bi_plb_busfreq = 33333333;
|
||||
bd->bi_pci_busfreq = 33333333;
|
||||
bd->bi_opbfreq = 33333333;
|
||||
}
|
||||
|
||||
static ram_addr_t __ppc405_set_bootinfo(CPUPPCState *env, ppc4xx_bd_info_t *bd)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
ram_addr_t bdloc;
|
||||
int i, n;
|
||||
|
||||
/* We put the bd structure at the top of memory */
|
||||
if (bd->bi_memsize >= 0x01000000UL) {
|
||||
bdloc = 0x01000000UL - sizeof(ppc4xx_bd_info_t);
|
||||
} else {
|
||||
bdloc = bd->bi_memsize - sizeof(ppc4xx_bd_info_t);
|
||||
}
|
||||
stl_be_phys(cs->as, bdloc + 0x00, bd->bi_memstart);
|
||||
stl_be_phys(cs->as, bdloc + 0x04, bd->bi_memsize);
|
||||
stl_be_phys(cs->as, bdloc + 0x08, bd->bi_flashstart);
|
||||
stl_be_phys(cs->as, bdloc + 0x0C, bd->bi_flashsize);
|
||||
stl_be_phys(cs->as, bdloc + 0x10, bd->bi_flashoffset);
|
||||
stl_be_phys(cs->as, bdloc + 0x14, bd->bi_sramstart);
|
||||
stl_be_phys(cs->as, bdloc + 0x18, bd->bi_sramsize);
|
||||
stl_be_phys(cs->as, bdloc + 0x1C, bd->bi_bootflags);
|
||||
stl_be_phys(cs->as, bdloc + 0x20, bd->bi_ipaddr);
|
||||
for (i = 0; i < 6; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x24 + i, bd->bi_enetaddr[i]);
|
||||
}
|
||||
stw_be_phys(cs->as, bdloc + 0x2A, bd->bi_ethspeed);
|
||||
stl_be_phys(cs->as, bdloc + 0x2C, bd->bi_intfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x30, bd->bi_busfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x34, bd->bi_baudrate);
|
||||
for (i = 0; i < 4; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x38 + i, bd->bi_s_version[i]);
|
||||
}
|
||||
for (i = 0; i < 32; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x3C + i, bd->bi_r_version[i]);
|
||||
}
|
||||
stl_be_phys(cs->as, bdloc + 0x5C, bd->bi_procfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x60, bd->bi_plb_busfreq);
|
||||
stl_be_phys(cs->as, bdloc + 0x64, bd->bi_pci_busfreq);
|
||||
for (i = 0; i < 6; i++) {
|
||||
stb_phys(cs->as, bdloc + 0x68 + i, bd->bi_pci_enetaddr[i]);
|
||||
}
|
||||
n = 0x70; /* includes 2 bytes hole */
|
||||
for (i = 0; i < 6; i++) {
|
||||
stb_phys(cs->as, bdloc + n++, bd->bi_pci_enetaddr2[i]);
|
||||
}
|
||||
stl_be_phys(cs->as, bdloc + n, bd->bi_opbfreq);
|
||||
n += 4;
|
||||
for (i = 0; i < 2; i++) {
|
||||
stl_be_phys(cs->as, bdloc + n, bd->bi_iic_fast[i]);
|
||||
n += 4;
|
||||
}
|
||||
|
||||
return bdloc;
|
||||
}
|
||||
|
||||
static ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size)
|
||||
{
|
||||
ppc4xx_bd_info_t bd;
|
||||
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
|
||||
ppc405_set_default_bootinfo(&bd, ram_size);
|
||||
|
||||
return __ppc405_set_bootinfo(env, &bd);
|
||||
}
|
||||
|
||||
static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
@ -221,18 +268,12 @@ static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
|||
env->load_info = &boot_info;
|
||||
}
|
||||
|
||||
static void ref405ep_init(MachineState *machine)
|
||||
static void ppc405_init(MachineState *machine)
|
||||
{
|
||||
Ppc405MachineState *ppc405 = PPC405_MACHINE(machine);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
PowerPCCPU *cpu;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *ram_memories = g_new(MemoryRegion, 2);
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
DeviceState *uicdev;
|
||||
|
||||
if (machine->ram_size != mc->default_ram_size) {
|
||||
char *sz = size_to_str(mc->default_ram_size);
|
||||
|
@ -241,22 +282,17 @@ static void ref405ep_init(MachineState *machine)
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* XXX: fix this */
|
||||
memory_region_init_alias(&ram_memories[0], NULL, "ef405ep.ram.alias",
|
||||
machine->ram, 0, machine->ram_size);
|
||||
ram_bases[0] = 0;
|
||||
ram_sizes[0] = machine->ram_size;
|
||||
memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0);
|
||||
ram_bases[1] = 0x00000000;
|
||||
ram_sizes[1] = 0x00000000;
|
||||
|
||||
cpu = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &uicdev, kernel_filename == NULL ? 0 : 1);
|
||||
|
||||
/* allocate SRAM */
|
||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", PPC405EP_SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, PPC405EP_SRAM_BASE, sram);
|
||||
object_initialize_child(OBJECT(machine), "soc", &ppc405->soc,
|
||||
TYPE_PPC405_SOC);
|
||||
object_property_set_uint(OBJECT(&ppc405->soc), "ram-size",
|
||||
machine->ram_size, &error_fatal);
|
||||
object_property_set_link(OBJECT(&ppc405->soc), "dram",
|
||||
OBJECT(machine->ram), &error_abort);
|
||||
object_property_set_bool(OBJECT(&ppc405->soc), "dram-init",
|
||||
kernel_filename != NULL, &error_abort);
|
||||
object_property_set_uint(OBJECT(&ppc405->soc), "sys-clk", 33333333,
|
||||
&error_abort);
|
||||
qdev_realize(DEVICE(&ppc405->soc), NULL, &error_fatal);
|
||||
|
||||
/* allocate and load BIOS */
|
||||
if (machine->firmware) {
|
||||
|
@ -285,15 +321,6 @@ static void ref405ep_init(MachineState *machine)
|
|||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
}
|
||||
|
||||
/* Register FPGA */
|
||||
ref405ep_fpga_init(sysmem, PPC405EP_FPGA_BASE);
|
||||
/* Register NVRAM */
|
||||
dev = qdev_new("sysbus-m48t08");
|
||||
qdev_prop_set_int32(dev, "base-year", 1968);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE);
|
||||
|
||||
/* Load kernel and initrd using U-Boot images */
|
||||
if (kernel_filename && machine->firmware) {
|
||||
target_ulong kernel_base, initrd_base;
|
||||
|
@ -322,63 +349,66 @@ static void ref405ep_init(MachineState *machine)
|
|||
|
||||
/* Load ELF kernel and rootfs.cpio */
|
||||
} else if (kernel_filename && !machine->firmware) {
|
||||
boot_from_kernel(machine, cpu);
|
||||
boot_from_kernel(machine, &ppc405->soc.cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void ref405ep_class_init(ObjectClass *oc, void *data)
|
||||
static void ppc405_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "ref405ep";
|
||||
mc->init = ref405ep_init;
|
||||
mc->default_ram_size = 0x08000000;
|
||||
mc->default_ram_id = "ef405ep.ram";
|
||||
mc->desc = "PPC405 generic machine";
|
||||
mc->init = ppc405_init;
|
||||
mc->default_ram_size = 128 * MiB;
|
||||
mc->default_ram_id = "ppc405.ram";
|
||||
}
|
||||
|
||||
static const TypeInfo ref405ep_type = {
|
||||
.name = MACHINE_TYPE_NAME("ref405ep"),
|
||||
static const TypeInfo ppc405_machine_type = {
|
||||
.name = TYPE_PPC405_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.class_init = ref405ep_class_init,
|
||||
.instance_size = sizeof(Ppc405MachineState),
|
||||
.class_init = ppc405_machine_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* AMCC Taihu evaluation board */
|
||||
/* - PowerPC 405EP processor
|
||||
* - SDRAM 128 MB at 0x00000000
|
||||
* - Boot flash 2 MB at 0xFFE00000
|
||||
* - Application flash 32 MB at 0xFC000000
|
||||
* - 2 serial ports
|
||||
* - 2 ethernet PHY
|
||||
* - 1 USB 1.1 device 0x50000000
|
||||
* - 1 LCD display 0x50100000
|
||||
* - 1 CPLD 0x50100000
|
||||
* - 1 I2C EEPROM
|
||||
* - 1 I2C thermal sensor
|
||||
* - a set of LEDs
|
||||
* - bit-bang SPI port using GPIOs
|
||||
* - 1 EBC interface connector 0 0x50200000
|
||||
* - 1 cardbus controller + expansion slot.
|
||||
* - 1 PCI expansion slot.
|
||||
/* PPC405EP reference board (IBM) */
|
||||
/*
|
||||
* Standalone board with:
|
||||
* - PowerPC 405EP CPU
|
||||
* - SDRAM (0x00000000)
|
||||
* - Flash (0xFFF80000)
|
||||
* - SRAM (0xFFF00000)
|
||||
* - NVRAM (0xF0000000)
|
||||
* - FPGA (0xF0300000)
|
||||
*/
|
||||
typedef struct taihu_cpld_t taihu_cpld_t;
|
||||
struct taihu_cpld_t {
|
||||
|
||||
#define PPC405EP_NVRAM_BASE 0xF0000000
|
||||
#define PPC405EP_FPGA_BASE 0xF0300000
|
||||
#define PPC405EP_FLASH_BASE 0xFFF80000
|
||||
|
||||
#define TYPE_REF405EP_FPGA "ref405ep-fpga"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ref405epFpgaState, REF405EP_FPGA);
|
||||
struct Ref405epFpgaState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint8_t reg0;
|
||||
uint8_t reg1;
|
||||
};
|
||||
|
||||
static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size)
|
||||
static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
Ref405epFpgaState *fpga = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
cpld = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
ret = cpld->reg0;
|
||||
ret = fpga->reg0;
|
||||
break;
|
||||
case 0x1:
|
||||
ret = cpld->reg1;
|
||||
ret = fpga->reg1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
|
@ -388,195 +418,113 @@ static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void taihu_cpld_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
Ref405epFpgaState *fpga = opaque;
|
||||
|
||||
cpld = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
/* Read only */
|
||||
break;
|
||||
case 0x1:
|
||||
cpld->reg1 = value;
|
||||
fpga->reg1 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps taihu_cpld_ops = {
|
||||
.read = taihu_cpld_read,
|
||||
.write = taihu_cpld_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
static const MemoryRegionOps ref405ep_fpga_ops = {
|
||||
.read = ref405ep_fpga_readb,
|
||||
.write = ref405ep_fpga_writeb,
|
||||
.impl.min_access_size = 1,
|
||||
.impl.max_access_size = 1,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static void taihu_cpld_reset (void *opaque)
|
||||
static void ref405ep_fpga_reset(DeviceState *dev)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
Ref405epFpgaState *fpga = REF405EP_FPGA(dev);
|
||||
|
||||
cpld = opaque;
|
||||
cpld->reg0 = 0x01;
|
||||
cpld->reg1 = 0x80;
|
||||
fpga->reg0 = 0x00;
|
||||
fpga->reg1 = 0x0F;
|
||||
}
|
||||
|
||||
static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base)
|
||||
static void ref405ep_fpga_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
MemoryRegion *cpld_memory = g_new(MemoryRegion, 1);
|
||||
Ref405epFpgaState *s = REF405EP_FPGA(dev);
|
||||
|
||||
cpld = g_new0(taihu_cpld_t, 1);
|
||||
memory_region_init_io(cpld_memory, NULL, &taihu_cpld_ops, cpld, "cpld", 0x100);
|
||||
memory_region_add_subregion(sysmem, base, cpld_memory);
|
||||
qemu_register_reset(&taihu_cpld_reset, cpld);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &ref405ep_fpga_ops, s,
|
||||
"fpga", 0x00000100);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||
}
|
||||
|
||||
static void taihu_405ep_init(MachineState *machine)
|
||||
static void ref405ep_fpga_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const char *bios_name = machine->firmware ?: BIOS_FILENAME;
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
char *filename;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *bios;
|
||||
MemoryRegion *ram_memories = g_new(MemoryRegion, 2);
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
long bios_size;
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
int linux_boot;
|
||||
int fl_idx;
|
||||
DriveInfo *dinfo;
|
||||
DeviceState *uicdev;
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
if (machine->ram_size != mc->default_ram_size) {
|
||||
char *sz = size_to_str(mc->default_ram_size);
|
||||
error_report("Invalid RAM size, should be %s", sz);
|
||||
g_free(sz);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ram_bases[0] = 0;
|
||||
ram_sizes[0] = 0x04000000;
|
||||
memory_region_init_alias(&ram_memories[0], NULL,
|
||||
"taihu_405ep.ram-0", machine->ram, ram_bases[0],
|
||||
ram_sizes[0]);
|
||||
ram_bases[1] = 0x04000000;
|
||||
ram_sizes[1] = 0x04000000;
|
||||
memory_region_init_alias(&ram_memories[1], NULL,
|
||||
"taihu_405ep.ram-1", machine->ram, ram_bases[1],
|
||||
ram_sizes[1]);
|
||||
ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &uicdev, kernel_filename == NULL ? 0 : 1);
|
||||
/* allocate and load BIOS */
|
||||
fl_idx = 0;
|
||||
#if defined(USE_FLASH_BIOS)
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
if (dinfo) {
|
||||
bios_size = 2 * MiB;
|
||||
pflash_cfi02_register(0xFFE00000,
|
||||
"taihu_405ep.bios", bios_size,
|
||||
blk_by_legacy_dinfo(dinfo),
|
||||
64 * KiB, 1,
|
||||
4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
fl_idx++;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
bios = g_new(MemoryRegion, 1);
|
||||
memory_region_init_rom(bios, NULL, "taihu_405ep.bios", BIOS_SIZE,
|
||||
&error_fatal);
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (filename) {
|
||||
bios_size = load_image_size(filename,
|
||||
memory_region_get_ram_ptr(bios),
|
||||
BIOS_SIZE);
|
||||
g_free(filename);
|
||||
if (bios_size < 0) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
} else if (!qtest_enabled()) {
|
||||
error_report("Could not load PowerPC BIOS '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/* Register Linux flash */
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
if (dinfo) {
|
||||
bios_size = 32 * MiB;
|
||||
pflash_cfi02_register(0xfc000000, "taihu_405ep.flash", bios_size,
|
||||
blk_by_legacy_dinfo(dinfo),
|
||||
64 * KiB, 1,
|
||||
4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
fl_idx++;
|
||||
}
|
||||
/* Register CLPD & LCD display */
|
||||
taihu_cpld_init(sysmem, 0x50100000);
|
||||
/* Load kernel */
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
if (linux_boot) {
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
machine->ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
machine->ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
error_report("could not load initial ram disk '%s'",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
dc->realize = ref405ep_fpga_realize;
|
||||
dc->reset = ref405ep_fpga_reset;
|
||||
/* Reason: only works as part of a ppc405 board */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static void taihu_class_init(ObjectClass *oc, void *data)
|
||||
static const TypeInfo ref405ep_fpga_type = {
|
||||
.name = TYPE_REF405EP_FPGA,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Ref405epFpgaState),
|
||||
.class_init = ref405ep_fpga_class_init,
|
||||
};
|
||||
|
||||
static void ref405ep_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
SysBusDevice *s;
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
|
||||
ppc405_init(machine);
|
||||
|
||||
/* allocate SRAM */
|
||||
memory_region_init_ram(sram, NULL, "ref405ep.sram", PPC405EP_SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(get_system_memory(), PPC405EP_SRAM_BASE, sram);
|
||||
|
||||
/* Register FPGA */
|
||||
dev = qdev_new(TYPE_REF405EP_FPGA);
|
||||
object_property_add_child(OBJECT(machine), "fpga", OBJECT(dev));
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, PPC405EP_FPGA_BASE);
|
||||
|
||||
/* Register NVRAM */
|
||||
dev = qdev_new("sysbus-m48t08");
|
||||
qdev_prop_set_int32(dev, "base-year", 1968);
|
||||
s = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
sysbus_mmio_map(s, 0, PPC405EP_NVRAM_BASE);
|
||||
}
|
||||
|
||||
static void ref405ep_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->desc = "taihu";
|
||||
mc->init = taihu_405ep_init;
|
||||
mc->default_ram_size = 0x08000000;
|
||||
mc->default_ram_id = "taihu_405ep.ram";
|
||||
mc->deprecation_reason = "incomplete, use 'ref405ep' instead";
|
||||
mc->desc = "ref405ep";
|
||||
mc->init = ref405ep_init;
|
||||
}
|
||||
|
||||
static const TypeInfo taihu_type = {
|
||||
.name = MACHINE_TYPE_NAME("taihu"),
|
||||
.parent = TYPE_MACHINE,
|
||||
.class_init = taihu_class_init,
|
||||
static const TypeInfo ref405ep_type = {
|
||||
.name = MACHINE_TYPE_NAME("ref405ep"),
|
||||
.parent = TYPE_PPC405_MACHINE,
|
||||
.class_init = ref405ep_class_init,
|
||||
};
|
||||
|
||||
static void ppc405_machine_init(void)
|
||||
{
|
||||
type_register_static(&ppc405_machine_type);
|
||||
type_register_static(&ref405ep_type);
|
||||
type_register_static(&taihu_type);
|
||||
type_register_static(&ref405ep_fpga_type);
|
||||
}
|
||||
|
||||
type_init(ppc405_machine_init)
|
||||
|
|
1156
hw/ppc/ppc405_uc.c
1156
hw/ppc/ppc405_uc.c
File diff suppressed because it is too large
Load Diff
|
@ -84,27 +84,30 @@ static int bamboo_load_device_tree(hwaddr addr,
|
|||
|
||||
ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property));
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set /memory/reg\n");
|
||||
|
||||
}
|
||||
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
||||
initrd_base);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
||||
|
||||
}
|
||||
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||
(initrd_base + initrd_size));
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
||||
|
||||
}
|
||||
ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
|
||||
kernel_cmdline);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
}
|
||||
|
||||
/* Copy data from the host device tree into the guest. Since the guest can
|
||||
/*
|
||||
* Copy data from the host device tree into the guest. Since the guest can
|
||||
* directly access the timebase without host involvement, we must expose
|
||||
* the correct frequencies. */
|
||||
* the correct frequencies.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
tb_freq = kvmppc_get_tbfreq();
|
||||
clock_freq = kvmppc_get_clockfreq();
|
||||
|
@ -193,12 +196,9 @@ static void bamboo_init(MachineState *machine)
|
|||
|
||||
/* interrupt controller */
|
||||
uicdev = qdev_new(TYPE_PPC_UIC);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uicdev), cpu, &error_fatal);
|
||||
object_unref(OBJECT(uicdev));
|
||||
uicsbd = SYS_BUS_DEVICE(uicdev);
|
||||
|
||||
object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu),
|
||||
&error_fatal);
|
||||
sysbus_realize_and_unref(uicsbd, &error_fatal);
|
||||
|
||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
|
||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
||||
|
@ -249,8 +249,10 @@ static void bamboo_init(MachineState *machine)
|
|||
if (pcibus) {
|
||||
/* Register network interfaces. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
/* There are no PCI NICs on the Bamboo board, but there are
|
||||
* PCI slots, so we can pick whatever default model we want. */
|
||||
/*
|
||||
* There are no PCI NICs on the Bamboo board, but there are
|
||||
* PCI slots, so we can pick whatever default model we want.
|
||||
*/
|
||||
pci_nic_init_nofail(&nd_table[i], pcibus, "e1000", NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1028,7 +1028,8 @@ void ppc4xx_dma_init(CPUPPCState *env, int dcr_base)
|
|||
|
||||
/*****************************************************************************/
|
||||
/* PCI Express controller */
|
||||
/* FIXME: This is not complete and does not work, only implemented partially
|
||||
/*
|
||||
* FIXME: This is not complete and does not work, only implemented partially
|
||||
* to allow firmware and guests to find an empty bus. Cards should use PCI.
|
||||
*/
|
||||
#include "hw/pci/pcie_host.h"
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "hw/irq.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
#include "hw/intc/ppc-uic.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
@ -37,38 +36,6 @@
|
|||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
static void ppc4xx_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Generic PowerPC 4xx processor instantiation */
|
||||
PowerPCCPU *ppc4xx_init(const char *cpu_type,
|
||||
clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
|
||||
uint32_t sysclk)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
|
||||
/* init CPUs */
|
||||
cpu = POWERPC_CPU(cpu_create(cpu_type));
|
||||
env = &cpu->env;
|
||||
|
||||
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
|
||||
cpu_clk->opaque = env;
|
||||
/* Set time-base frequency to sysclk */
|
||||
tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
|
||||
tb_clk->opaque = env;
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
/* Register qemu callbacks */
|
||||
qemu_register_reset(ppc4xx_reset, cpu);
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SDRAM controller */
|
||||
typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
|
||||
|
@ -98,12 +65,12 @@ enum {
|
|||
SDRAM0_CFGDATA = 0x011,
|
||||
};
|
||||
|
||||
/* XXX: TOFIX: some patches have made this code become inconsistent:
|
||||
/*
|
||||
* XXX: TOFIX: some patches have made this code become inconsistent:
|
||||
* there are type inconsistencies, mixing hwaddr, target_ulong
|
||||
* and uint32_t
|
||||
*/
|
||||
static uint32_t sdram_bcr (hwaddr ram_base,
|
||||
hwaddr ram_size)
|
||||
static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||
{
|
||||
uint32_t bcr;
|
||||
|
||||
|
@ -146,16 +113,17 @@ static inline hwaddr sdram_base(uint32_t bcr)
|
|||
return bcr & 0xFF800000;
|
||||
}
|
||||
|
||||
static target_ulong sdram_size (uint32_t bcr)
|
||||
static target_ulong sdram_size(uint32_t bcr)
|
||||
{
|
||||
target_ulong size;
|
||||
int sh;
|
||||
|
||||
sh = (bcr >> 17) & 0x7;
|
||||
if (sh == 7)
|
||||
if (sh == 7) {
|
||||
size = -1;
|
||||
else
|
||||
} else {
|
||||
size = (4 * MiB) << sh;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -175,7 +143,7 @@ static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i,
|
|||
}
|
||||
sdram->bcr[i] = bcr & 0xFFDEE001;
|
||||
if (enabled && (bcr & 0x00000001)) {
|
||||
trace_ppc4xx_sdram_unmap(sdram_base(bcr), sdram_size(bcr));
|
||||
trace_ppc4xx_sdram_map(sdram_base(bcr), sdram_size(bcr));
|
||||
memory_region_init(&sdram->containers[i], NULL, "sdram-containers",
|
||||
sdram_size(bcr));
|
||||
memory_region_add_subregion(&sdram->containers[i], 0,
|
||||
|
@ -186,7 +154,7 @@ static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i,
|
|||
}
|
||||
}
|
||||
|
||||
static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
|
||||
static void sdram_map_bcr(ppc4xx_sdram_t *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -200,7 +168,7 @@ static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
|
|||
}
|
||||
}
|
||||
|
||||
static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
|
||||
static void sdram_unmap_bcr(ppc4xx_sdram_t *sdram)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -212,7 +180,7 @@ static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t dcr_read_sdram (void *opaque, int dcrn)
|
||||
static uint32_t dcr_read_sdram(void *opaque, int dcrn)
|
||||
{
|
||||
ppc4xx_sdram_t *sdram;
|
||||
uint32_t ret;
|
||||
|
@ -280,7 +248,7 @@ static uint32_t dcr_read_sdram (void *opaque, int dcrn)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
||||
static void dcr_write_sdram(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
ppc4xx_sdram_t *sdram;
|
||||
|
||||
|
@ -313,10 +281,11 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
|||
sdram_unmap_bcr(sdram);
|
||||
sdram->status |= 0x80000000;
|
||||
}
|
||||
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
|
||||
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) {
|
||||
sdram->status |= 0x40000000;
|
||||
else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
|
||||
} else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) {
|
||||
sdram->status &= ~0x40000000;
|
||||
}
|
||||
sdram->cfg = val;
|
||||
break;
|
||||
case 0x24: /* SDRAM_STATUS */
|
||||
|
@ -348,10 +317,11 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
|||
break;
|
||||
case 0x98: /* SDRAM_ECCESR */
|
||||
val &= 0xFFF0F000;
|
||||
if (sdram->eccesr == 0 && val != 0)
|
||||
if (sdram->eccesr == 0 && val != 0) {
|
||||
qemu_irq_raise(sdram->irq);
|
||||
else if (sdram->eccesr != 0 && val == 0)
|
||||
} else if (sdram->eccesr != 0 && val == 0) {
|
||||
qemu_irq_lower(sdram->irq);
|
||||
}
|
||||
sdram->eccesr = val;
|
||||
break;
|
||||
default: /* Error */
|
||||
|
@ -361,7 +331,7 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
|||
}
|
||||
}
|
||||
|
||||
static void sdram_reset (void *opaque)
|
||||
static void sdram_reset(void *opaque)
|
||||
{
|
||||
ppc4xx_sdram_t *sdram;
|
||||
|
||||
|
@ -381,11 +351,11 @@ static void sdram_reset (void *opaque)
|
|||
sdram->cfg = 0x00800000;
|
||||
}
|
||||
|
||||
void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
|
||||
MemoryRegion *ram_memories,
|
||||
hwaddr *ram_bases,
|
||||
hwaddr *ram_sizes,
|
||||
int do_init)
|
||||
void ppc4xx_sdram_init(CPUPPCState *env, qemu_irq irq, int nbanks,
|
||||
MemoryRegion *ram_memories,
|
||||
hwaddr *ram_bases,
|
||||
hwaddr *ram_sizes,
|
||||
int do_init)
|
||||
{
|
||||
ppc4xx_sdram_t *sdram;
|
||||
|
||||
|
@ -404,8 +374,9 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
|
|||
sdram, &dcr_read_sdram, &dcr_write_sdram);
|
||||
ppc_dcr_register(env, SDRAM0_CFGDATA,
|
||||
sdram, &dcr_read_sdram, &dcr_write_sdram);
|
||||
if (do_init)
|
||||
if (do_init) {
|
||||
sdram_map_bcr(sdram);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -462,7 +433,7 @@ void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
|||
}
|
||||
error_report("at most %d bank%s of %s MiB each supported",
|
||||
nr_banks, nr_banks == 1 ? "" : "s", s->str);
|
||||
error_printf("Possible valid RAM size: %" PRIi64 " MiB \n",
|
||||
error_printf("Possible valid RAM size: %" PRIi64 " MiB\n",
|
||||
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
|
||||
|
||||
g_string_free(s, true);
|
||||
|
@ -491,32 +462,10 @@ enum {
|
|||
MAL0_RCBS1 = 0x1E1,
|
||||
};
|
||||
|
||||
typedef struct ppc4xx_mal_t ppc4xx_mal_t;
|
||||
struct ppc4xx_mal_t {
|
||||
qemu_irq irqs[4];
|
||||
uint32_t cfg;
|
||||
uint32_t esr;
|
||||
uint32_t ier;
|
||||
uint32_t txcasr;
|
||||
uint32_t txcarr;
|
||||
uint32_t txeobisr;
|
||||
uint32_t txdeir;
|
||||
uint32_t rxcasr;
|
||||
uint32_t rxcarr;
|
||||
uint32_t rxeobisr;
|
||||
uint32_t rxdeir;
|
||||
uint32_t *txctpr;
|
||||
uint32_t *rxctpr;
|
||||
uint32_t *rcbs;
|
||||
uint8_t txcnum;
|
||||
uint8_t rxcnum;
|
||||
};
|
||||
|
||||
static void ppc4xx_mal_reset(void *opaque)
|
||||
static void ppc4xx_mal_reset(DeviceState *dev)
|
||||
{
|
||||
ppc4xx_mal_t *mal;
|
||||
Ppc4xxMalState *mal = PPC4xx_MAL(dev);
|
||||
|
||||
mal = opaque;
|
||||
mal->cfg = 0x0007C000;
|
||||
mal->esr = 0x00000000;
|
||||
mal->ier = 0x00000000;
|
||||
|
@ -530,10 +479,9 @@ static void ppc4xx_mal_reset(void *opaque)
|
|||
|
||||
static uint32_t dcr_read_mal(void *opaque, int dcrn)
|
||||
{
|
||||
ppc4xx_mal_t *mal;
|
||||
Ppc4xxMalState *mal = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
mal = opaque;
|
||||
switch (dcrn) {
|
||||
case MAL0_CFG:
|
||||
ret = mal->cfg;
|
||||
|
@ -587,13 +535,12 @@ static uint32_t dcr_read_mal(void *opaque, int dcrn)
|
|||
|
||||
static void dcr_write_mal(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
ppc4xx_mal_t *mal;
|
||||
Ppc4xxMalState *mal = opaque;
|
||||
|
||||
mal = opaque;
|
||||
switch (dcrn) {
|
||||
case MAL0_CFG:
|
||||
if (val & 0x80000000) {
|
||||
ppc4xx_mal_reset(mal);
|
||||
ppc4xx_mal_reset(DEVICE(mal));
|
||||
}
|
||||
mal->cfg = val & 0x00FFC087;
|
||||
break;
|
||||
|
@ -644,55 +591,404 @@ static void dcr_write_mal(void *opaque, int dcrn, uint32_t val)
|
|||
}
|
||||
}
|
||||
|
||||
void ppc4xx_mal_init(CPUPPCState *env, uint8_t txcnum, uint8_t rxcnum,
|
||||
qemu_irq irqs[4])
|
||||
static void ppc4xx_mal_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ppc4xx_mal_t *mal;
|
||||
Ppc4xxMalState *mal = PPC4xx_MAL(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
int i;
|
||||
|
||||
assert(txcnum <= 32 && rxcnum <= 32);
|
||||
mal = g_malloc0(sizeof(*mal));
|
||||
mal->txcnum = txcnum;
|
||||
mal->rxcnum = rxcnum;
|
||||
mal->txctpr = g_new0(uint32_t, txcnum);
|
||||
mal->rxctpr = g_new0(uint32_t, rxcnum);
|
||||
mal->rcbs = g_new0(uint32_t, rxcnum);
|
||||
for (i = 0; i < 4; i++) {
|
||||
mal->irqs[i] = irqs[i];
|
||||
if (mal->txcnum > 32 || mal->rxcnum > 32) {
|
||||
error_setg(errp, "invalid TXC/RXC number");
|
||||
return;
|
||||
}
|
||||
qemu_register_reset(&ppc4xx_mal_reset, mal);
|
||||
ppc_dcr_register(env, MAL0_CFG,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_ESR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_IER,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_TXCASR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_TXCARR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_TXEOBISR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_TXDEIR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_RXCASR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_RXCARR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_RXEOBISR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc_dcr_register(env, MAL0_RXDEIR,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
for (i = 0; i < txcnum; i++) {
|
||||
ppc_dcr_register(env, MAL0_TXCTP0R + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
|
||||
mal->txctpr = g_new0(uint32_t, mal->txcnum);
|
||||
mal->rxctpr = g_new0(uint32_t, mal->rxcnum);
|
||||
mal->rcbs = g_new0(uint32_t, mal->rxcnum);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mal->irqs); i++) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &mal->irqs[i]);
|
||||
}
|
||||
for (i = 0; i < rxcnum; i++) {
|
||||
ppc_dcr_register(env, MAL0_RXCTP0R + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
|
||||
ppc4xx_dcr_register(dcr, MAL0_CFG, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_ESR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_IER, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_TXCASR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_TXCARR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_TXEOBISR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_TXDEIR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_RXCASR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_RXCARR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_RXEOBISR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
ppc4xx_dcr_register(dcr, MAL0_RXDEIR, mal, &dcr_read_mal, &dcr_write_mal);
|
||||
for (i = 0; i < mal->txcnum; i++) {
|
||||
ppc4xx_dcr_register(dcr, MAL0_TXCTP0R + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
}
|
||||
for (i = 0; i < rxcnum; i++) {
|
||||
ppc_dcr_register(env, MAL0_RCBS0 + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
for (i = 0; i < mal->rxcnum; i++) {
|
||||
ppc4xx_dcr_register(dcr, MAL0_RXCTP0R + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
}
|
||||
for (i = 0; i < mal->rxcnum; i++) {
|
||||
ppc4xx_dcr_register(dcr, MAL0_RCBS0 + i,
|
||||
mal, &dcr_read_mal, &dcr_write_mal);
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc4xx_mal_finalize(Object *obj)
|
||||
{
|
||||
Ppc4xxMalState *mal = PPC4xx_MAL(obj);
|
||||
|
||||
g_free(mal->rcbs);
|
||||
g_free(mal->rxctpr);
|
||||
g_free(mal->txctpr);
|
||||
}
|
||||
|
||||
static Property ppc4xx_mal_properties[] = {
|
||||
DEFINE_PROP_UINT8("txc-num", Ppc4xxMalState, txcnum, 0),
|
||||
DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_mal_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc4xx_mal_realize;
|
||||
dc->reset = ppc4xx_mal_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
device_class_set_props(dc, ppc4xx_mal_properties);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Peripheral local bus arbitrer */
|
||||
enum {
|
||||
PLB3A0_ACR = 0x077,
|
||||
PLB4A0_ACR = 0x081,
|
||||
PLB0_BESR = 0x084,
|
||||
PLB0_BEAR = 0x086,
|
||||
PLB0_ACR = 0x087,
|
||||
PLB4A1_ACR = 0x089,
|
||||
};
|
||||
|
||||
static uint32_t dcr_read_plb(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxPlbState *plb = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
switch (dcrn) {
|
||||
case PLB0_ACR:
|
||||
ret = plb->acr;
|
||||
break;
|
||||
case PLB0_BEAR:
|
||||
ret = plb->bear;
|
||||
break;
|
||||
case PLB0_BESR:
|
||||
ret = plb->besr;
|
||||
break;
|
||||
default:
|
||||
/* Avoid gcc warning */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dcr_write_plb(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxPlbState *plb = opaque;
|
||||
|
||||
switch (dcrn) {
|
||||
case PLB0_ACR:
|
||||
/*
|
||||
* We don't care about the actual parameters written as
|
||||
* we don't manage any priorities on the bus
|
||||
*/
|
||||
plb->acr = val & 0xF8000000;
|
||||
break;
|
||||
case PLB0_BEAR:
|
||||
/* Read only */
|
||||
break;
|
||||
case PLB0_BESR:
|
||||
/* Write-clear */
|
||||
plb->besr &= ~val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc405_plb_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxPlbState *plb = PPC4xx_PLB(dev);
|
||||
|
||||
plb->acr = 0x00000000;
|
||||
plb->bear = 0x00000000;
|
||||
plb->besr = 0x00000000;
|
||||
}
|
||||
|
||||
static void ppc405_plb_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxPlbState *plb = PPC4xx_PLB(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
|
||||
ppc4xx_dcr_register(dcr, PLB3A0_ACR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
ppc4xx_dcr_register(dcr, PLB4A0_ACR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
ppc4xx_dcr_register(dcr, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
ppc4xx_dcr_register(dcr, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
ppc4xx_dcr_register(dcr, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
ppc4xx_dcr_register(dcr, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb);
|
||||
}
|
||||
|
||||
static void ppc405_plb_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc405_plb_realize;
|
||||
dc->reset = ppc405_plb_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Peripheral controller */
|
||||
enum {
|
||||
EBC0_CFGADDR = 0x012,
|
||||
EBC0_CFGDATA = 0x013,
|
||||
};
|
||||
|
||||
static uint32_t dcr_read_ebc(void *opaque, int dcrn)
|
||||
{
|
||||
Ppc4xxEbcState *ebc = opaque;
|
||||
uint32_t ret;
|
||||
|
||||
switch (dcrn) {
|
||||
case EBC0_CFGADDR:
|
||||
ret = ebc->addr;
|
||||
break;
|
||||
case EBC0_CFGDATA:
|
||||
switch (ebc->addr) {
|
||||
case 0x00: /* B0CR */
|
||||
ret = ebc->bcr[0];
|
||||
break;
|
||||
case 0x01: /* B1CR */
|
||||
ret = ebc->bcr[1];
|
||||
break;
|
||||
case 0x02: /* B2CR */
|
||||
ret = ebc->bcr[2];
|
||||
break;
|
||||
case 0x03: /* B3CR */
|
||||
ret = ebc->bcr[3];
|
||||
break;
|
||||
case 0x04: /* B4CR */
|
||||
ret = ebc->bcr[4];
|
||||
break;
|
||||
case 0x05: /* B5CR */
|
||||
ret = ebc->bcr[5];
|
||||
break;
|
||||
case 0x06: /* B6CR */
|
||||
ret = ebc->bcr[6];
|
||||
break;
|
||||
case 0x07: /* B7CR */
|
||||
ret = ebc->bcr[7];
|
||||
break;
|
||||
case 0x10: /* B0AP */
|
||||
ret = ebc->bap[0];
|
||||
break;
|
||||
case 0x11: /* B1AP */
|
||||
ret = ebc->bap[1];
|
||||
break;
|
||||
case 0x12: /* B2AP */
|
||||
ret = ebc->bap[2];
|
||||
break;
|
||||
case 0x13: /* B3AP */
|
||||
ret = ebc->bap[3];
|
||||
break;
|
||||
case 0x14: /* B4AP */
|
||||
ret = ebc->bap[4];
|
||||
break;
|
||||
case 0x15: /* B5AP */
|
||||
ret = ebc->bap[5];
|
||||
break;
|
||||
case 0x16: /* B6AP */
|
||||
ret = ebc->bap[6];
|
||||
break;
|
||||
case 0x17: /* B7AP */
|
||||
ret = ebc->bap[7];
|
||||
break;
|
||||
case 0x20: /* BEAR */
|
||||
ret = ebc->bear;
|
||||
break;
|
||||
case 0x21: /* BESR0 */
|
||||
ret = ebc->besr0;
|
||||
break;
|
||||
case 0x22: /* BESR1 */
|
||||
ret = ebc->besr1;
|
||||
break;
|
||||
case 0x23: /* CFG */
|
||||
ret = ebc->cfg;
|
||||
break;
|
||||
default:
|
||||
ret = 0x00000000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = 0x00000000;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dcr_write_ebc(void *opaque, int dcrn, uint32_t val)
|
||||
{
|
||||
Ppc4xxEbcState *ebc = opaque;
|
||||
|
||||
switch (dcrn) {
|
||||
case EBC0_CFGADDR:
|
||||
ebc->addr = val;
|
||||
break;
|
||||
case EBC0_CFGDATA:
|
||||
switch (ebc->addr) {
|
||||
case 0x00: /* B0CR */
|
||||
break;
|
||||
case 0x01: /* B1CR */
|
||||
break;
|
||||
case 0x02: /* B2CR */
|
||||
break;
|
||||
case 0x03: /* B3CR */
|
||||
break;
|
||||
case 0x04: /* B4CR */
|
||||
break;
|
||||
case 0x05: /* B5CR */
|
||||
break;
|
||||
case 0x06: /* B6CR */
|
||||
break;
|
||||
case 0x07: /* B7CR */
|
||||
break;
|
||||
case 0x10: /* B0AP */
|
||||
break;
|
||||
case 0x11: /* B1AP */
|
||||
break;
|
||||
case 0x12: /* B2AP */
|
||||
break;
|
||||
case 0x13: /* B3AP */
|
||||
break;
|
||||
case 0x14: /* B4AP */
|
||||
break;
|
||||
case 0x15: /* B5AP */
|
||||
break;
|
||||
case 0x16: /* B6AP */
|
||||
break;
|
||||
case 0x17: /* B7AP */
|
||||
break;
|
||||
case 0x20: /* BEAR */
|
||||
break;
|
||||
case 0x21: /* BESR0 */
|
||||
break;
|
||||
case 0x22: /* BESR1 */
|
||||
break;
|
||||
case 0x23: /* CFG */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc405_ebc_reset(DeviceState *dev)
|
||||
{
|
||||
Ppc4xxEbcState *ebc = PPC4xx_EBC(dev);
|
||||
int i;
|
||||
|
||||
ebc->addr = 0x00000000;
|
||||
ebc->bap[0] = 0x7F8FFE80;
|
||||
ebc->bcr[0] = 0xFFE28000;
|
||||
for (i = 0; i < 8; i++) {
|
||||
ebc->bap[i] = 0x00000000;
|
||||
ebc->bcr[i] = 0x00000000;
|
||||
}
|
||||
ebc->besr0 = 0x00000000;
|
||||
ebc->besr1 = 0x00000000;
|
||||
ebc->cfg = 0x80400000;
|
||||
}
|
||||
|
||||
static void ppc405_ebc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
Ppc4xxEbcState *ebc = PPC4xx_EBC(dev);
|
||||
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||
|
||||
ppc4xx_dcr_register(dcr, EBC0_CFGADDR, ebc, &dcr_read_ebc, &dcr_write_ebc);
|
||||
ppc4xx_dcr_register(dcr, EBC0_CFGDATA, ebc, &dcr_read_ebc, &dcr_write_ebc);
|
||||
}
|
||||
|
||||
static void ppc405_ebc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = ppc405_ebc_realize;
|
||||
dc->reset = ppc405_ebc_reset;
|
||||
/* Reason: only works as function of a ppc4xx SoC */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
/* PPC4xx_DCR_DEVICE */
|
||||
|
||||
void ppc4xx_dcr_register(Ppc4xxDcrDeviceState *dev, int dcrn, void *opaque,
|
||||
dcr_read_cb dcr_read, dcr_write_cb dcr_write)
|
||||
{
|
||||
assert(dev->cpu);
|
||||
ppc_dcr_register(&dev->cpu->env, dcrn, opaque, dcr_read, dcr_write);
|
||||
}
|
||||
|
||||
bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu,
|
||||
Error **errp)
|
||||
{
|
||||
object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort);
|
||||
return sysbus_realize(SYS_BUS_DEVICE(dev), errp);
|
||||
}
|
||||
|
||||
static Property ppc4xx_dcr_properties[] = {
|
||||
DEFINE_PROP_LINK("cpu", Ppc4xxDcrDeviceState, cpu, TYPE_POWERPC_CPU,
|
||||
PowerPCCPU *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
device_class_set_props(dc, ppc4xx_dcr_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo ppc4xx_types[] = {
|
||||
{
|
||||
.name = TYPE_PPC4xx_MAL,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxMalState),
|
||||
.instance_finalize = ppc4xx_mal_finalize,
|
||||
.class_init = ppc4xx_mal_class_init,
|
||||
}, {
|
||||
.name = TYPE_PPC4xx_PLB,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxPlbState),
|
||||
.class_init = ppc405_plb_class_init,
|
||||
}, {
|
||||
.name = TYPE_PPC4xx_EBC,
|
||||
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxEbcState),
|
||||
.class_init = ppc405_ebc_class_init,
|
||||
}, {
|
||||
.name = TYPE_PPC4xx_DCR_DEVICE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(Ppc4xxDcrDeviceState),
|
||||
.class_init = ppc4xx_dcr_class_init,
|
||||
.abstract = true,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(ppc4xx_types)
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
|
||||
*/
|
||||
|
||||
/* This file implements emulation of the 32-bit PCI controller found in some
|
||||
* 4xx SoCs, such as the 440EP. */
|
||||
/*
|
||||
* This file implements emulation of the 32-bit PCI controller found in some
|
||||
* 4xx SoCs, such as the 440EP.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
|
@ -65,8 +67,10 @@ struct PPC4xxPCIState {
|
|||
#define PCIC0_CFGADDR 0x0
|
||||
#define PCIC0_CFGDATA 0x4
|
||||
|
||||
/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
|
||||
* PCI accesses. */
|
||||
/*
|
||||
* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
|
||||
* PCI accesses.
|
||||
*/
|
||||
#define PCIL0_PMM0LA 0x0
|
||||
#define PCIL0_PMM0MA 0x4
|
||||
#define PCIL0_PMM0PCILA 0x8
|
||||
|
@ -80,8 +84,10 @@ struct PPC4xxPCIState {
|
|||
#define PCIL0_PMM2PCILA 0x28
|
||||
#define PCIL0_PMM2PCIHA 0x2c
|
||||
|
||||
/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
|
||||
* PLB accesses. */
|
||||
/*
|
||||
* PCI Target Map (PTM) registers specify which PCI addresses are translated to
|
||||
* PLB accesses.
|
||||
*/
|
||||
#define PCIL0_PTM1MS 0x30
|
||||
#define PCIL0_PTM1LA 0x34
|
||||
#define PCIL0_PTM2MS 0x38
|
||||
|
@ -96,9 +102,10 @@ static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset,
|
|||
{
|
||||
struct PPC4xxPCIState *pci = opaque;
|
||||
|
||||
/* We ignore all target attempts at PCI configuration, effectively
|
||||
* assuming a bidirectional 1:1 mapping of PLB and PCI space. */
|
||||
|
||||
/*
|
||||
* We ignore all target attempts at PCI configuration, effectively
|
||||
* assuming a bidirectional 1:1 mapping of PLB and PCI space.
|
||||
*/
|
||||
switch (offset) {
|
||||
case PCIL0_PMM0LA:
|
||||
pci->pmm[0].la = value;
|
||||
|
@ -243,8 +250,10 @@ static void ppc4xx_pci_reset(void *opaque)
|
|||
memset(pci->ptm, 0, sizeof(pci->ptm));
|
||||
}
|
||||
|
||||
/* On Bamboo, all pins from each slot are tied to a single board IRQ. This
|
||||
* may need further refactoring for other boards. */
|
||||
/*
|
||||
* On Bamboo, all pins from each slot are tied to a single board IRQ.
|
||||
* This may need further refactoring for other boards.
|
||||
*/
|
||||
static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
int slot = PCI_SLOT(pci_dev->devfn);
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "elf.h"
|
||||
#include "exec/memory.h"
|
||||
#include "ppc440.h"
|
||||
#include "ppc405.h"
|
||||
#include "hw/block/flash.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/reset.h"
|
||||
|
@ -280,7 +279,6 @@ static void sam460ex_init(MachineState *machine)
|
|||
hwaddr ram_sizes[SDRAM_NR_BANKS] = {0};
|
||||
MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1);
|
||||
DeviceState *uic[4];
|
||||
qemu_irq mal_irqs[4];
|
||||
int i;
|
||||
PCIBus *pci_bus;
|
||||
PowerPCCPU *cpu;
|
||||
|
@ -309,11 +307,12 @@ static void sam460ex_init(MachineState *machine)
|
|||
ppc_dcr_init(env, NULL, NULL);
|
||||
|
||||
/* PLB arbitrer */
|
||||
ppc4xx_plb_init(env);
|
||||
dev = qdev_new(TYPE_PPC4xx_PLB);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal);
|
||||
object_unref(OBJECT(dev));
|
||||
|
||||
/* interrupt controllers */
|
||||
for (i = 0; i < ARRAY_SIZE(uic); i++) {
|
||||
SysBusDevice *sbd;
|
||||
/*
|
||||
* UICs 1, 2 and 3 are cascaded through UIC 0.
|
||||
* input_ints[n] is the interrupt number on UIC 0 which
|
||||
|
@ -325,22 +324,20 @@ static void sam460ex_init(MachineState *machine)
|
|||
const int input_ints[] = { -1, 30, 10, 16 };
|
||||
|
||||
uic[i] = qdev_new(TYPE_PPC_UIC);
|
||||
sbd = SYS_BUS_DEVICE(uic[i]);
|
||||
|
||||
qdev_prop_set_uint32(uic[i], "dcr-base", 0xc0 + i * 0x10);
|
||||
object_property_set_link(OBJECT(uic[i]), "cpu", OBJECT(cpu),
|
||||
&error_fatal);
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uic[i]), cpu, &error_fatal);
|
||||
object_unref(OBJECT(uic[i]));
|
||||
|
||||
sbdev = SYS_BUS_DEVICE(uic[i]);
|
||||
if (i == 0) {
|
||||
sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT,
|
||||
sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_INT,
|
||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
||||
sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT,
|
||||
sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_CINT,
|
||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT));
|
||||
} else {
|
||||
sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT,
|
||||
sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_INT,
|
||||
qdev_get_gpio_in(uic[0], input_ints[i]));
|
||||
sysbus_connect_irq(sbd, PPCUIC_OUTPUT_CINT,
|
||||
sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_CINT,
|
||||
qdev_get_gpio_in(uic[0], input_ints[i] + 1));
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +368,9 @@ static void sam460ex_init(MachineState *machine)
|
|||
qdev_get_gpio_in(uic[0], 3));
|
||||
|
||||
/* External bus controller */
|
||||
ppc405_ebc_init(env);
|
||||
dev = qdev_new(TYPE_PPC4xx_EBC);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal);
|
||||
object_unref(OBJECT(dev));
|
||||
|
||||
/* CPR */
|
||||
ppc4xx_cpr_init(env);
|
||||
|
@ -383,10 +382,15 @@ static void sam460ex_init(MachineState *machine)
|
|||
ppc4xx_sdr_init(env);
|
||||
|
||||
/* MAL */
|
||||
for (i = 0; i < ARRAY_SIZE(mal_irqs); i++) {
|
||||
mal_irqs[i] = qdev_get_gpio_in(uic[2], 3 + i);
|
||||
dev = qdev_new(TYPE_PPC4xx_MAL);
|
||||
qdev_prop_set_uint32(dev, "txc-num", 4);
|
||||
qdev_prop_set_uint32(dev, "rxc-num", 16);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(dev), cpu, &error_fatal);
|
||||
object_unref(OBJECT(dev));
|
||||
sbdev = SYS_BUS_DEVICE(dev);
|
||||
for (i = 0; i < ARRAY_SIZE(PPC4xx_MAL(dev)->irqs); i++) {
|
||||
sysbus_connect_irq(sbdev, i, qdev_get_gpio_in(uic[2], 3 + i));
|
||||
}
|
||||
ppc4xx_mal_init(env, 4, 16, mal_irqs);
|
||||
|
||||
/* DMA */
|
||||
ppc4xx_dma_init(env, 0x200);
|
||||
|
|
|
@ -95,6 +95,17 @@ 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_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
|
||||
pnv_sbe_xscom_mbox_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64
|
||||
pnv_sbe_xscom_mbox_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64
|
||||
pnv_sbe_reg_set_host_doorbell(uint64_t val) "val 0x%" PRIx64
|
||||
pnv_sbe_cmd_timer_start(uint64_t ns) "ns 0x%" PRIu64
|
||||
pnv_sbe_cmd_timer_stop(void) ""
|
||||
pnv_sbe_cmd_timer_expired(void) ""
|
||||
pnv_sbe_msg_recv(uint16_t cmd, uint16_t seq, uint16_t ctrl_flags) "cmd 0x%" PRIx16 " seq %"PRIu16 " ctrl_flags 0x%" PRIx16
|
||||
|
||||
# ppc.c
|
||||
ppc_tb_adjust(uint64_t offs1, uint64_t offs2, int64_t diff, int64_t seconds) "adjusted from 0x%"PRIx64" to 0x%"PRIx64", diff %"PRId64" (%"PRId64"s)"
|
||||
ppc_tb_load(uint64_t tb) "tb 0x%016" PRIx64
|
||||
|
@ -150,11 +161,9 @@ ppc440_pcix_reg_write(uint64_t addr, uint32_t val, uint32_t size) "addr 0x%" PRI
|
|||
# ppc405_boards.c
|
||||
opba_readb(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32
|
||||
opba_writeb(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64
|
||||
opba_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ppc405_gpio_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d"
|
||||
ppc405_gpio_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64
|
||||
ppc405_gpio_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ocm_update_mappings(uint32_t isarc, uint32_t isacntl, uint32_t dsarc, uint32_t dsacntl, uint32_t ocm_isarc, uint32_t ocm_isacntl, uint32_t ocm_dsarc, uint32_t ocm_dsacntl) "OCM update ISA 0x%08" PRIx32 " 0x%08" PRIx32 " (0x%08" PRIx32" 0x%08" PRIx32 ") DSA 0x%08" PRIx32 " 0x%08" PRIx32" (0x%08" PRIx32 " 0x%08" PRIx32 ")"
|
||||
ocm_map(const char* prefix, uint32_t isarc) "OCM map %s 0x%08" PRIx32
|
||||
|
@ -162,7 +171,6 @@ ocm_unmap(const char* prefix, uint32_t isarc) "OCM unmap %s 0x%08" PRIx32
|
|||
|
||||
ppc4xx_gpt_read(uint64_t addr, uint32_t size) "addr 0x%" PRIx64 " size %d"
|
||||
ppc4xx_gpt_write(uint64_t addr, uint32_t size, uint64_t val) "addr 0x%" PRIx64 " size %d = 0x%" PRIx64
|
||||
ppc4xx_gpt_init(uint64_t addr) "offet 0x%" PRIx64
|
||||
|
||||
ppc405ep_clocks_compute(const char *param, uint32_t param2, uint32_t val) "%s 0x%1" PRIx32 " %d"
|
||||
ppc405ep_clocks_setup(const char *trace) "%s"
|
||||
|
|
|
@ -104,12 +104,9 @@ static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk)
|
|||
|
||||
/* interrupt controller */
|
||||
uicdev = qdev_new(TYPE_PPC_UIC);
|
||||
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uicdev), cpu, &error_fatal);
|
||||
object_unref(OBJECT(uicdev));
|
||||
uicsbd = SYS_BUS_DEVICE(uicdev);
|
||||
|
||||
object_property_set_link(OBJECT(uicdev), "cpu", OBJECT(cpu),
|
||||
&error_fatal);
|
||||
sysbus_realize_and_unref(uicsbd, &error_fatal);
|
||||
|
||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
|
||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
||||
|
|
|
@ -195,6 +195,10 @@ typedef struct float_status {
|
|||
bool snan_bit_is_one;
|
||||
bool use_first_nan;
|
||||
bool no_signaling_nans;
|
||||
/* should overflowed results subtract re_bias to its exponent? */
|
||||
bool rebias_overflow;
|
||||
/* should underflowed results add re_bias to its exponent? */
|
||||
bool rebias_underflow;
|
||||
} float_status;
|
||||
|
||||
#endif /* SOFTFLOAT_TYPES_H */
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
#ifndef HW_INTC_PPC_UIC_H
|
||||
#define HW_INTC_PPC_UIC_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/ppc/ppc4xx.h"
|
||||
|
||||
#define TYPE_PPC_UIC "ppc-uic"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC)
|
||||
|
@ -56,14 +55,13 @@ enum {
|
|||
|
||||
struct PPCUIC {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
qemu_irq output_int;
|
||||
qemu_irq output_cint;
|
||||
|
||||
/* properties */
|
||||
CPUState *cpu;
|
||||
uint32_t dcr_base;
|
||||
bool use_vectors;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/pci-host/pnv_phb.h"
|
||||
|
||||
typedef struct PnvPHB3 PnvPHB3;
|
||||
typedef struct PnvChip PnvChip;
|
||||
|
@ -103,15 +104,16 @@ struct PnvPBCQState {
|
|||
};
|
||||
|
||||
/*
|
||||
* PHB3 PCIe Root port
|
||||
* PHB3 PCIe Root Bus
|
||||
*/
|
||||
#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root"
|
||||
struct PnvPHB3RootBus {
|
||||
PCIBus parent;
|
||||
|
||||
#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port"
|
||||
|
||||
typedef struct PnvPHB3RootPort {
|
||||
PCIESlot parent_obj;
|
||||
} PnvPHB3RootPort;
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
};
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB3RootBus, PNV_PHB3_ROOT_BUS)
|
||||
|
||||
/*
|
||||
* PHB3 PCIe Host Bridge for PowerNV machines (POWER8)
|
||||
|
@ -127,7 +129,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB3, PNV_PHB3)
|
|||
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||
|
||||
struct PnvPHB3 {
|
||||
PCIExpressHost parent_obj;
|
||||
DeviceState parent;
|
||||
|
||||
PnvPHB *phb_base;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
|
@ -164,5 +168,6 @@ uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size);
|
|||
void pnv_phb3_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size);
|
||||
void pnv_phb3_update_regions(PnvPHB3 *phb);
|
||||
void pnv_phb3_remap_irqs(PnvPHB3 *phb);
|
||||
void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb);
|
||||
|
||||
#endif /* PCI_HOST_PNV_PHB3_H */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
||||
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
|
||||
typedef struct PnvPHB4 PnvPHB4;
|
||||
typedef struct PnvPHB PnvPHB;
|
||||
typedef struct PnvChip PnvChip;
|
||||
|
||||
/*
|
||||
|
@ -45,15 +46,16 @@ typedef struct PnvPhb4DMASpace {
|
|||
} PnvPhb4DMASpace;
|
||||
|
||||
/*
|
||||
* PHB4 PCIe Root port
|
||||
* PHB4 PCIe Root Bus
|
||||
*/
|
||||
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root"
|
||||
#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port"
|
||||
#define TYPE_PNV_PHB5_ROOT_PORT "pnv-phb5-root-port"
|
||||
struct PnvPHB4RootBus {
|
||||
PCIBus parent;
|
||||
|
||||
typedef struct PnvPHB4RootPort {
|
||||
PCIESlot parent_obj;
|
||||
} PnvPHB4RootPort;
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
};
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4RootBus, PNV_PHB4_ROOT_BUS)
|
||||
|
||||
/*
|
||||
* PHB4 PCIe Host Bridge for PowerNV machines (POWER9)
|
||||
|
@ -78,13 +80,13 @@ OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4, PNV_PHB4)
|
|||
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||
|
||||
struct PnvPHB4 {
|
||||
PCIExpressHost parent_obj;
|
||||
DeviceState parent;
|
||||
|
||||
PnvPHB *phb_base;
|
||||
|
||||
uint32_t chip_id;
|
||||
uint32_t phb_id;
|
||||
|
||||
uint64_t version;
|
||||
|
||||
/* The owner PEC */
|
||||
PnvPhb4PecState *pec;
|
||||
|
||||
|
@ -157,6 +159,7 @@ struct PnvPHB4 {
|
|||
|
||||
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
|
||||
int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index);
|
||||
void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb);
|
||||
extern const MemoryRegionOps pnv_phb4_xscom_ops;
|
||||
|
||||
/*
|
||||
|
@ -205,7 +208,6 @@ struct PnvPhb4PecClass {
|
|||
uint64_t version;
|
||||
const char *phb_type;
|
||||
const uint32_t *num_phbs;
|
||||
const char *rp_model;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,11 +27,13 @@
|
|||
#include "hw/ppc/pnv_pnor.h"
|
||||
#include "hw/ppc/pnv_psi.h"
|
||||
#include "hw/ppc/pnv_occ.h"
|
||||
#include "hw/ppc/pnv_sbe.h"
|
||||
#include "hw/ppc/pnv_homer.h"
|
||||
#include "hw/ppc/pnv_xive.h"
|
||||
#include "hw/ppc/pnv_core.h"
|
||||
#include "hw/pci-host/pnv_phb3.h"
|
||||
#include "hw/pci-host/pnv_phb4.h"
|
||||
#include "hw/pci-host/pnv_phb.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_PNV_CHIP "pnv-chip"
|
||||
|
@ -80,7 +82,11 @@ struct Pnv8Chip {
|
|||
PnvHomer homer;
|
||||
|
||||
#define PNV8_CHIP_PHB3_MAX 4
|
||||
PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX];
|
||||
/*
|
||||
* The array is used to allow quick access to the phbs by
|
||||
* pnv_ics_get_child() and pnv_ics_resend_child().
|
||||
*/
|
||||
PnvPHB *phbs[PNV8_CHIP_PHB3_MAX];
|
||||
uint32_t num_phbs;
|
||||
|
||||
XICSFabric *xics;
|
||||
|
@ -100,6 +106,7 @@ struct Pnv9Chip {
|
|||
Pnv9Psi psi;
|
||||
PnvLpcController lpc;
|
||||
PnvOCC occ;
|
||||
PnvSBE sbe;
|
||||
PnvHomer homer;
|
||||
|
||||
uint32_t nr_quads;
|
||||
|
@ -129,6 +136,7 @@ struct Pnv10Chip {
|
|||
Pnv9Psi psi;
|
||||
PnvLpcController lpc;
|
||||
PnvOCC occ;
|
||||
PnvSBE sbe;
|
||||
PnvHomer homer;
|
||||
|
||||
uint32_t nr_quads;
|
||||
|
@ -189,8 +197,6 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10,
|
|||
TYPE_PNV_CHIP_POWER10)
|
||||
|
||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
|
||||
void pnv_phb_attach_root_port(PCIHostState *pci, const char *name,
|
||||
int index, int chip_id);
|
||||
|
||||
#define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv")
|
||||
typedef struct PnvMachineClass PnvMachineClass;
|
||||
|
@ -232,6 +238,7 @@ struct PnvMachineState {
|
|||
};
|
||||
|
||||
PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id);
|
||||
Object *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp);
|
||||
|
||||
#define PNV_FDT_ADDR 0x01000000
|
||||
#define PNV_TIMEBASE_FREQ 512000000ULL
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* QEMU PowerPC PowerNV Emulation of some SBE behaviour
|
||||
*
|
||||
* Copyright (c) 2022, IBM Corporation.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PPC_PNV_SBE_H
|
||||
#define PPC_PNV_SBE_H
|
||||
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_PNV_SBE "pnv-sbe"
|
||||
OBJECT_DECLARE_TYPE(PnvSBE, PnvSBEClass, PNV_SBE)
|
||||
#define TYPE_PNV9_SBE TYPE_PNV_SBE "-POWER9"
|
||||
DECLARE_INSTANCE_CHECKER(PnvSBE, PNV9_SBE, TYPE_PNV9_SBE)
|
||||
#define TYPE_PNV10_SBE TYPE_PNV_SBE "-POWER10"
|
||||
DECLARE_INSTANCE_CHECKER(PnvSBE, PNV10_SBE, TYPE_PNV10_SBE)
|
||||
|
||||
struct PnvSBE {
|
||||
DeviceState xd;
|
||||
|
||||
uint64_t mbox[8];
|
||||
uint64_t sbe_doorbell;
|
||||
uint64_t host_doorbell;
|
||||
|
||||
qemu_irq psi_irq;
|
||||
QEMUTimer *timer;
|
||||
|
||||
MemoryRegion xscom_mbox_regs;
|
||||
MemoryRegion xscom_ctrl_regs;
|
||||
};
|
||||
|
||||
struct PnvSBEClass {
|
||||
DeviceClass parent_class;
|
||||
|
||||
int xscom_ctrl_size;
|
||||
int xscom_mbox_size;
|
||||
const MemoryRegionOps *xscom_ctrl_ops;
|
||||
const MemoryRegionOps *xscom_mbox_ops;
|
||||
};
|
||||
|
||||
#endif /* PPC_PNV_SBE_H */
|
|
@ -92,6 +92,12 @@ struct PnvXScomInterfaceClass {
|
|||
#define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE
|
||||
#define PNV9_XSCOM_OCC_SIZE 0x8000
|
||||
|
||||
#define PNV9_XSCOM_SBE_CTRL_BASE 0x00050008
|
||||
#define PNV9_XSCOM_SBE_CTRL_SIZE 0x1
|
||||
|
||||
#define PNV9_XSCOM_SBE_MBOX_BASE 0x000D0050
|
||||
#define PNV9_XSCOM_SBE_MBOX_SIZE 0x16
|
||||
|
||||
#define PNV9_XSCOM_PBA_BASE 0x5012b00
|
||||
#define PNV9_XSCOM_PBA_SIZE 0x40
|
||||
|
||||
|
@ -134,6 +140,12 @@ struct PnvXScomInterfaceClass {
|
|||
#define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE
|
||||
#define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE
|
||||
|
||||
#define PNV10_XSCOM_SBE_CTRL_BASE PNV9_XSCOM_SBE_CTRL_BASE
|
||||
#define PNV10_XSCOM_SBE_CTRL_SIZE PNV9_XSCOM_SBE_CTRL_SIZE
|
||||
|
||||
#define PNV10_XSCOM_SBE_MBOX_BASE PNV9_XSCOM_SBE_MBOX_BASE
|
||||
#define PNV10_XSCOM_SBE_MBOX_SIZE PNV9_XSCOM_SBE_MBOX_SIZE
|
||||
|
||||
#define PNV10_XSCOM_PBA_BASE 0x01010CDA
|
||||
#define PNV10_XSCOM_PBA_SIZE 0x40
|
||||
|
||||
|
|
|
@ -27,11 +27,7 @@
|
|||
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "exec/memory.h"
|
||||
|
||||
/* PowerPC 4xx core initialization */
|
||||
PowerPCCPU *ppc4xx_init(const char *cpu_model,
|
||||
clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
|
||||
uint32_t sysclk);
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
||||
MemoryRegion ram_memories[],
|
||||
|
@ -44,9 +40,73 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
|
|||
hwaddr *ram_sizes,
|
||||
int do_init);
|
||||
|
||||
void ppc4xx_mal_init(CPUPPCState *env, uint8_t txcnum, uint8_t rxcnum,
|
||||
qemu_irq irqs[4]);
|
||||
|
||||
#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost"
|
||||
|
||||
/*
|
||||
* Generic DCR device
|
||||
*/
|
||||
#define TYPE_PPC4xx_DCR_DEVICE "ppc4xx-dcr-device"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxDcrDeviceState, PPC4xx_DCR_DEVICE);
|
||||
struct Ppc4xxDcrDeviceState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
PowerPCCPU *cpu;
|
||||
};
|
||||
|
||||
void ppc4xx_dcr_register(Ppc4xxDcrDeviceState *dev, int dcrn, void *opaque,
|
||||
dcr_read_cb dcr_read, dcr_write_cb dcr_write);
|
||||
bool ppc4xx_dcr_realize(Ppc4xxDcrDeviceState *dev, PowerPCCPU *cpu,
|
||||
Error **errp);
|
||||
|
||||
/* Memory Access Layer (MAL) */
|
||||
#define TYPE_PPC4xx_MAL "ppc4xx-mal"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxMalState, PPC4xx_MAL);
|
||||
struct Ppc4xxMalState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
qemu_irq irqs[4];
|
||||
uint32_t cfg;
|
||||
uint32_t esr;
|
||||
uint32_t ier;
|
||||
uint32_t txcasr;
|
||||
uint32_t txcarr;
|
||||
uint32_t txeobisr;
|
||||
uint32_t txdeir;
|
||||
uint32_t rxcasr;
|
||||
uint32_t rxcarr;
|
||||
uint32_t rxeobisr;
|
||||
uint32_t rxdeir;
|
||||
uint32_t *txctpr;
|
||||
uint32_t *rxctpr;
|
||||
uint32_t *rcbs;
|
||||
uint8_t txcnum;
|
||||
uint8_t rxcnum;
|
||||
};
|
||||
|
||||
/* Peripheral local bus arbitrer */
|
||||
#define TYPE_PPC4xx_PLB "ppc4xx-plb"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxPlbState, PPC4xx_PLB);
|
||||
struct Ppc4xxPlbState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
uint32_t acr;
|
||||
uint32_t bear;
|
||||
uint32_t besr;
|
||||
};
|
||||
|
||||
/* Peripheral controller */
|
||||
#define TYPE_PPC4xx_EBC "ppc4xx-ebc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Ppc4xxEbcState, PPC4xx_EBC);
|
||||
struct Ppc4xxEbcState {
|
||||
Ppc4xxDcrDeviceState parent_obj;
|
||||
|
||||
uint32_t addr;
|
||||
uint32_t bcr[8];
|
||||
uint32_t bap[8];
|
||||
uint32_t bear;
|
||||
uint32_t besr0;
|
||||
uint32_t besr1;
|
||||
uint32_t cfg;
|
||||
};
|
||||
|
||||
#endif /* PPC4XX_H */
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||
implementation for certain IBM POWER hardware. The sources are at
|
||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
||||
built from git tag qemu-slof-20220110.
|
||||
built from git tag qemu-slof-20220719.
|
||||
|
||||
- VOF (Virtual Open Firmware) is a minimalistic firmware to work with
|
||||
-machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and
|
||||
|
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
|
@ -1 +1 @@
|
|||
Subproject commit 5b4c5acdcd552a4e1796aeca6bb700f6cbb0282d
|
||||
Subproject commit 6b6c16b4b40763507cf1f518096f3c3883c5cf2d
|
|
@ -158,7 +158,11 @@ struct PowerPCCPUClass {
|
|||
void (*parent_parse_features)(const char *type, char *str, Error **errp);
|
||||
|
||||
uint32_t pvr;
|
||||
bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr);
|
||||
/*
|
||||
* If @best is false, match if pcc is in the family of pvr
|
||||
* Else match only if pcc is the best match for pvr in this family.
|
||||
*/
|
||||
bool (*pvr_match)(struct PowerPCCPUClass *pcc, uint32_t pvr, bool best);
|
||||
uint64_t pcr_mask; /* Available bits in PCR register */
|
||||
uint64_t pcr_supported; /* Bits for supported PowerISA versions */
|
||||
uint32_t svr;
|
||||
|
|
|
@ -120,6 +120,8 @@ void ppc_store_fpscr(CPUPPCState *env, target_ulong val)
|
|||
val |= FP_FEX;
|
||||
}
|
||||
env->fpscr = val;
|
||||
env->fp_status.rebias_overflow = (FP_OE & env->fpscr) ? true : false;
|
||||
env->fp_status.rebias_underflow = (FP_UE & env->fpscr) ? true : false;
|
||||
if (tcg_enabled()) {
|
||||
fpscr_set_rounding_mode(env);
|
||||
}
|
||||
|
|
|
@ -5912,15 +5912,25 @@ static void init_proc_POWER7(CPUPPCState *env)
|
|||
ppcPOWER7_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
||||
{
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7P_BASE) {
|
||||
return true;
|
||||
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
|
||||
if (!best) {
|
||||
if (base == CPU_POWERPC_POWER7_BASE) {
|
||||
return true;
|
||||
}
|
||||
if (base == CPU_POWERPC_POWER7P_BASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER7_BASE) {
|
||||
return true;
|
||||
|
||||
if (base != pcc_base) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER7(CPUState *cs)
|
||||
|
@ -6073,18 +6083,27 @@ static void init_proc_POWER8(CPUPPCState *env)
|
|||
ppcPOWER7_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
||||
{
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8NVL_BASE) {
|
||||
return true;
|
||||
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
|
||||
if (!best) {
|
||||
if (base == CPU_POWERPC_POWER8_BASE) {
|
||||
return true;
|
||||
}
|
||||
if (base == CPU_POWERPC_POWER8E_BASE) {
|
||||
return true;
|
||||
}
|
||||
if (base == CPU_POWERPC_POWER8NVL_BASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8E_BASE) {
|
||||
return true;
|
||||
if (base != pcc_base) {
|
||||
return false;
|
||||
}
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8_BASE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cpu_has_work_POWER8(CPUState *cs)
|
||||
|
@ -6282,11 +6301,26 @@ static void init_proc_POWER9(CPUPPCState *env)
|
|||
ppcPOWER9_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
||||
{
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER9_BASE) {
|
||||
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
|
||||
if (!best) {
|
||||
if (base == CPU_POWERPC_POWER9_BASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (base != pcc_base) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) {
|
||||
/* Major DD version matches to power9_v1.0 and power9_v2.0 */
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6499,11 +6533,26 @@ static void init_proc_POWER10(CPUPPCState *env)
|
|||
ppcPOWER9_irq_init(env_archcpu(env));
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
||||
{
|
||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER10_BASE) {
|
||||
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||
|
||||
if (!best) {
|
||||
if (base == CPU_POWERPC_POWER10_BASE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (base != pcc_base) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) {
|
||||
/* Major DD version matches to power10_v1.0 and power10_v2.0 */
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6910,7 +6959,7 @@ static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (pcc->pvr_match(pcc, pvr)) {
|
||||
if (pcc->pvr_match(pcc, pvr, true)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -7308,7 +7357,7 @@ static void ppc_cpu_instance_finalize(Object *obj)
|
|||
ppc_hash64_finalize(cpu);
|
||||
}
|
||||
|
||||
static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr, bool best)
|
||||
{
|
||||
return pcc->pvr == pvr;
|
||||
}
|
||||
|
|
|
@ -348,7 +348,6 @@ static inline int float_overflow_excp(CPUPPCState *env)
|
|||
|
||||
bool overflow_enabled = !!(env->fpscr & FP_OE);
|
||||
if (overflow_enabled) {
|
||||
/* XXX: should adjust the result */
|
||||
/* Update the floating-point enabled exception summary */
|
||||
env->fpscr |= FP_FEX;
|
||||
/* We must update the target FPR before raising the exception */
|
||||
|
@ -367,7 +366,6 @@ static inline void float_underflow_excp(CPUPPCState *env)
|
|||
/* Update the floating-point exception summary */
|
||||
env->fpscr |= FP_FX;
|
||||
if (env->fpscr & FP_UE) {
|
||||
/* XXX: should adjust the result */
|
||||
/* Update the floating-point enabled exception summary */
|
||||
env->fpscr |= FP_FEX;
|
||||
/* We must update the target FPR before raising the exception */
|
||||
|
|
|
@ -234,7 +234,7 @@ static bool pvr_match(PowerPCCPU *cpu, uint32_t pvr)
|
|||
if (pvr == pcc->pvr) {
|
||||
return true;
|
||||
}
|
||||
return pcc->pvr_match(pcc, pvr);
|
||||
return pcc->pvr_match(pcc, pvr, true);
|
||||
}
|
||||
|
||||
static int cpu_post_load(void *opaque, int version_id)
|
||||
|
|
Loading…
Reference in New Issue