[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controll
From: |
Cédric Le Goater |
Subject: |
[Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller |
Date: |
Mon, 3 Oct 2016 09:24:56 +0200 |
The LPC Controller on POWER9 is very similar to the one found on
POWER8 but accesses are now done via on MMIOs, without the XSCOM and
ECCB logic. The device tree is populated differently so we add a
PnvLpcClass and a couple of methods for the purpose.
LPC interrupts routing is missing. This is Work In Progress but it
gives us a P9 console and, more important, a first idea of what
changes we should consider for the following models.
Signed-off-by: Cédric Le Goater <address@hidden>
---
hw/ppc/pnv.c | 32 +++---
hw/ppc/pnv_lpc.c | 255 ++++++++++++++++++++++++++++++++++++++++++++---
include/hw/ppc/pnv.h | 9 +-
include/hw/ppc/pnv_lpc.h | 33 ++++++
4 files changed, 304 insertions(+), 25 deletions(-)
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
index 5b70ccf66fac..d5ef4f8ddb53 100644
--- a/hw/ppc/pnv.c
+++ b/hw/ppc/pnv.c
@@ -237,6 +237,8 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
xics_native_populate_icp(chip, fdt, 0, pnv_core->pir, smt);
}
+ pnv_lpc_populate(chip, fdt, 0);
+
/* Put all the memory in one node on chip 0 until we find a way to
* specify different ranges for each chip
*/
@@ -355,7 +357,7 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n,
int level)
static ISABus *pnv_isa_create(PnvChip *chip)
{
- PnvLpcController *lpc = &chip->lpc;
+ PnvLpcController *lpc = chip->lpc;
ISABus *isa_bus;
qemu_irq *irqs;
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
@@ -653,9 +655,6 @@ static void pnv_chip_init(Object *obj)
chip->xscom_base = pcc->xscom_base;
- object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
- object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
-
object_initialize(&chip->xics, sizeof(chip->xics), TYPE_XICS_NATIVE);
object_property_add_child(obj, "xics", OBJECT(&chip->xics), NULL);
@@ -667,11 +666,6 @@ static void pnv_chip_init(Object *obj)
object_property_add_const_link(OBJECT(&chip->occ), "psi",
OBJECT(&chip->psi), &error_abort);
- /*
- * The LPC controller needs PSI to generate interrupts
- */
- object_property_add_const_link(OBJECT(&chip->lpc), "psi",
- OBJECT(&chip->psi), &error_abort);
}
static void pnv_chip_realize(DeviceState *dev, Error **errp)
@@ -757,10 +751,24 @@ static void pnv_chip_realize(DeviceState *dev, Error
**errp)
xics_insert_ics(XICS_COMMON(&chip->xics), &chip->psi.ics);
/* Create LPC controller */
- object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
+ typename = g_strdup_printf(TYPE_PNV_LPC "-%s", pcc->cpu_model);
+ chip->lpc = PNV_LPC(object_new(typename));
+ object_property_add_child(OBJECT(chip), "lpc", OBJECT(chip->lpc), NULL);
+
+ /* The LPC controller needs PSI to generate interrupts */
+ object_property_add_const_link(OBJECT(chip->lpc), "psi",
+ OBJECT(&chip->psi), &error_abort);
+ object_property_set_bool(OBJECT(chip->lpc), true, "realized",
&error_fatal);
- memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
- &chip->lpc.xscom_regs);
+
+ if (PNV_CHIP_GET_CLASS(chip)->chip_type != PNV_CHIP_POWER9) {
+ memory_region_add_subregion(&chip->xscom, PNV_XSCOM_LPC_BASE << 3,
+ &chip->lpc->xscom_regs);
+ } else {
+ memory_region_add_subregion(get_system_memory(), PNV_POWER9_LPCM_BASE,
+ &chip->lpc->xscom_regs);
+ }
+
/* Create the simplified OCC model */
object_property_set_bool(OBJECT(&chip->occ), true, "realized",
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
index 8b78b0a1e414..93ccc656120c 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -28,6 +28,7 @@
#include "hw/ppc/fdt.h"
#include <libfdt.h>
+#include "exec/address-spaces.h"
enum {
ECCB_CTL = 0,
@@ -91,6 +92,88 @@ enum {
#define LPC_HC_REGS_OPB_SIZE 0x00001000
+int pnv_lpc_populate(PnvChip *chip, void *fdt, int offset)
+{
+ PnvLpcClass *plc = PNV_LPC_GET_CLASS(chip->lpc);
+
+ if (plc->populate) {
+ return plc->populate(chip, fdt, offset);
+ }
+ return 0;
+}
+
+static int pnv_lpc_power9_populate(PnvChip *chip, void *fdt, int root_offset)
+{
+ const char compat[] = "ibm,power9-lpcm-opb\0simple-bus";
+ const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc";
+ char *name;
+ int offset, lpcm_offset;
+ /* should depend on a MMIO base address of the chip */
+ uint64_t lpcm_addr = PNV_POWER9_LPCM_BASE;
+ /* TODO: build the ranges properly, array of :
+ * { offset, addr hi, addr low, size }
+ */
+ uint32_t ranges[4] = { 0,
+ cpu_to_be32(lpcm_addr >> 32),
+ cpu_to_be32((uint32_t)lpcm_addr),
+ cpu_to_be32(PNV_POWER9_LPCM_SIZE / 2),
+ };
+ uint32_t reg[2];
+
+ /*
+ * OPB bus
+ */
+ name = g_strdup_printf("address@hidden"PRIx64, lpcm_addr);
+ lpcm_offset = fdt_add_subnode(fdt, root_offset, name);
+ _FDT(lpcm_offset);
+ g_free(name);
+ _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#address-cells", 1)));
+ _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#size-cells", 1)));
+ _FDT((fdt_setprop(fdt, lpcm_offset, "compatible", compat,
sizeof(compat))));
+ _FDT((fdt_setprop_cell(fdt, lpcm_offset, "ibm,chip-id", chip->chip_id)));
+ _FDT((fdt_setprop(fdt, lpcm_offset, "ranges", ranges, sizeof(ranges))));
+
+ /*
+ * OPB Master registers
+ */
+ name = g_strdup_printf("address@hidden", LPC_OPB_REGS_OPB_ADDR);
+ offset = fdt_add_subnode(fdt, lpcm_offset, name);
+ _FDT(offset);
+ g_free(name);
+
+ reg[0] = cpu_to_be32(LPC_OPB_REGS_OPB_ADDR);
+ reg[1] = cpu_to_be32(LPC_OPB_REGS_OPB_SIZE);
+ _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+ _FDT((fdt_setprop_string(fdt, offset, "compatible",
+ "ibm,power9-lpcm-opb-master")));
+
+ /*
+ * LPC Host Controller registers
+ */
+ name = g_strdup_printf("address@hidden", LPC_HC_REGS_OPB_ADDR);
+ offset = fdt_add_subnode(fdt, lpcm_offset, name);
+ _FDT(offset);
+ g_free(name);
+
+ reg[0] = cpu_to_be32(LPC_HC_REGS_OPB_ADDR);
+ reg[1] = cpu_to_be32(LPC_HC_REGS_OPB_SIZE);
+ _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+ _FDT((fdt_setprop_string(fdt, offset, "compatible",
+ "ibm,power9-lpc-controller")));
+
+ name = g_strdup_printf("address@hidden", LPC_FW_OPB_ADDR);
+ offset = fdt_add_subnode(fdt, lpcm_offset, name);
+ _FDT(offset);
+ g_free(name);
+ _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
+ _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
+ _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
+ _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat,
+ sizeof(lpc_compat))));
+
+ return 0;
+}
+
/*
* TODO: the "primary" cell should only be added on chip 0. This is
* how skiboot chooses the default LPC controller on multichip
@@ -99,7 +182,8 @@ enum {
* It would be easly done if we can change the populate() interface to
* replace the PnvXScomInterface parameter by a PnvChip one
*/
-static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int
xscom_offset)
+static int pnv_lpc_power8_populate(PnvXScomInterface *dev, void *fdt,
+ int xscom_offset)
{
const char compat[] = "ibm,power8-lpc\0ibm,lpc";
char *name;
@@ -249,6 +333,74 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
.endianness = DEVICE_BIG_ENDIAN,
};
+static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+ PnvLpcController *lpc = PNV_LPC(opaque);
+ uint64_t val = 0;
+ uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
+ MemTxResult result;
+
+ switch (size) {
+ case 4:
+ val = address_space_ldl(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED,
+ &result);
+ break;
+ case 1:
+ val = address_space_ldub(&lpc->opb_as, opb_addr,
MEMTXATTRS_UNSPECIFIED,
+ &result);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
+ HWADDR_PRIx " invalid size %d\n", addr, size);
+ return 0;
+ }
+
+ if (result != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%"
+ HWADDR_PRIx "\n", addr);
+ }
+
+ return val;
+}
+
+static void pnv_lpc_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvLpcController *lpc = PNV_LPC(opaque);
+ uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK;
+ MemTxResult result;
+
+ switch (size) {
+ case 4:
+ address_space_stl(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
+ &result);
+ break;
+ case 1:
+ address_space_stb(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED,
+ &result);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
+ HWADDR_PRIx " invalid size %d\n", addr, size);
+ return;
+ }
+
+ if (result != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%"
+ HWADDR_PRIx "\n", addr);
+ }
+}
+
+static const MemoryRegionOps pnv_lpc_mmio_ops = {
+ .read = pnv_lpc_mmio_read,
+ .write = pnv_lpc_mmio_write,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
void pnv_lpc_eval_irqs(PnvLpcController *lpc)
{
bool lpc_to_opb_irq = false;
@@ -426,9 +578,29 @@ static const MemoryRegionOps opb_master_ops = {
},
};
+static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp)
+{
+ PnvLpcController *lpc = PNV_LPC(dev);
+
+ /* XScom region for LPC registers */
+ memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
+ &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
+ PNV_XSCOM_LPC_SIZE << 3);
+}
+
+static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp)
+{
+ PnvLpcController *lpc = PNV_LPC(dev);
+
+ /* Initialize MMIO region */
+ memory_region_init_io(&lpc->xscom_regs, OBJECT(dev), &pnv_lpc_mmio_ops,
lpc,
+ "lpcm", PNV_POWER9_LPCM_SIZE);
+}
+
static void pnv_lpc_realize(DeviceState *dev, Error **errp)
{
PnvLpcController *lpc = PNV_LPC(dev);
+ PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
Object *obj;
Error *error = NULL;
@@ -470,11 +642,6 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
&lpc->lpc_hc_regs);
- /* XScom region for LPC registers */
- memory_region_init_io(&lpc->xscom_regs, OBJECT(dev),
- &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
- PNV_XSCOM_LPC_SIZE << 3);
-
/* get PSI object from chip */
obj = object_property_get_link(OBJECT(dev), "psi", &error);
if (!obj) {
@@ -483,32 +650,96 @@ static void pnv_lpc_realize(DeviceState *dev, Error
**errp)
return;
}
lpc->psi = PNV_PSI(obj);
+
+ plc->realize(dev, errp);
}
-static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+ PnvLpcClass *plc = PNV_LPC_CLASS(klass);
- xdc->populate = pnv_lpc_populate;
+ xdc->populate = pnv_lpc_power8_populate;
dc->realize = pnv_lpc_realize;
+ plc->realize = pnv_lpc_power8_realize;
}
-static const TypeInfo pnv_lpc_info = {
- .name = TYPE_PNV_LPC,
- .parent = TYPE_DEVICE,
+static const TypeInfo pnv_lpc_power8e_info = {
+ .name = TYPE_PNV_LPC_POWER8E,
+ .parent = TYPE_PNV_LPC,
.instance_size = sizeof(PnvLpcController),
- .class_init = pnv_lpc_class_init,
+ .class_init = pnv_lpc_power8_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_PNV_XSCOM_INTERFACE },
+ { }
+ }
+};
+
+static const TypeInfo pnv_lpc_power8_info = {
+ .name = TYPE_PNV_LPC_POWER8,
+ .parent = TYPE_PNV_LPC,
+ .instance_size = sizeof(PnvLpcController),
+ .class_init = pnv_lpc_power8_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_PNV_XSCOM_INTERFACE },
+ { }
+ }
+};
+
+static const TypeInfo pnv_lpc_power8nvl_info = {
+ .name = TYPE_PNV_LPC_POWER8NVL,
+ .parent = TYPE_PNV_LPC,
+ .instance_size = sizeof(PnvLpcController),
+ .class_init = pnv_lpc_power8_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_PNV_XSCOM_INTERFACE },
{ }
}
};
+static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvLpcClass *plc = PNV_LPC_CLASS(klass);
+
+ dc->realize = pnv_lpc_realize;
+ plc->realize = pnv_lpc_power9_realize;
+ plc->populate = pnv_lpc_power9_populate;
+}
+
+static const TypeInfo pnv_lpc_power9_info = {
+ .name = TYPE_PNV_LPC_POWER9,
+ .parent = TYPE_PNV_LPC,
+ .instance_size = sizeof(PnvLpcController),
+ .class_init = pnv_lpc_power9_class_init,
+};
+
+static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = pnv_lpc_realize;
+ dc->desc = "PowerNV LPC Controller";
+}
+
+static const TypeInfo pnv_lpc_info = {
+ .name = TYPE_PNV_LPC,
+ .parent = TYPE_DEVICE,
+ .class_init = pnv_lpc_class_init,
+ .class_size = sizeof(PnvLpcClass),
+ .abstract = true,
+};
+
+
static void pnv_lpc_register_types(void)
{
type_register_static(&pnv_lpc_info);
+ type_register_static(&pnv_lpc_power8e_info);
+ type_register_static(&pnv_lpc_power8_info);
+ type_register_static(&pnv_lpc_power8nvl_info);
+ type_register_static(&pnv_lpc_power9_info);
}
type_init(pnv_lpc_register_types)
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
index ed3316501326..361af311a92e 100644
--- a/include/hw/ppc/pnv.h
+++ b/include/hw/ppc/pnv.h
@@ -57,7 +57,7 @@ typedef struct PnvChip {
uint64_t cores_mask;
void *cores;
- PnvLpcController lpc;
+ PnvLpcController *lpc;
XICSNative xics;
PnvPsiController psi;
PnvOCC occ;
@@ -154,4 +154,11 @@ typedef struct PnvMachineState {
#define PNV_PSIHB_BAR_SIZE 0x0000000000100000ull
+/*
+ * POWER9 MMIO regions
+ */
+#define PNV_POWER9_MMIO_BASE 0x006000000000000ull
+#define PNV_POWER9_LPCM_BASE (PNV_POWER9_MMIO_BASE + 0x30000000000ull)
+#define PNV_POWER9_LPCM_SIZE 0x100000000ull
+
#endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
index fc348dca50ca..510fab2fd115 100644
--- a/include/hw/ppc/pnv_lpc.h
+++ b/include/hw/ppc/pnv_lpc.h
@@ -22,8 +22,13 @@
#define TYPE_PNV_LPC "pnv-lpc"
#define PNV_LPC(obj) \
OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
+#define PNV_LPC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PnvLpcClass, (klass), TYPE_PNV_LPC)
+#define PNV_LPC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PnvLpcClass, (obj), TYPE_PNV_LPC)
typedef struct PnvPsiController PnvPsiController;
+typedef struct PnvChip PnvChip;
typedef struct PnvLpcController {
DeviceState parent;
@@ -68,7 +73,35 @@ typedef struct PnvLpcController {
MemoryRegion xscom_regs;
} PnvLpcController;
+typedef struct PnvLpcClass {
+ DeviceClass parent_class;
+
+ int (*populate)(PnvChip *chip, void *fdt, int offset);
+ void (*realize)(DeviceState *dev, Error **errp);
+} PnvLpcClass;
+
+
+#define TYPE_PNV_LPC_POWER8E TYPE_PNV_LPC "-POWER8E"
+#define PNV_LPC_POWER8E(obj) \
+ OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8E)
+
+#define TYPE_PNV_LPC_POWER8 TYPE_PNV_LPC "-POWER8"
+#define PNV_LPC_POWER8(obj) \
+ OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8)
+
+#define TYPE_PNV_LPC_POWER8NVL TYPE_PNV_LPC "-POWER8NVL"
+#define PNV_LPC_POWER8NVL(obj) \
+ OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER8NVL)
+
+#define TYPE_PNV_LPC_POWER9 TYPE_PNV_LPC "-POWER9"
+#define PNV_LPC_POWER9(obj) \
+ OBJECT_CHECK(PnvLpc, (obj), TYPE_PNV_LPC_POWER9)
+
+
+
#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */
void pnv_lpc_eval_irqs(PnvLpcController *lpc);
+int pnv_lpc_populate(PnvChip *chip, void *fdt, int root_offset);
+
#endif /* _PPC_PNV_LPC_H */
--
2.7.4
- Re: [Qemu-ppc] [PATCH v4 17/20] ppc/pnv: Add cut down PSI bridge model and hookup external interrupt, (continued)
[Qemu-ppc] [PATCH v4 18/20] ppc/pnv: Add OCC model stub with interrupt support, Cédric Le Goater, 2016/10/03
[Qemu-ppc] [PATCH v4 19/20] ppc/pnv: Add Naples chip support for LPC interrupts, Cédric Le Goater, 2016/10/03
[Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller,
Cédric Le Goater <=
Re: [Qemu-ppc] [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space, no-reply, 2016/10/03