]> git.proxmox.com Git - mirror_qemu.git/blobdiff - hw/ppc/pnv_lpc.c
machine: show if CLI option '-numa node,mem' is supported in QAPI schema
[mirror_qemu.git] / hw / ppc / pnv_lpc.c
index 9b18ce55e39191ff63ad29c9eb80d6d7a7ae4f44..a9f150c3cab0be18ad8b2ce9a07972964755ff7f 100644 (file)
@@ -22,6 +22,7 @@
 #include "target/ppc/cpu.h"
 #include "qapi/error.h"
 #include "qemu/log.h"
+#include "qemu/module.h"
 #include "hw/isa/isa.h"
 
 #include "hw/ppc/pnv.h"
@@ -39,6 +40,8 @@ enum {
 };
 
 /* OPB Master LS registers */
+#define OPB_MASTER_LS_ROUTE0    0x8
+#define OPB_MASTER_LS_ROUTE1    0xC
 #define OPB_MASTER_LS_IRQ_STAT  0x50
 #define   OPB_MASTER_IRQ_LPC            0x00000800
 #define OPB_MASTER_LS_IRQ_MASK  0x54
@@ -89,10 +92,11 @@ enum {
 #define LPC_FW_OPB_SIZE         0x10000000
 
 #define LPC_OPB_REGS_OPB_ADDR   0xc0010000
-#define LPC_OPB_REGS_OPB_SIZE   0x00002000
+#define LPC_OPB_REGS_OPB_SIZE   0x00000060
+#define LPC_OPB_REGS_OPBA_ADDR  0xc0011000
+#define LPC_OPB_REGS_OPBA_SIZE  0x00000008
 #define LPC_HC_REGS_OPB_ADDR    0xc0012000
-#define LPC_HC_REGS_OPB_SIZE    0x00001000
-
+#define LPC_HC_REGS_OPB_SIZE    0x00000100
 
 static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset)
 {
@@ -117,6 +121,100 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset)
     return 0;
 }
 
+/* POWER9 only */
+int pnv_dt_lpc(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;
+    uint64_t lpcm_addr = PNV9_LPCM_BASE(chip);
+    uint32_t opb_ranges[8] = { 0,
+                               cpu_to_be32(lpcm_addr >> 32),
+                               cpu_to_be32((uint32_t)lpcm_addr),
+                               cpu_to_be32(PNV9_LPCM_SIZE / 2),
+                               cpu_to_be32(PNV9_LPCM_SIZE / 2),
+                               cpu_to_be32(lpcm_addr >> 32),
+                               cpu_to_be32(PNV9_LPCM_SIZE / 2),
+                               cpu_to_be32(PNV9_LPCM_SIZE / 2),
+    };
+    uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32),
+                            cpu_to_be32((uint32_t)lpcm_addr),
+                            cpu_to_be32(PNV9_LPCM_SIZE >> 32),
+                            cpu_to_be32((uint32_t)PNV9_LPCM_SIZE),
+    };
+    uint32_t reg[2];
+
+    /*
+     * OPB bus
+     */
+    name = g_strdup_printf("lpcm-opb@%"PRIx64, lpcm_addr);
+    lpcm_offset = fdt_add_subnode(fdt, root_offset, name);
+    _FDT(lpcm_offset);
+    g_free(name);
+
+    _FDT((fdt_setprop(fdt, lpcm_offset, "reg", opb_reg, sizeof(opb_reg))));
+    _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", opb_ranges,
+                      sizeof(opb_ranges))));
+
+    /*
+     * OPB Master registers
+     */
+    name = g_strdup_printf("opb-master@%x", 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")));
+
+    /*
+     * OPB arbitrer registers
+     */
+    name = g_strdup_printf("opb-arbitrer@%x", LPC_OPB_REGS_OPBA_ADDR);
+    offset = fdt_add_subnode(fdt, lpcm_offset, name);
+    _FDT(offset);
+    g_free(name);
+
+    reg[0] = cpu_to_be32(LPC_OPB_REGS_OPBA_ADDR);
+    reg[1] = cpu_to_be32(LPC_OPB_REGS_OPBA_SIZE);
+    _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+    _FDT((fdt_setprop_string(fdt, offset, "compatible",
+                             "ibm,power9-lpcm-opb-arbiter")));
+
+    /*
+     * LPC Host Controller registers
+     */
+    name = g_strdup_printf("lpc-controller@%x", 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("lpc@0");
+    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, "compatible", lpc_compat,
+                      sizeof(lpc_compat))));
+
+    return 0;
+}
+
 /*
  * These read/write handlers of the OPB address space should be common
  * with the P9 LPC Controller which uses direct MMIOs.
@@ -241,9 +339,78 @@ 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,
+};
+
 static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
 {
     bool lpc_to_opb_irq = false;
+    PnvLpcClass *plc = PNV_LPC_GET_CLASS(lpc);
 
     /* Update LPC controller to OPB line */
     if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) {
@@ -266,7 +433,7 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc)
     lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask;
 
     /* Reflect the interrupt */
-    pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0);
+    pnv_psi_irq_set(lpc->psi, plc->psi_irq, lpc->opb_irq_stat != 0);
 }
 
 static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
@@ -357,6 +524,12 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
     uint64_t val = 0xfffffffffffffffful;
 
     switch (addr) {
+    case OPB_MASTER_LS_ROUTE0: /* TODO */
+        val = lpc->opb_irq_route0;
+        break;
+    case OPB_MASTER_LS_ROUTE1: /* TODO */
+        val = lpc->opb_irq_route1;
+        break;
     case OPB_MASTER_LS_IRQ_STAT:
         val = lpc->opb_irq_stat;
         break;
@@ -383,6 +556,12 @@ static void opb_master_write(void *opaque, hwaddr addr,
     PnvLpcController *lpc = opaque;
 
     switch (addr) {
+    case OPB_MASTER_LS_ROUTE0: /* TODO */
+        lpc->opb_irq_route0 = val;
+        break;
+    case OPB_MASTER_LS_ROUTE1: /* TODO */
+        lpc->opb_irq_route1 = val;
+        break;
     case OPB_MASTER_LS_IRQ_STAT:
         lpc->opb_irq_stat &= ~val;
         pnv_lpc_eval_irqs(lpc);
@@ -418,11 +597,102 @@ static const MemoryRegionOps opb_master_ops = {
     },
 };
 
+static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp)
+{
+    PnvLpcController *lpc = PNV_LPC(dev);
+    PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    plc->parent_realize(dev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    /* P8 uses a XSCOM region for LPC registers */
+    pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(lpc),
+                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
+                          PNV_XSCOM_LPC_SIZE);
+}
+
+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);
+
+    dc->desc = "PowerNV LPC Controller POWER8";
+
+    xdc->dt_xscom = pnv_lpc_dt_xscom;
+
+    plc->psi_irq = PSIHB_IRQ_LPC_I2C;
+
+    device_class_set_parent_realize(dc, pnv_lpc_power8_realize,
+                                    &plc->parent_realize);
+}
+
+static const TypeInfo pnv_lpc_power8_info = {
+    .name          = TYPE_PNV8_LPC,
+    .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_realize(DeviceState *dev, Error **errp)
+{
+    PnvLpcController *lpc = PNV_LPC(dev);
+    PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    plc->parent_realize(dev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    /* P9 uses a MMIO region */
+    memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops,
+                          lpc, "lpcm", PNV9_LPCM_SIZE);
+}
+
+static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PnvLpcClass *plc = PNV_LPC_CLASS(klass);
+
+    dc->desc = "PowerNV LPC Controller POWER9";
+
+    plc->psi_irq = PSIHB9_IRQ_LPCHC;
+
+    device_class_set_parent_realize(dc, pnv_lpc_power9_realize,
+                                    &plc->parent_realize);
+}
+
+static const TypeInfo pnv_lpc_power9_info = {
+    .name          = TYPE_PNV9_LPC,
+    .parent        = TYPE_PNV_LPC,
+    .instance_size = sizeof(PnvLpcController),
+    .class_init    = pnv_lpc_power9_class_init,
+};
+
 static void pnv_lpc_realize(DeviceState *dev, Error **errp)
 {
     PnvLpcController *lpc = PNV_LPC(dev);
     Object *obj;
-    Error *error = NULL;
+    Error *local_err = NULL;
+
+    obj = object_property_get_link(OBJECT(dev), "psi", &local_err);
+    if (!obj) {
+        error_propagate(errp, local_err);
+        error_prepend(errp, "required link 'psi' not found: ");
+        return;
+    }
+    /* The LPC controller needs PSI to generate interrupts  */
+    lpc->psi = PNV_PSI(obj);
 
     /* Reg inits */
     lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
@@ -462,46 +732,29 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp)
                           "lpc-hc", LPC_HC_REGS_OPB_SIZE);
     memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
                                 &lpc->lpc_hc_regs);
-
-    /* XScom region for LPC registers */
-    pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
-                          &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
-                          PNV_XSCOM_LPC_SIZE);
-
-    /* get PSI object from chip */
-    obj = object_property_get_link(OBJECT(dev), "psi", &error);
-    if (!obj) {
-        error_setg(errp, "%s: required link 'psi' not found: %s",
-                   __func__, error_get_pretty(error));
-        return;
-    }
-    lpc->psi = PNV_PSI(obj);
 }
 
 static void pnv_lpc_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
-
-    xdc->dt_xscom = pnv_lpc_dt_xscom;
 
     dc->realize = pnv_lpc_realize;
+    dc->desc = "PowerNV LPC Controller";
 }
 
 static const TypeInfo pnv_lpc_info = {
     .name          = TYPE_PNV_LPC,
     .parent        = TYPE_DEVICE,
-    .instance_size = sizeof(PnvLpcController),
     .class_init    = pnv_lpc_class_init,
-    .interfaces = (InterfaceInfo[]) {
-        { TYPE_PNV_XSCOM_INTERFACE },
-        { }
-    }
+    .class_size    = sizeof(PnvLpcClass),
+    .abstract      = true,
 };
 
 static void pnv_lpc_register_types(void)
 {
     type_register_static(&pnv_lpc_info);
+    type_register_static(&pnv_lpc_power8_info);
+    type_register_static(&pnv_lpc_power9_info);
 }
 
 type_init(pnv_lpc_register_types)