[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Cont
From: |
David Gibson |
Subject: |
Re: [Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller |
Date: |
Fri, 14 Oct 2016 17:43:32 +1100 |
User-agent: |
Mutt/1.7.0 (2016-08-17) |
On Mon, Oct 03, 2016 at 09:24:56AM +0200, Cédric Le Goater wrote:
> 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));
Urgh. I think doing object_new() from realize is frowned upon.
Unfortunately, I don't know what the right way to do this is :/.
> + 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 */
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
signature.asc
Description: PGP signature
- 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, 2016/10/03
- Re: [Qemu-ppc] [PATCH v4 20/20] ppc/pnv: add support for POWER9 LPC Controller,
David Gibson <=
Re: [Qemu-ppc] [Qemu-devel] [PATCH v4 00/20] ppc/pnv: booting the kernel and reaching user space, no-reply, 2016/10/03