qemu-ppc
[Top][All Lists]
Advanced

[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

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]