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
|
PowerPC Machines
|
||||||
----------------
|
----------------
|
||||||
405 (ref405ep and taihu)
|
405 (ref405ep)
|
||||||
L: qemu-ppc@nongnu.org
|
L: qemu-ppc@nongnu.org
|
||||||
S: Orphan
|
S: Orphan
|
||||||
F: hw/ppc/ppc405_boards.c
|
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
|
better reflects the way this property affects all random data within
|
||||||
the device tree blob, not just the ``kaslr-seed`` node.
|
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)
|
``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
|
This machine was removed because it was unused. Alternative AST2500 based
|
||||||
OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``.
|
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
|
linux-user mode CPUs
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -6,5 +6,4 @@ Embedded family boards
|
|||||||
- ``ppce500`` generic paravirt e500 platform
|
- ``ppce500`` generic paravirt e500 platform
|
||||||
- ``ref405ep`` ref405ep
|
- ``ref405ep`` ref405ep
|
||||||
- ``sam460ex`` aCube Sam460ex
|
- ``sam460ex`` aCube Sam460ex
|
||||||
- ``taihu`` taihu
|
|
||||||
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
|
- ``virtex-ml507`` Xilinx Virtex ML507 reference design
|
||||||
|
@ -62,7 +62,7 @@ Booting via ``-kernel`` supports the following:
|
|||||||
+-------------------+-------------------+------------------+
|
+-------------------+-------------------+------------------+
|
||||||
| vmlinux LE | ✓ | ✓ |
|
| vmlinux LE | ✓ | ✓ |
|
||||||
+-------------------+-------------------+------------------+
|
+-------------------+-------------------+------------------+
|
||||||
| zImage.pseries BE | x | ✓¹ |
|
| zImage.pseries BE | ✓¹ | ✓¹ |
|
||||||
+-------------------+-------------------+------------------+
|
+-------------------+-------------------+------------------+
|
||||||
| zImage.pseries LE | ✓ | ✓ |
|
| zImage.pseries LE | ✓ | ✓ |
|
||||||
+-------------------+-------------------+------------------+
|
+-------------------+-------------------+------------------+
|
||||||
|
@ -214,18 +214,35 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
|
|||||||
p->frac_lo &= ~round_mask;
|
p->frac_lo &= ~round_mask;
|
||||||
}
|
}
|
||||||
} else if (unlikely(exp >= exp_max)) {
|
} else if (unlikely(exp >= exp_max)) {
|
||||||
flags |= float_flag_overflow | float_flag_inexact;
|
flags |= float_flag_overflow;
|
||||||
if (overflow_norm) {
|
if (s->rebias_overflow) {
|
||||||
|
exp -= fmt->exp_re_bias;
|
||||||
|
} else if (overflow_norm) {
|
||||||
|
flags |= float_flag_inexact;
|
||||||
exp = exp_max - 1;
|
exp = exp_max - 1;
|
||||||
frac_allones(p);
|
frac_allones(p);
|
||||||
p->frac_lo &= ~round_mask;
|
p->frac_lo &= ~round_mask;
|
||||||
} else {
|
} else {
|
||||||
|
flags |= float_flag_inexact;
|
||||||
p->cls = float_class_inf;
|
p->cls = float_class_inf;
|
||||||
exp = exp_max;
|
exp = exp_max;
|
||||||
frac_clear(p);
|
frac_clear(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frac_shr(p, frac_shift);
|
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) {
|
} else if (s->flush_to_zero) {
|
||||||
flags |= float_flag_output_denormal;
|
flags |= float_flag_output_denormal;
|
||||||
p->cls = float_class_zero;
|
p->cls = float_class_zero;
|
||||||
|
@ -521,6 +521,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int exp_size;
|
int exp_size;
|
||||||
int exp_bias;
|
int exp_bias;
|
||||||
|
int exp_re_bias;
|
||||||
int exp_max;
|
int exp_max;
|
||||||
int frac_size;
|
int frac_size;
|
||||||
int frac_shift;
|
int frac_shift;
|
||||||
@ -532,6 +533,7 @@ typedef struct {
|
|||||||
#define FLOAT_PARAMS_(E) \
|
#define FLOAT_PARAMS_(E) \
|
||||||
.exp_size = E, \
|
.exp_size = E, \
|
||||||
.exp_bias = ((1 << E) - 1) >> 1, \
|
.exp_bias = ((1 << E) - 1) >> 1, \
|
||||||
|
.exp_re_bias = (1 << (E - 1)) + (1 << (E - 2)), \
|
||||||
.exp_max = (1 << E) - 1
|
.exp_max = (1 << E) - 1
|
||||||
|
|
||||||
#define FLOAT_PARAMS(E, F) \
|
#define FLOAT_PARAMS(E, F) \
|
||||||
|
@ -25,11 +25,8 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "hw/intc/ppc-uic.h"
|
#include "hw/intc/ppc-uic.h"
|
||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
#include "cpu.h"
|
|
||||||
#include "hw/ppc/ppc.h"
|
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
#include "qapi/error.h"
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DCR_UICSR = 0x000,
|
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)
|
static void ppcuic_set_irq(void *opaque, int irq_num, int level)
|
||||||
{
|
{
|
||||||
PPCUIC *uic;
|
PPCUIC *uic = opaque;
|
||||||
uint32_t mask, sr;
|
uint32_t mask, sr;
|
||||||
|
|
||||||
uic = opaque;
|
|
||||||
mask = 1U << (31 - irq_num);
|
mask = 1U << (31 - irq_num);
|
||||||
LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
|
LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
|
||||||
" mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
|
" 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)
|
static uint32_t dcr_read_uic(void *opaque, int dcrn)
|
||||||
{
|
{
|
||||||
PPCUIC *uic;
|
PPCUIC *uic = opaque;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
uic = opaque;
|
|
||||||
dcrn -= uic->dcr_base;
|
dcrn -= uic->dcr_base;
|
||||||
switch (dcrn) {
|
switch (dcrn) {
|
||||||
case DCR_UICSR:
|
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)
|
static void dcr_write_uic(void *opaque, int dcrn, uint32_t val)
|
||||||
{
|
{
|
||||||
PPCUIC *uic;
|
PPCUIC *uic = opaque;
|
||||||
|
|
||||||
uic = opaque;
|
|
||||||
dcrn -= uic->dcr_base;
|
dcrn -= uic->dcr_base;
|
||||||
LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
|
LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
|
||||||
switch (dcrn) {
|
switch (dcrn) {
|
||||||
@ -251,19 +245,12 @@ static void ppc_uic_reset(DeviceState *dev)
|
|||||||
static void ppc_uic_realize(DeviceState *dev, Error **errp)
|
static void ppc_uic_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PPCUIC *uic = PPC_UIC(dev);
|
PPCUIC *uic = PPC_UIC(dev);
|
||||||
|
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
PowerPCCPU *cpu;
|
|
||||||
int i;
|
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++) {
|
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);
|
&dcr_read_uic, &dcr_write_uic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +260,6 @@ static void ppc_uic_realize(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Property ppc_uic_properties[] = {
|
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_UINT32("dcr-base", PPCUIC, dcr_base, 0xc0),
|
||||||
DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
|
DEFINE_PROP_BOOL("use-vectors", PPCUIC, use_vectors, true),
|
||||||
DEFINE_PROP_END_OF_LIST()
|
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 = {
|
static const TypeInfo ppc_uic_info = {
|
||||||
.name = TYPE_PPC_UIC,
|
.name = TYPE_PPC_UIC,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_PPC4xx_DCR_DEVICE,
|
||||||
.instance_size = sizeof(PPCUIC),
|
.instance_size = sizeof(PPCUIC),
|
||||||
.class_init = ppc_uic_class_init,
|
.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_msi.c',
|
||||||
'pnv_phb3_pbcq.c',
|
'pnv_phb3_pbcq.c',
|
||||||
'pnv_phb4.c',
|
'pnv_phb4.c',
|
||||||
'pnv_phb4_pec.c'
|
'pnv_phb4_pec.c',
|
||||||
|
'pnv_phb.c',
|
||||||
))
|
))
|
||||||
|
337
hw/pci-host/pnv_phb.c
Normal file
337
hw/pci-host/pnv_phb.c
Normal file
@ -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)
|
55
hw/pci-host/pnv_phb.h
Normal file
55
hw/pci-host/pnv_phb.h
Normal file
@ -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/visitor.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "hw/pci-host/pnv_phb3_regs.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-host/pnv_phb3.h"
|
||||||
#include "hw/pci/pcie_host.h"
|
#include "hw/pci/pcie_host.h"
|
||||||
#include "hw/pci/pcie_port.h"
|
#include "hw/pci/pcie_port.h"
|
||||||
@ -26,7 +27,7 @@
|
|||||||
|
|
||||||
static PCIDevice *pnv_phb3_find_cfg_dev(PnvPHB3 *phb)
|
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];
|
uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3];
|
||||||
uint8_t bus, devfn;
|
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)
|
uint64_t pnv_phb3_reg_read(void *opaque, hwaddr off, unsigned size)
|
||||||
{
|
{
|
||||||
PnvPHB3 *phb = opaque;
|
PnvPHB3 *phb = opaque;
|
||||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
|
||||||
if ((off & 0xfffc) == PHB_CONFIG_DATA) {
|
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)
|
static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PnvPHB3 *phb = PNV_PHB3(dev);
|
PnvPHB3 *phb = PNV_PHB3(dev);
|
||||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
|
||||||
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -1034,26 +1061,6 @@ static void pnv_phb3_realize(DeviceState *dev, Error **errp)
|
|||||||
/* Controller Registers */
|
/* Controller Registers */
|
||||||
memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb,
|
memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb3_reg_ops, phb,
|
||||||
"phb3-regs", 0x1000);
|
"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)
|
void pnv_phb3_update_regions(PnvPHB3 *phb)
|
||||||
@ -1078,47 +1085,80 @@ void pnv_phb3_update_regions(PnvPHB3 *phb)
|
|||||||
pnv_phb3_check_all_m64s(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[] = {
|
static Property pnv_phb3_properties[] = {
|
||||||
DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0),
|
DEFINE_PROP_UINT32("index", PnvPHB3, phb_id, 0),
|
||||||
DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0),
|
DEFINE_PROP_UINT32("chip-id", PnvPHB3, chip_id, 0),
|
||||||
DEFINE_PROP_LINK("chip", PnvPHB3, chip, TYPE_PNV_CHIP, PnvChip *),
|
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(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pnv_phb3_class_init(ObjectClass *klass, void *data)
|
static void pnv_phb3_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
hc->root_bus_path = pnv_phb3_root_bus_path;
|
|
||||||
dc->realize = pnv_phb3_realize;
|
dc->realize = pnv_phb3_realize;
|
||||||
device_class_set_props(dc, pnv_phb3_properties);
|
device_class_set_props(dc, pnv_phb3_properties);
|
||||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
|
||||||
dc->user_creatable = false;
|
dc->user_creatable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pnv_phb3_type_info = {
|
static const TypeInfo pnv_phb3_type_info = {
|
||||||
.name = TYPE_PNV_PHB3,
|
.name = TYPE_PNV_PHB3,
|
||||||
.parent = TYPE_PCIE_HOST_BRIDGE,
|
.parent = TYPE_DEVICE,
|
||||||
.instance_size = sizeof(PnvPHB3),
|
.instance_size = sizeof(PnvPHB3),
|
||||||
.class_init = pnv_phb3_class_init,
|
.class_init = pnv_phb3_class_init,
|
||||||
.instance_init = pnv_phb3_instance_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)
|
static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
BusClass *k = BUS_CLASS(klass);
|
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
|
* PHB3 has only a single root complex. Enforce the limit on the
|
||||||
* parent bus
|
* parent bus
|
||||||
@ -1132,51 +1172,9 @@ static const TypeInfo pnv_phb3_root_bus_info = {
|
|||||||
.class_init = pnv_phb3_root_bus_class_init,
|
.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)
|
static void pnv_phb3_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&pnv_phb3_root_bus_info);
|
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_type_info);
|
||||||
type_register_static(&pnv_phb3_iommu_memory_region_info);
|
type_register_static(&pnv_phb3_iommu_memory_region_info);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
static PCIDevice *pnv_phb4_find_cfg_dev(PnvPHB4 *phb)
|
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];
|
uint64_t addr = phb->regs[PHB_CONFIG_ADDRESS >> 3];
|
||||||
uint8_t bus, devfn;
|
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,
|
static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off,
|
||||||
unsigned size, uint64_t val)
|
unsigned size, uint64_t val)
|
||||||
{
|
{
|
||||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||||
PCIDevice *pdev;
|
PCIDevice *pdev;
|
||||||
|
|
||||||
if (size != 4) {
|
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,
|
static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
PCIHostState *pci = PCI_HOST_BRIDGE(phb);
|
PCIHostState *pci = PCI_HOST_BRIDGE(phb->phb_base);
|
||||||
PCIDevice *pdev;
|
PCIDevice *pdev;
|
||||||
uint64_t val;
|
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);
|
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)
|
static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PnvPHB4 *phb = PNV_PHB4(dev);
|
PnvPHB4 *phb = PNV_PHB4(dev);
|
||||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(phb->pec);
|
|
||||||
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
|
|
||||||
XiveSource *xsrc = &phb->xsrc;
|
XiveSource *xsrc = &phb->xsrc;
|
||||||
int nr_irqs;
|
int nr_irqs;
|
||||||
char name[32];
|
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,
|
memory_region_init_io(&phb->mr_regs, OBJECT(phb), &pnv_phb4_reg_ops, phb,
|
||||||
name, 0x2000);
|
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 */
|
/* Setup XIVE Source */
|
||||||
if (phb->big_phb) {
|
if (phb->big_phb) {
|
||||||
nr_irqs = PNV_PHB4_MAX_INTs;
|
nr_irqs = PNV_PHB4_MAX_INTs;
|
||||||
@ -1591,16 +1596,6 @@ static void pnv_phb4_realize(DeviceState *dev, Error **errp)
|
|||||||
pnv_phb4_xscom_realize(phb);
|
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)
|
* 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_UINT32("chip-id", PnvPHB4, chip_id, 0),
|
||||||
DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC,
|
DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC,
|
||||||
PnvPhb4PecState *),
|
PnvPhb4PecState *),
|
||||||
|
DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
static void pnv_phb4_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass);
|
XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass);
|
||||||
|
|
||||||
hc->root_bus_path = pnv_phb4_root_bus_path;
|
|
||||||
dc->realize = pnv_phb4_realize;
|
dc->realize = pnv_phb4_realize;
|
||||||
device_class_set_props(dc, pnv_phb4_properties);
|
device_class_set_props(dc, pnv_phb4_properties);
|
||||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
|
||||||
dc->user_creatable = false;
|
dc->user_creatable = false;
|
||||||
|
|
||||||
xfc->notify = pnv_phb4_xive_notify;
|
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 = {
|
static const TypeInfo pnv_phb4_type_info = {
|
||||||
.name = TYPE_PNV_PHB4,
|
.name = TYPE_PNV_PHB4,
|
||||||
.parent = TYPE_PCIE_HOST_BRIDGE,
|
.parent = TYPE_DEVICE,
|
||||||
.instance_init = pnv_phb4_instance_init,
|
.instance_init = pnv_phb4_instance_init,
|
||||||
.instance_size = sizeof(PnvPHB4),
|
.instance_size = sizeof(PnvPHB4),
|
||||||
.class_init = pnv_phb4_class_init,
|
.class_init = pnv_phb4_class_init,
|
||||||
@ -1721,10 +1714,55 @@ static const TypeInfo pnv_phb5_type_info = {
|
|||||||
.instance_size = sizeof(PnvPHB4),
|
.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)
|
static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
BusClass *k = BUS_CLASS(klass);
|
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
|
* PHB4 has only a single root complex. Enforce the limit on the
|
||||||
* parent bus
|
* parent bus
|
||||||
@ -1738,94 +1776,9 @@ static const TypeInfo pnv_phb4_root_bus_info = {
|
|||||||
.class_init = pnv_phb4_root_bus_class_init,
|
.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)
|
static void pnv_phb4_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&pnv_phb4_root_bus_info);
|
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_phb4_type_info);
|
||||||
type_register_static(&pnv_phb5_type_info);
|
type_register_static(&pnv_phb5_type_info);
|
||||||
type_register_static(&pnv_phb4_iommu_memory_region_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,
|
int stack_no,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
|
PnvPHB *phb = PNV_PHB(qdev_new(TYPE_PNV_PHB));
|
||||||
PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type));
|
|
||||||
int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no);
|
int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no);
|
||||||
|
|
||||||
object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb));
|
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];
|
pec->num_phbs = pecc->num_phbs[pec->index];
|
||||||
|
|
||||||
/* Create PHBs if running with defaults */
|
/* Create PHBs if running with defaults */
|
||||||
for (i = 0; i < pec->num_phbs; i++) {
|
if (defaults_enabled()) {
|
||||||
pnv_pec_default_phb_realize(pec, i, errp);
|
for (i = 0; i < pec->num_phbs; i++) {
|
||||||
|
pnv_pec_default_phb_realize(pec, i, errp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the XSCOM regions for the PEC registers */
|
/* 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->version = PNV_PHB4_VERSION;
|
||||||
pecc->phb_type = TYPE_PNV_PHB4;
|
pecc->phb_type = TYPE_PNV_PHB4;
|
||||||
pecc->num_phbs = pnv_pec_num_phbs;
|
pecc->num_phbs = pnv_pec_num_phbs;
|
||||||
pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pnv_pec_type_info = {
|
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->version = PNV_PHB5_VERSION;
|
||||||
pecc->phb_type = TYPE_PNV_PHB5;
|
pecc->phb_type = TYPE_PNV_PHB5;
|
||||||
pecc->num_phbs = pnv_phb5_pec_num_stacks;
|
pecc->num_phbs = pnv_phb5_pec_num_stacks;
|
||||||
pecc->rp_model = TYPE_PNV_PHB5_ROOT_PORT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo pnv_phb5_pec_type_info = {
|
static const TypeInfo pnv_phb5_pec_type_info = {
|
||||||
|
@ -58,7 +58,6 @@ config PPC4XX
|
|||||||
|
|
||||||
config SAM460EX
|
config SAM460EX
|
||||||
bool
|
bool
|
||||||
select PPC405
|
|
||||||
select PFLASH_CFI01
|
select PFLASH_CFI01
|
||||||
select IDE_SII3112
|
select IDE_SII3112
|
||||||
select M41T80
|
select M41T80
|
||||||
@ -72,6 +71,7 @@ config SAM460EX
|
|||||||
|
|
||||||
config PEGASOS2
|
config PEGASOS2
|
||||||
bool
|
bool
|
||||||
|
imply ATI_VGA
|
||||||
select MV64361
|
select MV64361
|
||||||
select VT82C686
|
select VT82C686
|
||||||
select IDE_VIA
|
select IDE_VIA
|
||||||
@ -79,7 +79,6 @@ config PEGASOS2
|
|||||||
select VOF
|
select VOF
|
||||||
# This should come with VT82C686
|
# This should come with VT82C686
|
||||||
select ACPI_X86
|
select ACPI_X86
|
||||||
imply ATI_VGA
|
|
||||||
|
|
||||||
config PREP
|
config PREP
|
||||||
bool
|
bool
|
||||||
|
@ -46,6 +46,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files(
|
|||||||
'pnv_lpc.c',
|
'pnv_lpc.c',
|
||||||
'pnv_psi.c',
|
'pnv_psi.c',
|
||||||
'pnv_occ.c',
|
'pnv_occ.c',
|
||||||
|
'pnv_sbe.c',
|
||||||
'pnv_bmc.c',
|
'pnv_bmc.c',
|
||||||
'pnv_homer.c',
|
'pnv_homer.c',
|
||||||
'pnv_pnor.c',
|
'pnv_pnor.c',
|
||||||
|
186
hw/ppc/pnv.c
186
hw/ppc/pnv.c
@ -43,6 +43,7 @@
|
|||||||
#include "hw/ipmi/ipmi.h"
|
#include "hw/ipmi/ipmi.h"
|
||||||
#include "target/ppc/mmu-hash64.h"
|
#include "target/ppc/mmu-hash64.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
|
#include "hw/pci-host/pnv_phb.h"
|
||||||
|
|
||||||
#include "hw/ppc/xics.h"
|
#include "hw/ppc/xics.h"
|
||||||
#include "hw/qdev-properties.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);
|
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 void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt)
|
||||||
{
|
{
|
||||||
static const char compat[] = "ibm,power8-xscom\0ibm,xscom";
|
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);
|
ics_pic_print_info(&chip8->psi.ics, mon);
|
||||||
|
|
||||||
for (i = 0; i < chip8->num_phbs; i++) {
|
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);
|
pnv_phb3_msi_pic_print_info(&phb3->msis, mon);
|
||||||
ics_pic_print_info(&phb3->lsis, 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)
|
static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque)
|
||||||
{
|
{
|
||||||
Monitor *mon = 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) {
|
if (!phb) {
|
||||||
pnv_phb4_pic_print_info(phb4, mon);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), mon);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +788,7 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type)
|
|||||||
PowerPCCPUClass *ppc =
|
PowerPCCPUClass *ppc =
|
||||||
POWERPC_CPU_CLASS(object_class_by_name(cpu_type));
|
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)
|
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);
|
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++) {
|
for (i = 0; i < chip8->num_phbs; i++) {
|
||||||
object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3);
|
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)
|
static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
|
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),
|
memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip),
|
||||||
&chip8->homer.regs);
|
&chip8->homer.regs);
|
||||||
|
|
||||||
/* PHB3 controllers */
|
/* PHB controllers */
|
||||||
for (i = 0; i < chip8->num_phbs; i++) {
|
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), "index", i, &error_fatal);
|
||||||
object_property_set_int(OBJECT(phb), "chip-id", chip->chip_id,
|
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, "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);
|
object_initialize_child(obj, "homer", &chip9->homer, TYPE_PNV9_HOMER);
|
||||||
|
|
||||||
/* Number of PECs is the chip default */
|
/* 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),
|
memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip),
|
||||||
&chip9->occ.sram_regs);
|
&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 */
|
/* HOMER */
|
||||||
object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip),
|
object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip),
|
||||||
&error_abort);
|
&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, "psi", &chip10->psi, TYPE_PNV10_PSI);
|
||||||
object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC);
|
object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC);
|
||||||
object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC);
|
object_initialize_child(obj, "occ", &chip10->occ, TYPE_PNV10_OCC);
|
||||||
|
object_initialize_child(obj, "sbe", &chip10->sbe, TYPE_PNV10_SBE);
|
||||||
object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER);
|
object_initialize_child(obj, "homer", &chip10->homer, TYPE_PNV10_HOMER);
|
||||||
|
|
||||||
chip->num_pecs = pcc->num_pecs;
|
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),
|
PNV10_OCC_SENSOR_BASE(chip),
|
||||||
&chip10->occ.sram_regs);
|
&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 */
|
/* HOMER */
|
||||||
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
|
object_property_set_link(OBJECT(&chip10->homer), "chip", OBJECT(chip),
|
||||||
&error_abort);
|
&error_abort);
|
||||||
@ -1957,7 +2044,8 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < chip8->num_phbs; j++) {
|
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)) {
|
if (ics_valid_irq(&phb3->lsis, irq)) {
|
||||||
return &phb3->lsis;
|
return &phb3->lsis;
|
||||||
@ -1995,7 +2083,8 @@ static void pnv_ics_resend(XICSFabric *xi)
|
|||||||
ics_resend(&chip8->psi.ics);
|
ics_resend(&chip8->psi.ics);
|
||||||
|
|
||||||
for (j = 0; j < chip8->num_phbs; j++) {
|
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(&phb3->lsis);
|
||||||
ics_resend(ICS(&phb3->msis));
|
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);
|
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||||
static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv";
|
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->desc = "IBM PowerNV (Non-Virtualized) POWER8";
|
||||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
|
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->icp_get = pnv_icp_get;
|
||||||
xic->ics_get = pnv_ics_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 = compat;
|
||||||
pmc->compat_size = sizeof(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)
|
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);
|
PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc);
|
||||||
static const char compat[] = "qemu,powernv9\0ibm,powernv";
|
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->desc = "IBM PowerNV (Non-Virtualized) POWER9";
|
||||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0");
|
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;
|
xfc->match_nvt = pnv_match_nvt;
|
||||||
|
|
||||||
mc->alias = "powernv";
|
mc->alias = "powernv";
|
||||||
@ -2122,6 +2226,8 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
|
|||||||
pmc->compat = compat;
|
pmc->compat = compat;
|
||||||
pmc->compat_size = sizeof(compat);
|
pmc->compat_size = sizeof(compat);
|
||||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
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)
|
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);
|
XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc);
|
||||||
static const char compat[] = "qemu,powernv10\0ibm,powernv";
|
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->desc = "IBM PowerNV (Non-Virtualized) POWER10";
|
||||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0");
|
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 = compat;
|
||||||
pmc->compat_size = sizeof(compat);
|
pmc->compat_size = sizeof(compat);
|
||||||
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
pmc->dt_power_mgt = pnv_dt_power_mgt;
|
||||||
|
|
||||||
xfc->match_nvt = pnv10_xive_match_nvt;
|
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)
|
static bool pnv_machine_get_hb(Object *obj, Error **errp)
|
||||||
|
414
hw/ppc/pnv_sbe.c
Normal file
414
hw/ppc/pnv_sbe.c
Normal file
@ -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, "reg", reg, sizeof(reg))));
|
||||||
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size)));
|
_FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size)));
|
||||||
_FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
|
_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.fdt = fdt;
|
||||||
args.xscom_offset = xscom_offset;
|
args.xscom_offset = xscom_offset;
|
||||||
|
198
hw/ppc/ppc405.h
198
hw/ppc/ppc405.h
@ -25,54 +25,168 @@
|
|||||||
#ifndef PPC405_H
|
#ifndef PPC405_H
|
||||||
#define PPC405_H
|
#define PPC405_H
|
||||||
|
|
||||||
|
#include "qom/object.h"
|
||||||
#include "hw/ppc/ppc4xx.h"
|
#include "hw/ppc/ppc4xx.h"
|
||||||
|
#include "hw/intc/ppc-uic.h"
|
||||||
|
#include "hw/i2c/ppc4xx_i2c.h"
|
||||||
|
|
||||||
#define PPC405EP_SDRAM_BASE 0x00000000
|
/* PLB to OPB bridge */
|
||||||
#define PPC405EP_NVRAM_BASE 0xF0000000
|
#define TYPE_PPC405_POB "ppc405-pob"
|
||||||
#define PPC405EP_FPGA_BASE 0xF0300000
|
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405PobState, PPC405_POB);
|
||||||
#define PPC405EP_SRAM_BASE 0xFFF00000
|
struct Ppc405PobState {
|
||||||
#define PPC405EP_SRAM_SIZE (512 * KiB)
|
Ppc4xxDcrDeviceState parent_obj;
|
||||||
#define PPC405EP_FLASH_BASE 0xFFF80000
|
|
||||||
|
|
||||||
/* Bootinfo as set-up by u-boot */
|
uint32_t bear;
|
||||||
typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
|
uint32_t besr0;
|
||||||
struct ppc4xx_bd_info_t {
|
uint32_t besr1;
|
||||||
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];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PowerPC 405 core */
|
/* OPB arbitrer */
|
||||||
ram_addr_t ppc405_set_bootinfo(CPUPPCState *env, ram_addr_t ram_size);
|
#define TYPE_PPC405_OPBA "ppc405-opba"
|
||||||
|
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405OpbaState, PPC405_OPBA);
|
||||||
|
struct Ppc405OpbaState {
|
||||||
|
SysBusDevice parent_obj;
|
||||||
|
|
||||||
void ppc4xx_plb_init(CPUPPCState *env);
|
MemoryRegion io;
|
||||||
void ppc405_ebc_init(CPUPPCState *env);
|
uint8_t cr;
|
||||||
|
uint8_t pr;
|
||||||
|
};
|
||||||
|
|
||||||
PowerPCCPU *ppc405ep_init(MemoryRegion *address_space_mem,
|
/* DMA controller */
|
||||||
MemoryRegion ram_memories[2],
|
#define TYPE_PPC405_DMA "ppc405-dma"
|
||||||
hwaddr ram_bases[2],
|
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405DmaState, PPC405_DMA);
|
||||||
hwaddr ram_sizes[2],
|
struct Ppc405DmaState {
|
||||||
uint32_t sysclk, DeviceState **uicdev,
|
Ppc4xxDcrDeviceState parent_obj;
|
||||||
int do_init);
|
|
||||||
|
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 */
|
#endif /* PPC405_H */
|
||||||
|
@ -48,97 +48,24 @@
|
|||||||
#define KERNEL_LOAD_ADDR 0x01000000
|
#define KERNEL_LOAD_ADDR 0x01000000
|
||||||
#define INITRD_LOAD_ADDR 0x01800000
|
#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
|
#define USE_FLASH_BIOS
|
||||||
|
|
||||||
/*****************************************************************************/
|
#define TYPE_PPC405_MACHINE MACHINE_TYPE_NAME("ppc405")
|
||||||
/* PPC405EP reference board (IBM) */
|
OBJECT_DECLARE_SIMPLE_TYPE(Ppc405MachineState, PPC405_MACHINE);
|
||||||
/* Standalone board with:
|
|
||||||
* - PowerPC 405EP CPU
|
struct Ppc405MachineState {
|
||||||
* - SDRAM (0x00000000)
|
/* Private */
|
||||||
* - Flash (0xFFF80000)
|
MachineState parent_obj;
|
||||||
* - SRAM (0xFFF00000)
|
/* Public */
|
||||||
* - NVRAM (0xF0000000)
|
|
||||||
* - FPGA (0xF0300000)
|
Ppc405SoCState soc;
|
||||||
*/
|
|
||||||
typedef struct ref405ep_fpga_t ref405ep_fpga_t;
|
|
||||||
struct ref405ep_fpga_t {
|
|
||||||
uint8_t reg0;
|
|
||||||
uint8_t reg1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size)
|
/* CPU reset handler when booting directly from a loaded kernel */
|
||||||
{
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
static struct boot_info {
|
static struct boot_info {
|
||||||
uint32_t entry;
|
uint32_t entry;
|
||||||
uint32_t bdloc;
|
uint32_t bdloc;
|
||||||
@ -169,6 +96,126 @@ static void main_cpu_reset(void *opaque)
|
|||||||
env->nip = bi->entry;
|
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)
|
static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
||||||
{
|
{
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
@ -221,18 +268,12 @@ static void boot_from_kernel(MachineState *machine, PowerPCCPU *cpu)
|
|||||||
env->load_info = &boot_info;
|
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);
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||||
const char *kernel_filename = machine->kernel_filename;
|
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();
|
MemoryRegion *sysmem = get_system_memory();
|
||||||
DeviceState *uicdev;
|
|
||||||
|
|
||||||
if (machine->ram_size != mc->default_ram_size) {
|
if (machine->ram_size != mc->default_ram_size) {
|
||||||
char *sz = size_to_str(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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: fix this */
|
object_initialize_child(OBJECT(machine), "soc", &ppc405->soc,
|
||||||
memory_region_init_alias(&ram_memories[0], NULL, "ef405ep.ram.alias",
|
TYPE_PPC405_SOC);
|
||||||
machine->ram, 0, machine->ram_size);
|
object_property_set_uint(OBJECT(&ppc405->soc), "ram-size",
|
||||||
ram_bases[0] = 0;
|
machine->ram_size, &error_fatal);
|
||||||
ram_sizes[0] = machine->ram_size;
|
object_property_set_link(OBJECT(&ppc405->soc), "dram",
|
||||||
memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0);
|
OBJECT(machine->ram), &error_abort);
|
||||||
ram_bases[1] = 0x00000000;
|
object_property_set_bool(OBJECT(&ppc405->soc), "dram-init",
|
||||||
ram_sizes[1] = 0x00000000;
|
kernel_filename != NULL, &error_abort);
|
||||||
|
object_property_set_uint(OBJECT(&ppc405->soc), "sys-clk", 33333333,
|
||||||
cpu = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
&error_abort);
|
||||||
33333333, &uicdev, kernel_filename == NULL ? 0 : 1);
|
qdev_realize(DEVICE(&ppc405->soc), NULL, &error_fatal);
|
||||||
|
|
||||||
/* allocate SRAM */
|
|
||||||
memory_region_init_ram(sram, NULL, "ef405ep.sram", PPC405EP_SRAM_SIZE,
|
|
||||||
&error_fatal);
|
|
||||||
memory_region_add_subregion(sysmem, PPC405EP_SRAM_BASE, sram);
|
|
||||||
|
|
||||||
/* allocate and load BIOS */
|
/* allocate and load BIOS */
|
||||||
if (machine->firmware) {
|
if (machine->firmware) {
|
||||||
@ -285,15 +321,6 @@ static void ref405ep_init(MachineState *machine)
|
|||||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
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 */
|
/* Load kernel and initrd using U-Boot images */
|
||||||
if (kernel_filename && machine->firmware) {
|
if (kernel_filename && machine->firmware) {
|
||||||
target_ulong kernel_base, initrd_base;
|
target_ulong kernel_base, initrd_base;
|
||||||
@ -322,63 +349,66 @@ static void ref405ep_init(MachineState *machine)
|
|||||||
|
|
||||||
/* Load ELF kernel and rootfs.cpio */
|
/* Load ELF kernel and rootfs.cpio */
|
||||||
} else if (kernel_filename && !machine->firmware) {
|
} 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);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
mc->desc = "ref405ep";
|
mc->desc = "PPC405 generic machine";
|
||||||
mc->init = ref405ep_init;
|
mc->init = ppc405_init;
|
||||||
mc->default_ram_size = 0x08000000;
|
mc->default_ram_size = 128 * MiB;
|
||||||
mc->default_ram_id = "ef405ep.ram";
|
mc->default_ram_id = "ppc405.ram";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo ref405ep_type = {
|
static const TypeInfo ppc405_machine_type = {
|
||||||
.name = MACHINE_TYPE_NAME("ref405ep"),
|
.name = TYPE_PPC405_MACHINE,
|
||||||
.parent = TYPE_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 */
|
/* PPC405EP reference board (IBM) */
|
||||||
/* - PowerPC 405EP processor
|
/*
|
||||||
* - SDRAM 128 MB at 0x00000000
|
* Standalone board with:
|
||||||
* - Boot flash 2 MB at 0xFFE00000
|
* - PowerPC 405EP CPU
|
||||||
* - Application flash 32 MB at 0xFC000000
|
* - SDRAM (0x00000000)
|
||||||
* - 2 serial ports
|
* - Flash (0xFFF80000)
|
||||||
* - 2 ethernet PHY
|
* - SRAM (0xFFF00000)
|
||||||
* - 1 USB 1.1 device 0x50000000
|
* - NVRAM (0xF0000000)
|
||||||
* - 1 LCD display 0x50100000
|
* - FPGA (0xF0300000)
|
||||||
* - 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.
|
|
||||||
*/
|
*/
|
||||||
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 reg0;
|
||||||
uint8_t reg1;
|
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;
|
uint32_t ret;
|
||||||
|
|
||||||
cpld = opaque;
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
ret = cpld->reg0;
|
ret = fpga->reg0;
|
||||||
break;
|
break;
|
||||||
case 0x1:
|
case 0x1:
|
||||||
ret = cpld->reg1;
|
ret = fpga->reg1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -388,195 +418,113 @@ static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void taihu_cpld_write(void *opaque, hwaddr addr,
|
static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value,
|
||||||
uint64_t value, unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
taihu_cpld_t *cpld;
|
Ref405epFpgaState *fpga = opaque;
|
||||||
|
|
||||||
cpld = opaque;
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
/* Read only */
|
/* Read only */
|
||||||
break;
|
break;
|
||||||
case 0x1:
|
case 0x1:
|
||||||
cpld->reg1 = value;
|
fpga->reg1 = value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemoryRegionOps taihu_cpld_ops = {
|
static const MemoryRegionOps ref405ep_fpga_ops = {
|
||||||
.read = taihu_cpld_read,
|
.read = ref405ep_fpga_readb,
|
||||||
.write = taihu_cpld_write,
|
.write = ref405ep_fpga_writeb,
|
||||||
.impl = {
|
.impl.min_access_size = 1,
|
||||||
.min_access_size = 1,
|
.impl.max_access_size = 1,
|
||||||
.max_access_size = 1,
|
.valid.min_access_size = 1,
|
||||||
},
|
.valid.max_access_size = 4,
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.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;
|
fpga->reg0 = 0x00;
|
||||||
cpld->reg0 = 0x01;
|
fpga->reg1 = 0x0F;
|
||||||
cpld->reg1 = 0x80;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base)
|
static void ref405ep_fpga_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
taihu_cpld_t *cpld;
|
Ref405epFpgaState *s = REF405EP_FPGA(dev);
|
||||||
MemoryRegion *cpld_memory = g_new(MemoryRegion, 1);
|
|
||||||
|
|
||||||
cpld = g_new0(taihu_cpld_t, 1);
|
memory_region_init_io(&s->iomem, OBJECT(s), &ref405ep_fpga_ops, s,
|
||||||
memory_region_init_io(cpld_memory, NULL, &taihu_cpld_ops, cpld, "cpld", 0x100);
|
"fpga", 0x00000100);
|
||||||
memory_region_add_subregion(sysmem, base, cpld_memory);
|
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
|
||||||
qemu_register_reset(&taihu_cpld_reset, cpld);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void taihu_405ep_init(MachineState *machine)
|
static void ref405ep_fpga_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
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;
|
|
||||||
|
|
||||||
if (machine->ram_size != mc->default_ram_size) {
|
dc->realize = ref405ep_fpga_realize;
|
||||||
char *sz = size_to_str(mc->default_ram_size);
|
dc->reset = ref405ep_fpga_reset;
|
||||||
error_report("Invalid RAM size, should be %s", sz);
|
/* Reason: only works as part of a ppc405 board */
|
||||||
g_free(sz);
|
dc->user_creatable = false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
MachineClass *mc = MACHINE_CLASS(oc);
|
||||||
|
|
||||||
mc->desc = "taihu";
|
mc->desc = "ref405ep";
|
||||||
mc->init = taihu_405ep_init;
|
mc->init = ref405ep_init;
|
||||||
mc->default_ram_size = 0x08000000;
|
|
||||||
mc->default_ram_id = "taihu_405ep.ram";
|
|
||||||
mc->deprecation_reason = "incomplete, use 'ref405ep' instead";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo taihu_type = {
|
static const TypeInfo ref405ep_type = {
|
||||||
.name = MACHINE_TYPE_NAME("taihu"),
|
.name = MACHINE_TYPE_NAME("ref405ep"),
|
||||||
.parent = TYPE_MACHINE,
|
.parent = TYPE_PPC405_MACHINE,
|
||||||
.class_init = taihu_class_init,
|
.class_init = ref405ep_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ppc405_machine_init(void)
|
static void ppc405_machine_init(void)
|
||||||
{
|
{
|
||||||
|
type_register_static(&ppc405_machine_type);
|
||||||
type_register_static(&ref405ep_type);
|
type_register_static(&ref405ep_type);
|
||||||
type_register_static(&taihu_type);
|
type_register_static(&ref405ep_fpga_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(ppc405_machine_init)
|
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,
|
ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property,
|
||||||
sizeof(mem_reg_property));
|
sizeof(mem_reg_property));
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
fprintf(stderr, "couldn't set /memory/reg\n");
|
fprintf(stderr, "couldn't set /memory/reg\n");
|
||||||
|
}
|
||||||
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
||||||
initrd_base);
|
initrd_base);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
||||||
|
}
|
||||||
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||||
(initrd_base + initrd_size));
|
(initrd_base + initrd_size));
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
||||||
|
}
|
||||||
ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
|
ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
|
||||||
kernel_cmdline);
|
kernel_cmdline);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
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
|
* directly access the timebase without host involvement, we must expose
|
||||||
* the correct frequencies. */
|
* the correct frequencies.
|
||||||
|
*/
|
||||||
if (kvm_enabled()) {
|
if (kvm_enabled()) {
|
||||||
tb_freq = kvmppc_get_tbfreq();
|
tb_freq = kvmppc_get_tbfreq();
|
||||||
clock_freq = kvmppc_get_clockfreq();
|
clock_freq = kvmppc_get_clockfreq();
|
||||||
@ -193,12 +196,9 @@ static void bamboo_init(MachineState *machine)
|
|||||||
|
|
||||||
/* interrupt controller */
|
/* interrupt controller */
|
||||||
uicdev = qdev_new(TYPE_PPC_UIC);
|
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);
|
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,
|
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
|
||||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
||||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
||||||
@ -249,8 +249,10 @@ static void bamboo_init(MachineState *machine)
|
|||||||
if (pcibus) {
|
if (pcibus) {
|
||||||
/* Register network interfaces. */
|
/* Register network interfaces. */
|
||||||
for (i = 0; i < nb_nics; i++) {
|
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);
|
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 */
|
/* 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.
|
* to allow firmware and guests to find an empty bus. Cards should use PCI.
|
||||||
*/
|
*/
|
||||||
#include "hw/pci/pcie_host.h"
|
#include "hw/pci/pcie_host.h"
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
#include "hw/irq.h"
|
#include "hw/irq.h"
|
||||||
#include "hw/ppc/ppc.h"
|
#include "hw/ppc/ppc.h"
|
||||||
#include "hw/ppc/ppc4xx.h"
|
#include "hw/ppc/ppc4xx.h"
|
||||||
#include "hw/intc/ppc-uic.h"
|
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
@ -37,38 +36,6 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "trace.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 */
|
/* SDRAM controller */
|
||||||
typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
|
typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
|
||||||
@ -98,12 +65,12 @@ enum {
|
|||||||
SDRAM0_CFGDATA = 0x011,
|
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
|
* there are type inconsistencies, mixing hwaddr, target_ulong
|
||||||
* and uint32_t
|
* and uint32_t
|
||||||
*/
|
*/
|
||||||
static uint32_t sdram_bcr (hwaddr ram_base,
|
static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size)
|
||||||
hwaddr ram_size)
|
|
||||||
{
|
{
|
||||||
uint32_t bcr;
|
uint32_t bcr;
|
||||||
|
|
||||||
@ -146,16 +113,17 @@ static inline hwaddr sdram_base(uint32_t bcr)
|
|||||||
return bcr & 0xFF800000;
|
return bcr & 0xFF800000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static target_ulong sdram_size (uint32_t bcr)
|
static target_ulong sdram_size(uint32_t bcr)
|
||||||
{
|
{
|
||||||
target_ulong size;
|
target_ulong size;
|
||||||
int sh;
|
int sh;
|
||||||
|
|
||||||
sh = (bcr >> 17) & 0x7;
|
sh = (bcr >> 17) & 0x7;
|
||||||
if (sh == 7)
|
if (sh == 7) {
|
||||||
size = -1;
|
size = -1;
|
||||||
else
|
} else {
|
||||||
size = (4 * MiB) << sh;
|
size = (4 * MiB) << sh;
|
||||||
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@ -175,7 +143,7 @@ static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i,
|
|||||||
}
|
}
|
||||||
sdram->bcr[i] = bcr & 0xFFDEE001;
|
sdram->bcr[i] = bcr & 0xFFDEE001;
|
||||||
if (enabled && (bcr & 0x00000001)) {
|
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",
|
memory_region_init(&sdram->containers[i], NULL, "sdram-containers",
|
||||||
sdram_size(bcr));
|
sdram_size(bcr));
|
||||||
memory_region_add_subregion(&sdram->containers[i], 0,
|
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;
|
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;
|
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;
|
ppc4xx_sdram_t *sdram;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
@ -280,7 +248,7 @@ static uint32_t dcr_read_sdram (void *opaque, int dcrn)
|
|||||||
return ret;
|
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;
|
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_unmap_bcr(sdram);
|
||||||
sdram->status |= 0x80000000;
|
sdram->status |= 0x80000000;
|
||||||
}
|
}
|
||||||
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
|
if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) {
|
||||||
sdram->status |= 0x40000000;
|
sdram->status |= 0x40000000;
|
||||||
else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
|
} else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) {
|
||||||
sdram->status &= ~0x40000000;
|
sdram->status &= ~0x40000000;
|
||||||
|
}
|
||||||
sdram->cfg = val;
|
sdram->cfg = val;
|
||||||
break;
|
break;
|
||||||
case 0x24: /* SDRAM_STATUS */
|
case 0x24: /* SDRAM_STATUS */
|
||||||
@ -348,10 +317,11 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
|
|||||||
break;
|
break;
|
||||||
case 0x98: /* SDRAM_ECCESR */
|
case 0x98: /* SDRAM_ECCESR */
|
||||||
val &= 0xFFF0F000;
|
val &= 0xFFF0F000;
|
||||||
if (sdram->eccesr == 0 && val != 0)
|
if (sdram->eccesr == 0 && val != 0) {
|
||||||
qemu_irq_raise(sdram->irq);
|
qemu_irq_raise(sdram->irq);
|
||||||
else if (sdram->eccesr != 0 && val == 0)
|
} else if (sdram->eccesr != 0 && val == 0) {
|
||||||
qemu_irq_lower(sdram->irq);
|
qemu_irq_lower(sdram->irq);
|
||||||
|
}
|
||||||
sdram->eccesr = val;
|
sdram->eccesr = val;
|
||||||
break;
|
break;
|
||||||
default: /* Error */
|
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;
|
ppc4xx_sdram_t *sdram;
|
||||||
|
|
||||||
@ -381,11 +351,11 @@ static void sdram_reset (void *opaque)
|
|||||||
sdram->cfg = 0x00800000;
|
sdram->cfg = 0x00800000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
|
void ppc4xx_sdram_init(CPUPPCState *env, qemu_irq irq, int nbanks,
|
||||||
MemoryRegion *ram_memories,
|
MemoryRegion *ram_memories,
|
||||||
hwaddr *ram_bases,
|
hwaddr *ram_bases,
|
||||||
hwaddr *ram_sizes,
|
hwaddr *ram_sizes,
|
||||||
int do_init)
|
int do_init)
|
||||||
{
|
{
|
||||||
ppc4xx_sdram_t *sdram;
|
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);
|
sdram, &dcr_read_sdram, &dcr_write_sdram);
|
||||||
ppc_dcr_register(env, SDRAM0_CFGDATA,
|
ppc_dcr_register(env, SDRAM0_CFGDATA,
|
||||||
sdram, &dcr_read_sdram, &dcr_write_sdram);
|
sdram, &dcr_read_sdram, &dcr_write_sdram);
|
||||||
if (do_init)
|
if (do_init) {
|
||||||
sdram_map_bcr(sdram);
|
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",
|
error_report("at most %d bank%s of %s MiB each supported",
|
||||||
nr_banks, nr_banks == 1 ? "" : "s", s->str);
|
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);
|
used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB);
|
||||||
|
|
||||||
g_string_free(s, true);
|
g_string_free(s, true);
|
||||||
@ -491,32 +462,10 @@ enum {
|
|||||||
MAL0_RCBS1 = 0x1E1,
|
MAL0_RCBS1 = 0x1E1,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ppc4xx_mal_t ppc4xx_mal_t;
|
static void ppc4xx_mal_reset(DeviceState *dev)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
ppc4xx_mal_t *mal;
|
Ppc4xxMalState *mal = PPC4xx_MAL(dev);
|
||||||
|
|
||||||
mal = opaque;
|
|
||||||
mal->cfg = 0x0007C000;
|
mal->cfg = 0x0007C000;
|
||||||
mal->esr = 0x00000000;
|
mal->esr = 0x00000000;
|
||||||
mal->ier = 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)
|
static uint32_t dcr_read_mal(void *opaque, int dcrn)
|
||||||
{
|
{
|
||||||
ppc4xx_mal_t *mal;
|
Ppc4xxMalState *mal = opaque;
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
mal = opaque;
|
|
||||||
switch (dcrn) {
|
switch (dcrn) {
|
||||||
case MAL0_CFG:
|
case MAL0_CFG:
|
||||||
ret = mal->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)
|
static void dcr_write_mal(void *opaque, int dcrn, uint32_t val)
|
||||||
{
|
{
|
||||||
ppc4xx_mal_t *mal;
|
Ppc4xxMalState *mal = opaque;
|
||||||
|
|
||||||
mal = opaque;
|
|
||||||
switch (dcrn) {
|
switch (dcrn) {
|
||||||
case MAL0_CFG:
|
case MAL0_CFG:
|
||||||
if (val & 0x80000000) {
|
if (val & 0x80000000) {
|
||||||
ppc4xx_mal_reset(mal);
|
ppc4xx_mal_reset(DEVICE(mal));
|
||||||
}
|
}
|
||||||
mal->cfg = val & 0x00FFC087;
|
mal->cfg = val & 0x00FFC087;
|
||||||
break;
|
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,
|
static void ppc4xx_mal_realize(DeviceState *dev, Error **errp)
|
||||||
qemu_irq irqs[4])
|
|
||||||
{
|
{
|
||||||
ppc4xx_mal_t *mal;
|
Ppc4xxMalState *mal = PPC4xx_MAL(dev);
|
||||||
|
Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
assert(txcnum <= 32 && rxcnum <= 32);
|
if (mal->txcnum > 32 || mal->rxcnum > 32) {
|
||||||
mal = g_malloc0(sizeof(*mal));
|
error_setg(errp, "invalid TXC/RXC number");
|
||||||
mal->txcnum = txcnum;
|
return;
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
qemu_register_reset(&ppc4xx_mal_reset, mal);
|
|
||||||
ppc_dcr_register(env, MAL0_CFG,
|
mal->txctpr = g_new0(uint32_t, mal->txcnum);
|
||||||
mal, &dcr_read_mal, &dcr_write_mal);
|
mal->rxctpr = g_new0(uint32_t, mal->rxcnum);
|
||||||
ppc_dcr_register(env, MAL0_ESR,
|
mal->rcbs = g_new0(uint32_t, mal->rxcnum);
|
||||||
mal, &dcr_read_mal, &dcr_write_mal);
|
|
||||||
ppc_dcr_register(env, MAL0_IER,
|
for (i = 0; i < ARRAY_SIZE(mal->irqs); i++) {
|
||||||
mal, &dcr_read_mal, &dcr_write_mal);
|
sysbus_init_irq(SYS_BUS_DEVICE(dev), &mal->irqs[i]);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
for (i = 0; i < rxcnum; i++) {
|
|
||||||
ppc_dcr_register(env, MAL0_RXCTP0R + i,
|
ppc4xx_dcr_register(dcr, MAL0_CFG, mal, &dcr_read_mal, &dcr_write_mal);
|
||||||
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++) {
|
for (i = 0; i < mal->rxcnum; i++) {
|
||||||
ppc_dcr_register(env, MAL0_RCBS0 + i,
|
ppc4xx_dcr_register(dcr, MAL0_RXCTP0R + i,
|
||||||
mal, &dcr_read_mal, &dcr_write_mal);
|
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>
|
* 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/osdep.h"
|
||||||
#include "qemu/log.h"
|
#include "qemu/log.h"
|
||||||
@ -65,8 +67,10 @@ struct PPC4xxPCIState {
|
|||||||
#define PCIC0_CFGADDR 0x0
|
#define PCIC0_CFGADDR 0x0
|
||||||
#define PCIC0_CFGDATA 0x4
|
#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_PMM0LA 0x0
|
||||||
#define PCIL0_PMM0MA 0x4
|
#define PCIL0_PMM0MA 0x4
|
||||||
#define PCIL0_PMM0PCILA 0x8
|
#define PCIL0_PMM0PCILA 0x8
|
||||||
@ -80,8 +84,10 @@ struct PPC4xxPCIState {
|
|||||||
#define PCIL0_PMM2PCILA 0x28
|
#define PCIL0_PMM2PCILA 0x28
|
||||||
#define PCIL0_PMM2PCIHA 0x2c
|
#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_PTM1MS 0x30
|
||||||
#define PCIL0_PTM1LA 0x34
|
#define PCIL0_PTM1LA 0x34
|
||||||
#define PCIL0_PTM2MS 0x38
|
#define PCIL0_PTM2MS 0x38
|
||||||
@ -96,9 +102,10 @@ static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset,
|
|||||||
{
|
{
|
||||||
struct PPC4xxPCIState *pci = opaque;
|
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) {
|
switch (offset) {
|
||||||
case PCIL0_PMM0LA:
|
case PCIL0_PMM0LA:
|
||||||
pci->pmm[0].la = value;
|
pci->pmm[0].la = value;
|
||||||
@ -243,8 +250,10 @@ static void ppc4xx_pci_reset(void *opaque)
|
|||||||
memset(pci->ptm, 0, sizeof(pci->ptm));
|
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)
|
static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
|
||||||
{
|
{
|
||||||
int slot = PCI_SLOT(pci_dev->devfn);
|
int slot = PCI_SLOT(pci_dev->devfn);
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
#include "ppc440.h"
|
#include "ppc440.h"
|
||||||
#include "ppc405.h"
|
|
||||||
#include "hw/block/flash.h"
|
#include "hw/block/flash.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/reset.h"
|
#include "sysemu/reset.h"
|
||||||
@ -280,7 +279,6 @@ static void sam460ex_init(MachineState *machine)
|
|||||||
hwaddr ram_sizes[SDRAM_NR_BANKS] = {0};
|
hwaddr ram_sizes[SDRAM_NR_BANKS] = {0};
|
||||||
MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1);
|
MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1);
|
||||||
DeviceState *uic[4];
|
DeviceState *uic[4];
|
||||||
qemu_irq mal_irqs[4];
|
|
||||||
int i;
|
int i;
|
||||||
PCIBus *pci_bus;
|
PCIBus *pci_bus;
|
||||||
PowerPCCPU *cpu;
|
PowerPCCPU *cpu;
|
||||||
@ -309,11 +307,12 @@ static void sam460ex_init(MachineState *machine)
|
|||||||
ppc_dcr_init(env, NULL, NULL);
|
ppc_dcr_init(env, NULL, NULL);
|
||||||
|
|
||||||
/* PLB arbitrer */
|
/* 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 */
|
/* interrupt controllers */
|
||||||
for (i = 0; i < ARRAY_SIZE(uic); i++) {
|
for (i = 0; i < ARRAY_SIZE(uic); i++) {
|
||||||
SysBusDevice *sbd;
|
|
||||||
/*
|
/*
|
||||||
* UICs 1, 2 and 3 are cascaded through UIC 0.
|
* UICs 1, 2 and 3 are cascaded through UIC 0.
|
||||||
* input_ints[n] is the interrupt number on UIC 0 which
|
* 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 };
|
const int input_ints[] = { -1, 30, 10, 16 };
|
||||||
|
|
||||||
uic[i] = qdev_new(TYPE_PPC_UIC);
|
uic[i] = qdev_new(TYPE_PPC_UIC);
|
||||||
sbd = SYS_BUS_DEVICE(uic[i]);
|
|
||||||
|
|
||||||
qdev_prop_set_uint32(uic[i], "dcr-base", 0xc0 + i * 0x10);
|
qdev_prop_set_uint32(uic[i], "dcr-base", 0xc0 + i * 0x10);
|
||||||
object_property_set_link(OBJECT(uic[i]), "cpu", OBJECT(cpu),
|
ppc4xx_dcr_realize(PPC4xx_DCR_DEVICE(uic[i]), cpu, &error_fatal);
|
||||||
&error_fatal);
|
object_unref(OBJECT(uic[i]));
|
||||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
|
||||||
|
|
||||||
|
sbdev = SYS_BUS_DEVICE(uic[i]);
|
||||||
if (i == 0) {
|
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));
|
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));
|
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_CINT));
|
||||||
} else {
|
} else {
|
||||||
sysbus_connect_irq(sbd, PPCUIC_OUTPUT_INT,
|
sysbus_connect_irq(sbdev, PPCUIC_OUTPUT_INT,
|
||||||
qdev_get_gpio_in(uic[0], input_ints[i]));
|
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));
|
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));
|
qdev_get_gpio_in(uic[0], 3));
|
||||||
|
|
||||||
/* External bus controller */
|
/* 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 */
|
/* CPR */
|
||||||
ppc4xx_cpr_init(env);
|
ppc4xx_cpr_init(env);
|
||||||
@ -383,10 +382,15 @@ static void sam460ex_init(MachineState *machine)
|
|||||||
ppc4xx_sdr_init(env);
|
ppc4xx_sdr_init(env);
|
||||||
|
|
||||||
/* MAL */
|
/* MAL */
|
||||||
for (i = 0; i < ARRAY_SIZE(mal_irqs); i++) {
|
dev = qdev_new(TYPE_PPC4xx_MAL);
|
||||||
mal_irqs[i] = qdev_get_gpio_in(uic[2], 3 + i);
|
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 */
|
/* DMA */
|
||||||
ppc4xx_dma_init(env, 0x200);
|
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_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
|
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.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_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
|
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
|
# ppc405_boards.c
|
||||||
opba_readb(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32
|
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_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_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_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_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
|
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_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_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_compute(const char *param, uint32_t param2, uint32_t val) "%s 0x%1" PRIx32 " %d"
|
||||||
ppc405ep_clocks_setup(const char *trace) "%s"
|
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 */
|
/* interrupt controller */
|
||||||
uicdev = qdev_new(TYPE_PPC_UIC);
|
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);
|
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,
|
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_INT,
|
||||||
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
qdev_get_gpio_in(DEVICE(cpu), PPC40x_INPUT_INT));
|
||||||
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
sysbus_connect_irq(uicsbd, PPCUIC_OUTPUT_CINT,
|
||||||
|
@ -195,6 +195,10 @@ typedef struct float_status {
|
|||||||
bool snan_bit_is_one;
|
bool snan_bit_is_one;
|
||||||
bool use_first_nan;
|
bool use_first_nan;
|
||||||
bool no_signaling_nans;
|
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;
|
} float_status;
|
||||||
|
|
||||||
#endif /* SOFTFLOAT_TYPES_H */
|
#endif /* SOFTFLOAT_TYPES_H */
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
#ifndef HW_INTC_PPC_UIC_H
|
#ifndef HW_INTC_PPC_UIC_H
|
||||||
#define HW_INTC_PPC_UIC_H
|
#define HW_INTC_PPC_UIC_H
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#include "hw/ppc/ppc4xx.h"
|
||||||
#include "qom/object.h"
|
|
||||||
|
|
||||||
#define TYPE_PPC_UIC "ppc-uic"
|
#define TYPE_PPC_UIC "ppc-uic"
|
||||||
OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC)
|
OBJECT_DECLARE_SIMPLE_TYPE(PPCUIC, PPC_UIC)
|
||||||
@ -56,14 +55,13 @@ enum {
|
|||||||
|
|
||||||
struct PPCUIC {
|
struct PPCUIC {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
SysBusDevice parent_obj;
|
Ppc4xxDcrDeviceState parent_obj;
|
||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
qemu_irq output_int;
|
qemu_irq output_int;
|
||||||
qemu_irq output_cint;
|
qemu_irq output_cint;
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
CPUState *cpu;
|
|
||||||
uint32_t dcr_base;
|
uint32_t dcr_base;
|
||||||
bool use_vectors;
|
bool use_vectors;
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "hw/pci/pcie_port.h"
|
#include "hw/pci/pcie_port.h"
|
||||||
#include "hw/ppc/xics.h"
|
#include "hw/ppc/xics.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
#include "hw/pci-host/pnv_phb.h"
|
||||||
|
|
||||||
typedef struct PnvPHB3 PnvPHB3;
|
typedef struct PnvPHB3 PnvPHB3;
|
||||||
typedef struct PnvChip PnvChip;
|
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"
|
#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root"
|
||||||
|
struct PnvPHB3RootBus {
|
||||||
|
PCIBus parent;
|
||||||
|
|
||||||
#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port"
|
uint32_t chip_id;
|
||||||
|
uint32_t phb_id;
|
||||||
typedef struct PnvPHB3RootPort {
|
};
|
||||||
PCIESlot parent_obj;
|
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB3RootBus, PNV_PHB3_ROOT_BUS)
|
||||||
} PnvPHB3RootPort;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PHB3 PCIe Host Bridge for PowerNV machines (POWER8)
|
* 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)
|
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||||
|
|
||||||
struct PnvPHB3 {
|
struct PnvPHB3 {
|
||||||
PCIExpressHost parent_obj;
|
DeviceState parent;
|
||||||
|
|
||||||
|
PnvPHB *phb_base;
|
||||||
|
|
||||||
uint32_t chip_id;
|
uint32_t chip_id;
|
||||||
uint32_t phb_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_reg_write(void *opaque, hwaddr off, uint64_t val, unsigned size);
|
||||||
void pnv_phb3_update_regions(PnvPHB3 *phb);
|
void pnv_phb3_update_regions(PnvPHB3 *phb);
|
||||||
void pnv_phb3_remap_irqs(PnvPHB3 *phb);
|
void pnv_phb3_remap_irqs(PnvPHB3 *phb);
|
||||||
|
void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb);
|
||||||
|
|
||||||
#endif /* PCI_HOST_PNV_PHB3_H */
|
#endif /* PCI_HOST_PNV_PHB3_H */
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
typedef struct PnvPhb4PecState PnvPhb4PecState;
|
||||||
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
|
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
|
||||||
typedef struct PnvPHB4 PnvPHB4;
|
typedef struct PnvPHB4 PnvPHB4;
|
||||||
|
typedef struct PnvPHB PnvPHB;
|
||||||
typedef struct PnvChip PnvChip;
|
typedef struct PnvChip PnvChip;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -45,15 +46,16 @@ typedef struct PnvPhb4DMASpace {
|
|||||||
} PnvPhb4DMASpace;
|
} PnvPhb4DMASpace;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PHB4 PCIe Root port
|
* PHB4 PCIe Root Bus
|
||||||
*/
|
*/
|
||||||
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root"
|
#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root"
|
||||||
#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port"
|
struct PnvPHB4RootBus {
|
||||||
#define TYPE_PNV_PHB5_ROOT_PORT "pnv-phb5-root-port"
|
PCIBus parent;
|
||||||
|
|
||||||
typedef struct PnvPHB4RootPort {
|
uint32_t chip_id;
|
||||||
PCIESlot parent_obj;
|
uint32_t phb_id;
|
||||||
} PnvPHB4RootPort;
|
};
|
||||||
|
OBJECT_DECLARE_SIMPLE_TYPE(PnvPHB4RootBus, PNV_PHB4_ROOT_BUS)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PHB4 PCIe Host Bridge for PowerNV machines (POWER9)
|
* 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)
|
#define PCI_MMIO_TOTAL_SIZE (0x1ull << 60)
|
||||||
|
|
||||||
struct PnvPHB4 {
|
struct PnvPHB4 {
|
||||||
PCIExpressHost parent_obj;
|
DeviceState parent;
|
||||||
|
|
||||||
|
PnvPHB *phb_base;
|
||||||
|
|
||||||
uint32_t chip_id;
|
uint32_t chip_id;
|
||||||
uint32_t phb_id;
|
uint32_t phb_id;
|
||||||
|
|
||||||
uint64_t version;
|
|
||||||
|
|
||||||
/* The owner PEC */
|
/* The owner PEC */
|
||||||
PnvPhb4PecState *pec;
|
PnvPhb4PecState *pec;
|
||||||
|
|
||||||
@ -157,6 +159,7 @@ struct PnvPHB4 {
|
|||||||
|
|
||||||
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
|
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
|
||||||
int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index);
|
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;
|
extern const MemoryRegionOps pnv_phb4_xscom_ops;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -205,7 +208,6 @@ struct PnvPhb4PecClass {
|
|||||||
uint64_t version;
|
uint64_t version;
|
||||||
const char *phb_type;
|
const char *phb_type;
|
||||||
const uint32_t *num_phbs;
|
const uint32_t *num_phbs;
|
||||||
const char *rp_model;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -27,11 +27,13 @@
|
|||||||
#include "hw/ppc/pnv_pnor.h"
|
#include "hw/ppc/pnv_pnor.h"
|
||||||
#include "hw/ppc/pnv_psi.h"
|
#include "hw/ppc/pnv_psi.h"
|
||||||
#include "hw/ppc/pnv_occ.h"
|
#include "hw/ppc/pnv_occ.h"
|
||||||
|
#include "hw/ppc/pnv_sbe.h"
|
||||||
#include "hw/ppc/pnv_homer.h"
|
#include "hw/ppc/pnv_homer.h"
|
||||||
#include "hw/ppc/pnv_xive.h"
|
#include "hw/ppc/pnv_xive.h"
|
||||||
#include "hw/ppc/pnv_core.h"
|
#include "hw/ppc/pnv_core.h"
|
||||||
#include "hw/pci-host/pnv_phb3.h"
|
#include "hw/pci-host/pnv_phb3.h"
|
||||||
#include "hw/pci-host/pnv_phb4.h"
|
#include "hw/pci-host/pnv_phb4.h"
|
||||||
|
#include "hw/pci-host/pnv_phb.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
|
||||||
#define TYPE_PNV_CHIP "pnv-chip"
|
#define TYPE_PNV_CHIP "pnv-chip"
|
||||||
@ -80,7 +82,11 @@ struct Pnv8Chip {
|
|||||||
PnvHomer homer;
|
PnvHomer homer;
|
||||||
|
|
||||||
#define PNV8_CHIP_PHB3_MAX 4
|
#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;
|
uint32_t num_phbs;
|
||||||
|
|
||||||
XICSFabric *xics;
|
XICSFabric *xics;
|
||||||
@ -100,6 +106,7 @@ struct Pnv9Chip {
|
|||||||
Pnv9Psi psi;
|
Pnv9Psi psi;
|
||||||
PnvLpcController lpc;
|
PnvLpcController lpc;
|
||||||
PnvOCC occ;
|
PnvOCC occ;
|
||||||
|
PnvSBE sbe;
|
||||||
PnvHomer homer;
|
PnvHomer homer;
|
||||||
|
|
||||||
uint32_t nr_quads;
|
uint32_t nr_quads;
|
||||||
@ -129,6 +136,7 @@ struct Pnv10Chip {
|
|||||||
Pnv9Psi psi;
|
Pnv9Psi psi;
|
||||||
PnvLpcController lpc;
|
PnvLpcController lpc;
|
||||||
PnvOCC occ;
|
PnvOCC occ;
|
||||||
|
PnvSBE sbe;
|
||||||
PnvHomer homer;
|
PnvHomer homer;
|
||||||
|
|
||||||
uint32_t nr_quads;
|
uint32_t nr_quads;
|
||||||
@ -189,8 +197,6 @@ DECLARE_INSTANCE_CHECKER(PnvChip, PNV_CHIP_POWER10,
|
|||||||
TYPE_PNV_CHIP_POWER10)
|
TYPE_PNV_CHIP_POWER10)
|
||||||
|
|
||||||
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
|
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")
|
#define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv")
|
||||||
typedef struct PnvMachineClass PnvMachineClass;
|
typedef struct PnvMachineClass PnvMachineClass;
|
||||||
@ -232,6 +238,7 @@ struct PnvMachineState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id);
|
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_FDT_ADDR 0x01000000
|
||||||
#define PNV_TIMEBASE_FREQ 512000000ULL
|
#define PNV_TIMEBASE_FREQ 512000000ULL
|
||||||
|
55
include/hw/ppc/pnv_sbe.h
Normal file
55
include/hw/ppc/pnv_sbe.h
Normal file
@ -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_BASE PNV_XSCOM_OCC_BASE
|
||||||
#define PNV9_XSCOM_OCC_SIZE 0x8000
|
#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_BASE 0x5012b00
|
||||||
#define PNV9_XSCOM_PBA_SIZE 0x40
|
#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_BASE PNV9_XSCOM_OCC_BASE
|
||||||
#define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE
|
#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_BASE 0x01010CDA
|
||||||
#define PNV10_XSCOM_PBA_SIZE 0x40
|
#define PNV10_XSCOM_PBA_SIZE 0x40
|
||||||
|
|
||||||
|
@ -27,11 +27,7 @@
|
|||||||
|
|
||||||
#include "hw/ppc/ppc.h"
|
#include "hw/ppc/ppc.h"
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
|
#include "hw/sysbus.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);
|
|
||||||
|
|
||||||
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
void ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks,
|
||||||
MemoryRegion ram_memories[],
|
MemoryRegion ram_memories[],
|
||||||
@ -44,9 +40,73 @@ void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks,
|
|||||||
hwaddr *ram_sizes,
|
hwaddr *ram_sizes,
|
||||||
int do_init);
|
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"
|
#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 */
|
#endif /* PPC4XX_H */
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||||
implementation for certain IBM POWER hardware. The sources are at
|
implementation for certain IBM POWER hardware. The sources are at
|
||||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
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
|
- 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
|
-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);
|
void (*parent_parse_features)(const char *type, char *str, Error **errp);
|
||||||
|
|
||||||
uint32_t pvr;
|
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_mask; /* Available bits in PCR register */
|
||||||
uint64_t pcr_supported; /* Bits for supported PowerISA versions */
|
uint64_t pcr_supported; /* Bits for supported PowerISA versions */
|
||||||
uint32_t svr;
|
uint32_t svr;
|
||||||
|
@ -120,6 +120,8 @@ void ppc_store_fpscr(CPUPPCState *env, target_ulong val)
|
|||||||
val |= FP_FEX;
|
val |= FP_FEX;
|
||||||
}
|
}
|
||||||
env->fpscr = val;
|
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()) {
|
if (tcg_enabled()) {
|
||||||
fpscr_set_rounding_mode(env);
|
fpscr_set_rounding_mode(env);
|
||||||
}
|
}
|
||||||
|
@ -5912,15 +5912,25 @@ static void init_proc_POWER7(CPUPPCState *env)
|
|||||||
ppcPOWER7_irq_init(env_archcpu(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) {
|
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||||
return true;
|
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)
|
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));
|
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) {
|
uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK;
|
||||||
return true;
|
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) {
|
if (base != pcc_base) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
if ((pvr & CPU_POWERPC_POWER_SERVER_MASK) == CPU_POWERPC_POWER8_BASE) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cpu_has_work_POWER8(CPUState *cs)
|
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));
|
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 true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6499,11 +6533,26 @@ static void init_proc_POWER10(CPUPPCState *env)
|
|||||||
ppcPOWER9_irq_init(env_archcpu(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 true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6910,7 +6959,7 @@ static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcc->pvr_match(pcc, pvr)) {
|
if (pcc->pvr_match(pcc, pvr, true)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7308,7 +7357,7 @@ static void ppc_cpu_instance_finalize(Object *obj)
|
|||||||
ppc_hash64_finalize(cpu);
|
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;
|
return pcc->pvr == pvr;
|
||||||
}
|
}
|
||||||
|
@ -348,7 +348,6 @@ static inline int float_overflow_excp(CPUPPCState *env)
|
|||||||
|
|
||||||
bool overflow_enabled = !!(env->fpscr & FP_OE);
|
bool overflow_enabled = !!(env->fpscr & FP_OE);
|
||||||
if (overflow_enabled) {
|
if (overflow_enabled) {
|
||||||
/* XXX: should adjust the result */
|
|
||||||
/* Update the floating-point enabled exception summary */
|
/* Update the floating-point enabled exception summary */
|
||||||
env->fpscr |= FP_FEX;
|
env->fpscr |= FP_FEX;
|
||||||
/* We must update the target FPR before raising the exception */
|
/* 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 */
|
/* Update the floating-point exception summary */
|
||||||
env->fpscr |= FP_FX;
|
env->fpscr |= FP_FX;
|
||||||
if (env->fpscr & FP_UE) {
|
if (env->fpscr & FP_UE) {
|
||||||
/* XXX: should adjust the result */
|
|
||||||
/* Update the floating-point enabled exception summary */
|
/* Update the floating-point enabled exception summary */
|
||||||
env->fpscr |= FP_FEX;
|
env->fpscr |= FP_FEX;
|
||||||
/* We must update the target FPR before raising the exception */
|
/* 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) {
|
if (pvr == pcc->pvr) {
|
||||||
return true;
|
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)
|
static int cpu_post_load(void *opaque, int version_id)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user