From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50231) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bmb40-0000fh-4u for qemu-devel@nongnu.org; Wed, 21 Sep 2016 02:31:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bmb3w-00023U-TD for qemu-devel@nongnu.org; Wed, 21 Sep 2016 02:31:24 -0400 Date: Wed, 21 Sep 2016 16:23:47 +1000 From: David Gibson Message-ID: <20160921062347.GE20488@umbus> References: <1473943560-14846-1-git-send-email-clg@kaod.org> <1473943560-14846-10-git-send-email-clg@kaod.org> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="ANwsuk3Muel3MOM0" Content-Disposition: inline In-Reply-To: <1473943560-14846-10-git-send-email-clg@kaod.org> Subject: Re: [Qemu-devel] [PATCH v3 09/10] ppc/pnv: add a LPC controller List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: =?iso-8859-1?Q?C=E9dric?= Le Goater Cc: qemu-ppc@nongnu.org, Benjamin Herrenschmidt , qemu-devel@nongnu.org --ANwsuk3Muel3MOM0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Thu, Sep 15, 2016 at 02:45:59PM +0200, C=E9dric Le Goater wrote: > From: Benjamin Herrenschmidt >=20 > This version of the LPC controller model doesn't yet implement > support for the SerIRQ deserializer present in the Naples version > of the chip though some preliminary work is there. >=20 > Signed-off-by: Benjamin Herrenschmidt > [clg: - updated for qemu-2.7 > - ported on latest PowerNV patchset (v3) > - changed the XSCOM interface to fit new model > - qomified the model > - moved the ISA hunks in another patch > - removed printf logging > - added a couple of UNIMP logging ] > Signed-off-by: C=E9dric Le Goater > --- > hw/ppc/Makefile.objs | 2 +- > hw/ppc/pnv.c | 15 ++ > hw/ppc/pnv_lpc.c | 455 +++++++++++++++++++++++++++++++++++++++= ++++++ > include/hw/ppc/pnv.h | 3 + > include/hw/ppc/pnv_lpc.h | 63 +++++++ > include/hw/ppc/pnv_xscom.h | 3 + > 6 files changed, 540 insertions(+), 1 deletion(-) > create mode 100644 hw/ppc/pnv_lpc.c > create mode 100644 include/hw/ppc/pnv_lpc.h >=20 > diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs > index 08c213c40684..ebc72af0a7c6 100644 > --- a/hw/ppc/Makefile.objs > +++ b/hw/ppc/Makefile.objs > @@ -6,7 +6,7 @@ obj-$(CONFIG_PSERIES) +=3D spapr_hcall.o spapr_iommu.o sp= apr_rtas.o > obj-$(CONFIG_PSERIES) +=3D spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng= =2Eo > obj-$(CONFIG_PSERIES) +=3D spapr_cpu_core.o > # IBM PowerNV > -obj-$(CONFIG_POWERNV) +=3D pnv.o pnv_xscom.o pnv_core.o > +obj-$(CONFIG_POWERNV) +=3D pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o > ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) > obj-y +=3D spapr_pci_vfio.o > endif > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c > index 6a3d1fbf8403..1aa7b8ee8903 100644 > --- a/hw/ppc/pnv.c > +++ b/hw/ppc/pnv.c > @@ -569,6 +569,14 @@ static void pnv_chip_init(Object *obj) > object_property_add_child(obj, "xscom", OBJECT(&chip->xscom), NULL); > object_property_add_const_link(OBJECT(&chip->xscom), "chip", > OBJECT(chip), &error_abort); > + > + /* > + * Add the lpc controller as an XScom child as we need to populate > + * the device tree for the isa bus. > + */ > + object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); > + object_property_add_child(OBJECT(&chip->xscom), "lpc", > + OBJECT(&chip->lpc), NULL); > } > =20 > static void pnv_chip_realize(DeviceState *dev, Error **errp) > @@ -626,6 +634,13 @@ static void pnv_chip_realize(DeviceState *dev, Error= **errp) > } > g_free(typename); > =20 > + /* Create LPC controller */ > + object_property_set_bool(OBJECT(&chip->lpc), true, "realized", > + &error_fatal); > + memory_region_add_subregion(&chip->xscom.xscom_mr, > + pcc->xscom_addr(PNV_XSCOM_LPC_BASE), > + &chip->lpc.xscom_regs); Again, I think the memory region construction should be in the actual LPC device's realize(). > + > if (pcc->realize) { > pcc->realize(chip, errp); > } > diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c > new file mode 100644 > index 000000000000..de4bab0ca085 > --- /dev/null > +++ b/hw/ppc/pnv_lpc.c > @@ -0,0 +1,455 @@ > +/* > + * QEMU PowerPC PowerNV LPC controller > + * > + * Copyright (c) 2016, 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 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 . > + */ > + > +#include "qemu/osdep.h" > +#include "sysemu/sysemu.h" > +#include "target-ppc/cpu.h" > +#include "qapi/error.h" > +#include "qemu/log.h" > + > +#include "hw/ppc/pnv_lpc.h" > +#include "hw/ppc/pnv.h" > +#include "hw/ppc/fdt.h" > + > +#include > + > +enum { > + ECCB_CTL =3D 0, > + ECCB_RESET =3D 1, > + ECCB_STAT =3D 2, > + ECCB_DATA =3D 3, > +}; > + > +/* OPB Master LS registers */ > +#define OPB_MASTER_LS_IRQ_STAT 0x50 > +#define OPB_MASTER_IRQ_LPC 0x00000800 > +#define OPB_MASTER_LS_IRQ_MASK 0x54 > +#define OPB_MASTER_LS_IRQ_POL 0x58 > +#define OPB_MASTER_LS_IRQ_INPUT 0x5c > + > +/* LPC HC registers */ > +#define LPC_HC_FW_SEG_IDSEL 0x24 > +#define LPC_HC_FW_RD_ACC_SIZE 0x28 > +#define LPC_HC_FW_RD_1B 0x00000000 > +#define LPC_HC_FW_RD_2B 0x01000000 > +#define LPC_HC_FW_RD_4B 0x02000000 > +#define LPC_HC_FW_RD_16B 0x04000000 > +#define LPC_HC_FW_RD_128B 0x07000000 > +#define LPC_HC_IRQSER_CTRL 0x30 > +#define LPC_HC_IRQSER_EN 0x80000000 > +#define LPC_HC_IRQSER_QMODE 0x40000000 > +#define LPC_HC_IRQSER_START_MASK 0x03000000 > +#define LPC_HC_IRQSER_START_4CLK 0x00000000 > +#define LPC_HC_IRQSER_START_6CLK 0x01000000 > +#define LPC_HC_IRQSER_START_8CLK 0x02000000 > +#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQST= AT */ > +#define LPC_HC_IRQSTAT 0x38 > +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to .= =2E. */ > +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=3DIOCHK#, IR= Q2=3DSMI# */ > +#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 > +#define LPC_HC_IRQ_LRESET 0x00000400 > +#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 > +#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 > +#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 > +#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 > +#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 > +#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 > +#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 > +#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 > +#define LPC_HC_ERROR_ADDRESS 0x40 > + > +#define ISA_IO_SIZE 0x00010000 > +#define ISA_MEM_SIZE 0x10000000 > +#define LPC_IO_OPB_ADDR 0xd0010000 > +#define LPC_IO_OPB_SIZE 0x00010000 > +#define LPC_MEM_OPB_ADDR 0xe0010000 > +#define LPC_MEM_OPB_SIZE 0x10000000 > +#define LPC_FW_OPB_ADDR 0xf0000000 > +#define LPC_FW_OPB_SIZE 0x10000000 > + > +#define LPC_OPB_REGS_OPB_ADDR 0xc0010000 > +#define LPC_OPB_REGS_OPB_SIZE 0x00002000 > +#define LPC_HC_REGS_OPB_ADDR 0xc0012000 > +#define LPC_HC_REGS_OPB_SIZE 0x00001000 > + > + > +static int pnv_lpc_devnode(PnvXScomInterface *dev, void *fdt, int xscom_= offset) > +{ > + const char compat[] =3D "ibm,power8-lpc"; > + char *name; > + int offset; > + uint32_t lpc_pcba =3D PNV_XSCOM_LPC_BASE; > + uint32_t reg[] =3D { > + cpu_to_be32(lpc_pcba), > + cpu_to_be32(PNV_XSCOM_LPC_SIZE) > + }; > + > + name =3D g_strdup_printf("isa@%x", lpc_pcba); > + offset =3D fdt_add_subnode(fdt, xscom_offset, name); > + _FDT(offset); > + g_free(name); > + > + _FDT((fdt_appendprop(fdt, offset, "reg", reg, sizeof(reg)))); Any particular reason for the appendprop instead of setprop? > + _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", compat, sizeof(compat))= )); > + return 0; > +} > + > +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, > + int sz) > +{ > + bool success; > + > + /* XXX Handle access size limits and FW read caching here */ Are the access size limits built into the address_space mechanism not sufficient? > + success =3D !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECI= FIED, > + data, sz, false); > + > + return success; > +} > + > +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *dat= a, > + int sz) > +{ > + bool success; > + > + /* XXX Handle access size limits here */ > + success =3D !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECI= FIED, > + data, sz, true); > + > + return success; > +} > + > +#define ECCB_CTL_READ (1ull << (63 - 15)) > +#define ECCB_CTL_SZ_LSH (63 - 7) > +#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) > +#define ECCB_CTL_ADDR_MASK 0xffffffffu; > + > +#define ECCB_STAT_OP_DONE (1ull << (63 - 52)) > +#define ECCB_STAT_OP_ERR (1ull << (63 - 52)) > +#define ECCB_STAT_RD_DATA_LSH (63 - 37) > +#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) > + > +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) > +{ > + /* XXX Check for magic bits at the top, addr size etc... */ > + unsigned int sz =3D (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; > + uint32_t opb_addr =3D cmd & ECCB_CTL_ADDR_MASK; > + uint8_t data[4]; > + bool success; > + > + if (cmd & ECCB_CTL_READ) { > + success =3D opb_read(lpc, opb_addr, data, sz); > + if (success) { > + lpc->eccb_stat_reg =3D ECCB_STAT_OP_DONE | > + (((uint64_t)data[0]) << 24 | > + ((uint64_t)data[1]) << 16 | > + ((uint64_t)data[2]) << 8 | > + ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; > + } else { > + lpc->eccb_stat_reg =3D ECCB_STAT_OP_DONE | > + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); > + } > + } else { > + data[0] =3D lpc->eccb_data_reg >> 24; > + data[1] =3D lpc->eccb_data_reg >> 16; > + data[2] =3D lpc->eccb_data_reg >> 8; > + data[3] =3D lpc->eccb_data_reg; > + > + success =3D opb_write(lpc, opb_addr, data, sz); > + lpc->eccb_stat_reg =3D ECCB_STAT_OP_DONE; > + } > + /* XXX Which error bit (if any) to signal OPB error ? */ > +} > + > +static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned s= ize) > +{ > + PnvLpcController *lpc =3D PNV_LPC(opaque); > + uint32_t offset =3D pnv_xscom_pcba(opaque, addr); > + uint64_t val =3D 0; > + > + switch (offset & 3) { > + case ECCB_CTL: > + case ECCB_RESET: > + val =3D 0; > + break; > + case ECCB_STAT: > + val =3D lpc->eccb_stat_reg; > + lpc->eccb_stat_reg =3D 0; > + break; > + case ECCB_DATA: > + val =3D ((uint64_t)lpc->eccb_data_reg) << 32; > + break; > + } > + return val; > +} > + > +static void pnv_lpc_xscom_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + PnvLpcController *lpc =3D PNV_LPC(opaque); > + uint32_t offset =3D pnv_xscom_pcba(opaque, addr); > + > + switch (offset & 3) { > + case ECCB_CTL: > + pnv_lpc_do_eccb(lpc, val); > + break; > + case ECCB_RESET: > + /* XXXX */ > + break; > + case ECCB_STAT: > + break; > + case ECCB_DATA: > + lpc->eccb_data_reg =3D val >> 32; > + break; > + } > +} > + > +static const MemoryRegionOps pnv_lpc_xscom_ops =3D { > + .read =3D pnv_lpc_xscom_read, > + .write =3D pnv_lpc_xscom_write, > + .valid.min_access_size =3D 8, > + .valid.max_access_size =3D 8, > + .impl.min_access_size =3D 8, > + .impl.max_access_size =3D 8, > + .endianness =3D DEVICE_BIG_ENDIAN, > +}; > + > +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) > +{ > + PnvLpcController *lpc =3D opaque; > + uint64_t val =3D 0xfffffffffffffffful; > + > + switch (addr) { > + case LPC_HC_FW_SEG_IDSEL: > + val =3D lpc->lpc_hc_fw_seg_idsel; > + break; > + case LPC_HC_FW_RD_ACC_SIZE: > + val =3D lpc->lpc_hc_fw_rd_acc_size; > + break; > + case LPC_HC_IRQSER_CTRL: > + val =3D lpc->lpc_hc_irqser_ctrl; > + break; > + case LPC_HC_IRQMASK: > + val =3D lpc->lpc_hc_irqmask; > + break; > + case LPC_HC_IRQSTAT: > + val =3D lpc->lpc_hc_irqstat; > + break; > + case LPC_HC_ERROR_ADDRESS: > + val =3D lpc->lpc_hc_error_addr; > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr); > + } > + return val; > +} > + > +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, > + unsigned size) > +{ > + PnvLpcController *lpc =3D opaque; > + > + /* XXX Filter out reserved bits */ > + > + switch (addr) { > + case LPC_HC_FW_SEG_IDSEL: > + /* XXX Actually figure out how that works as this impact > + * memory regions/aliases > + */ > + lpc->lpc_hc_fw_seg_idsel =3D val; > + break; > + case LPC_HC_FW_RD_ACC_SIZE: > + lpc->lpc_hc_fw_rd_acc_size =3D val; > + break; > + case LPC_HC_IRQSER_CTRL: > + lpc->lpc_hc_irqser_ctrl =3D val; > + break; > + case LPC_HC_IRQMASK: > + lpc->lpc_hc_irqmask =3D val; > + break; > + case LPC_HC_IRQSTAT: > + lpc->lpc_hc_irqstat &=3D ~val; > + break; > + case LPC_HC_ERROR_ADDRESS: > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr); > + } > +} > + > +static const MemoryRegionOps lpc_hc_ops =3D { > + .read =3D lpc_hc_read, > + .write =3D lpc_hc_write, > + .endianness =3D DEVICE_BIG_ENDIAN, > + .valid =3D { > + .min_access_size =3D 4, > + .max_access_size =3D 4, > + }, > + .impl =3D { > + .min_access_size =3D 4, > + .max_access_size =3D 4, > + }, > +}; > + > +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) > +{ > + PnvLpcController *lpc =3D opaque; > + uint64_t val =3D 0xfffffffffffffffful; > + > + switch (addr) { > + case OPB_MASTER_LS_IRQ_STAT: > + val =3D lpc->opb_irq_stat; > + break; > + case OPB_MASTER_LS_IRQ_MASK: > + val =3D lpc->opb_irq_mask; > + break; > + case OPB_MASTER_LS_IRQ_POL: > + val =3D lpc->opb_irq_pol; > + break; > + case OPB_MASTER_LS_IRQ_INPUT: > + val =3D lpc->opb_irq_input; > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr); > + } > + > + return val; > +} > + > +static void opb_master_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + PnvLpcController *lpc =3D opaque; > + > + switch (addr) { > + case OPB_MASTER_LS_IRQ_STAT: > + lpc->opb_irq_stat &=3D ~val; > + break; > + case OPB_MASTER_LS_IRQ_MASK: > + /* XXX Filter out reserved bits */ > + lpc->opb_irq_mask =3D val; > + break; > + case OPB_MASTER_LS_IRQ_POL: > + /* XXX Filter out reserved bits */ > + lpc->opb_irq_pol =3D val; > + break; > + case OPB_MASTER_LS_IRQ_INPUT: > + /* Read only */ > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" > + HWADDR_PRIx "\n", addr); > + } > +} > + > +static const MemoryRegionOps opb_master_ops =3D { > + .read =3D opb_master_read, > + .write =3D opb_master_write, > + .endianness =3D DEVICE_BIG_ENDIAN, > + .valid =3D { > + .min_access_size =3D 4, > + .max_access_size =3D 4, > + }, > + .impl =3D { > + .min_access_size =3D 4, > + .max_access_size =3D 4, > + }, > +}; > + > +static void pnv_lpc_realize(DeviceState *dev, Error **errp) > +{ > + PnvLpcController *lpc =3D PNV_LPC(dev); > + > + /* Reg inits */ > + lpc->lpc_hc_fw_rd_acc_size =3D LPC_HC_FW_RD_4B; > + > + /* Create address space and backing MR for the OPB bus */ > + memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000= ull); > + address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); > + > + /* Create ISA IO and Mem space regions which are the root of > + * the ISA bus (ie, ISA address spaces). We don't create a > + * separate one for FW which we alias to memory. > + */ > + memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); > + memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SI= ZE); > + > + /* Create windows from the OPB space to the ISA space */ > + memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", > + &lpc->isa_io, 0, LPC_IO_OPB_SIZE); > + memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, > + &lpc->opb_isa_io); > + memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-me= m", > + &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); > + memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, > + &lpc->opb_isa_mem); > + memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", > + &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); > + memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, > + &lpc->opb_isa_fw); > + > + /* Create MMIO regions for LPC HC and OPB registers */ > + memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_maste= r_ops, > + lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); > + memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, > + &lpc->opb_master_regs); > + memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, l= pc, > + "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 */ > + memory_region_init_io(&lpc->xscom_regs, OBJECT(dev), > + &pnv_lpc_xscom_ops, lpc, "xscom-lpc", > + pnv_xscom_addr(PNV_XSCOM_INTERFACE(dev), > + PNV_XSCOM_LPC_SIZE)); > +} > + > +static void pnv_lpc_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc =3D DEVICE_CLASS(klass); > + PnvXScomInterfaceClass *xdc =3D PNV_XSCOM_INTERFACE_CLASS(klass); > + > + xdc->devnode =3D pnv_lpc_devnode; > + > + dc->realize =3D pnv_lpc_realize; > +} > + > +static const TypeInfo pnv_lpc_info =3D { > + .name =3D TYPE_PNV_LPC, > + .parent =3D TYPE_DEVICE, > + .instance_size =3D sizeof(PnvLpcController), > + .class_init =3D pnv_lpc_class_init, > + .interfaces =3D (InterfaceInfo[]) { > + { TYPE_PNV_XSCOM_INTERFACE }, > + { } > + } > +}; > + > +static void pnv_lpc_register_types(void) > +{ > + type_register_static(&pnv_lpc_info); > +} > + > +type_init(pnv_lpc_register_types) > diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h > index 0371710b1882..a30579a5817f 100644 > --- a/include/hw/ppc/pnv.h > +++ b/include/hw/ppc/pnv.h > @@ -22,6 +22,7 @@ > #include "hw/boards.h" > #include "hw/sysbus.h" > #include "hw/ppc/pnv_xscom.h" > +#include "hw/ppc/pnv_lpc.h" > =20 > #define TYPE_PNV_CHIP "powernv-chip" > #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) > @@ -48,6 +49,8 @@ typedef struct PnvChip { > uint32_t nr_cores; > uint64_t cores_mask; > void *cores; > + > + PnvLpcController lpc; > } PnvChip; > =20 > typedef struct PnvChipClass { > diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h > new file mode 100644 > index 000000000000..3741e573b200 > --- /dev/null > +++ b/include/hw/ppc/pnv_lpc.h > @@ -0,0 +1,63 @@ > +/* > + * QEMU PowerPC PowerNV LPC controller > + * > + * Copyright (c) 2016, 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 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 . > + */ > +#ifndef _PPC_PNV_LPC_H > +#define _PPC_PNV_LPC_H > + > +#define TYPE_PNV_LPC "pnv-lpc" > +#define PNV_LPC(obj) \ > + OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) > + > +typedef struct PnvLpcController { > + DeviceState parent; > + > + uint64_t eccb_stat_reg; > + uint32_t eccb_data_reg; > + > + /* OPB bus */ > + MemoryRegion opb_mr; > + AddressSpace opb_as; > + /* ISA IO and Memory space */ > + MemoryRegion isa_io; > + MemoryRegion isa_mem; > + /* Windows from OPB to ISA (aliases) */ > + MemoryRegion opb_isa_io; > + MemoryRegion opb_isa_mem; > + MemoryRegion opb_isa_fw; > + /* Registers */ > + MemoryRegion lpc_hc_regs; > + MemoryRegion opb_master_regs; > + > + /* OPB Master LS registers */ > + uint32_t opb_irq_stat; > + uint32_t opb_irq_mask; > + uint32_t opb_irq_pol; > + uint32_t opb_irq_input; > + > + /* LPC HC registers */ > + uint32_t lpc_hc_fw_seg_idsel; > + uint32_t lpc_hc_fw_rd_acc_size; > + uint32_t lpc_hc_irqser_ctrl; > + uint32_t lpc_hc_irqmask; > + uint32_t lpc_hc_irqstat; > + uint32_t lpc_hc_error_addr; > + > + MemoryRegion xscom_regs; > +} PnvLpcController; > + > +#endif /* _PPC_PNV_LPC_H */ > diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h > index 31e5e8847b90..46b6ce7767c7 100644 > --- a/include/hw/ppc/pnv_xscom.h > +++ b/include/hw/ppc/pnv_xscom.h > @@ -82,6 +82,9 @@ typedef struct PnvXScom { > #define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) <<= 24)) > #define PNV_XSCOM_EX_CORE_SIZE 0x100000 > =20 > +#define PNV_XSCOM_LPC_BASE 0xb0020 > +#define PNV_XSCOM_LPC_SIZE 0x4 > + > extern int pnv_xscom_populate_fdt(PnvXScom *xscom, void *fdt, int offset= ); > =20 > /* --=20 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 --ANwsuk3Muel3MOM0 Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJX4idwAAoJEGw4ysog2bOSuhgQAK1fFxuRRpzvyvffIJOaosM0 cxNEc4DFoVa+2PLx3I+5Vru6dUhiPnN//966YrZjj24oKqqppRyL6+fRIPjrfsQz s5e7AKIHWISHQM8dVfnE14kzF9C8o+FWBvwwAPuz0YJPPQxZ6jYgDiWa/7U1IOGU ZyI6d3iVHnIOWQQPQGB+oPY7qu2wmWyYKkeEBJP/vXaDbZwtv/48phe5pXu/mCeE G0hk/eg7Y/LTMJjLRCentWJ59uGFBoufMUHSjE4uyfNC9roen6D/KqrInNxUR9BO kJ3MgdXumFDOknamT+AhH3B8QymecNElJozlxF8qzC5ZpK1vCVM3EtFZhviLRkvr 5mFmiCn20MSkJn9izvkVPm+re6QI1dNL+53mKfGGrhzuzkcjUpCP97hsJUV/tqK/ L2+gN+O/h6Y/wRnH5rlISDnwX2y0t19M2j4th0mv0jT4usdo1m2Evqbc2yPAPEbG xhwAZ5LpX/Yszg7qUzZb0nWId8E6xoOqtnH7/r4uUk4SMeCgUIQO307Cg6uVAgIe Y5F1OO6yx1Dw/a8K6r+1TqZljAmw4Wq1nogWiN6C0ZPFiucMP/P49saJOoZdvgCt Z7fohn/5EUFJJNmpK7dwuWawGx4cB7CgL/mQpul6+JI7tsrsN/xRshra1C6LkHRX xpfhFgflZdE+ojX9pZq3 =69zH -----END PGP SIGNATURE----- --ANwsuk3Muel3MOM0--