On Thu, Oct 13, 2016 at 01:52:55PM +1100, David Gibson wrote: > On Mon, Oct 03, 2016 at 09:24:44AM +0200, Cédric Le Goater wrote: > > From: Benjamin Herrenschmidt <b...@kernel.crashing.org> > > > > The LPC (Low Pin Count) interface on a POWER8 is made accessible to > > the system through the ADU (XSCOM interface). This interface is part > > of set of units connected together via a local OPB (On-Chip Peripheral > > Bus) which act as a bridge between the ADU and the off chip LPC > > endpoints, like external flash modules. > > > > The most important units of this OPB are : > > - OPB Master: contains the ADU slave logic, a set of internal > > registers and the logic to control the OPB. > > - LPCHC (LPC HOST Controller): which implements a OPB Slave, a set of > > internal registers and the LPC HOST Controller to control the LPC > > interface. > > > > Four address spaces are provided to the ADU : > > - LPC Bus Firmware Memory > > - LPC Bus Memory > > - LPC Bus I/O (ISA bus) > > - and the registers for the OPB Master and the LPC Host Controller > > > > On POWER8, an intermediate hop is necessary to reach the OPB, through > > a unit called the ECCB. OPB commands are simply mangled in ECCB write > > commands. > > > > On POWER9, the OPB master address space can be accessed via MMIO. The > > logic is same but the code will be simpler as the XSCOM and ECCB hops > > are not necessary anymore. > > > > 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. > > > > Signed-off-by: Benjamin Herrenschmidt <b...@kernel.crashing.org> > > [clg: - updated for qemu-2.7 > > - ported on latest PowerNV patchset > > - 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 > > - rewrote commit log ] > > Signed-off-by: Cédric Le Goater <c...@kaod.org> > > It looks reasonable as far as it goes. > > I don't see anything wiring this up to qemu's common ISA > infrastructure, which seems a bit odd.
Sorry.. hadn't read the next patch yet. Reviewed-by: David Gibson <da...@gibson.dropbear.id.au> > > > --- > > > > Todo: > > > > - rework the address_space read/write ops as they should be shared > > with the P9 support. > > > > Changes since v3: > > > > - rewrote commit log > > - fixed appendprop > > > > hw/ppc/Makefile.objs | 2 +- > > hw/ppc/pnv.c | 9 + > > hw/ppc/pnv_lpc.c | 471 > > +++++++++++++++++++++++++++++++++++++++++++++ > > include/hw/ppc/pnv.h | 3 + > > include/hw/ppc/pnv_lpc.h | 67 +++++++ > > include/hw/ppc/pnv_xscom.h | 3 + > > 6 files changed, 554 insertions(+), 1 deletion(-) > > create mode 100644 hw/ppc/pnv_lpc.c > > create mode 100644 include/hw/ppc/pnv_lpc.h > > > > 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) += spapr_hcall.o spapr_iommu.o > > spapr_rtas.o > > obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o > > obj-$(CONFIG_PSERIES) += spapr_cpu_core.o > > # IBM PowerNV > > -obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o > > +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o > > ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) > > obj-y += spapr_pci_vfio.o > > endif > > diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c > > index ffe245fe59d2..e41244294435 100644 > > --- a/hw/ppc/pnv.c > > +++ b/hw/ppc/pnv.c > > @@ -563,6 +563,9 @@ static void pnv_chip_init(Object *obj) > > PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); > > > > 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); > > } > > > > static void pnv_chip_realize(DeviceState *dev, Error **errp) > > @@ -626,6 +629,12 @@ static void pnv_chip_realize(DeviceState *dev, Error > > **errp) > > &PNV_CORE(pnv_core)->xscom_regs); > > } > > g_free(typename); > > + > > + /* Create LPC controller */ > > + 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); > > } > > > > static Property pnv_chip_properties[] = { > > diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c > > new file mode 100644 > > index 000000000000..210cc1cff167 > > --- /dev/null > > +++ b/hw/ppc/pnv_lpc.c > > @@ -0,0 +1,471 @@ > > +/* > > + * 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 > > <http://www.gnu.org/licenses/>. > > + */ > > + > > +#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 <libfdt.h> > > + > > +enum { > > + ECCB_CTL = 0, > > + ECCB_RESET = 1, > > + ECCB_STAT = 2, > > + ECCB_DATA = 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_IRQSTAT > > */ > > +#define LPC_HC_IRQSTAT 0x38 > > +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... > > */ > > +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, > > IRQ2=SMI# */ > > +#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 LPC_OPB_SIZE 0x100000000ull > > + > > +#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 > > + > > + > > +/* > > + * TODO: the "primary" cell should only be added on chip 0. This is > > + * how skiboot chooses the default LPC controller on multichip > > + * systems. > > + * > > + * 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) > > +{ > > + const char compat[] = "ibm,power8-lpc\0ibm,lpc"; > > + char *name; > > + int offset; > > + uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE; > > + uint32_t reg[] = { > > + cpu_to_be32(lpc_pcba), > > + cpu_to_be32(PNV_XSCOM_LPC_SIZE) > > + }; > > + > > + name = g_strdup_printf("isa@%x", lpc_pcba); > > + offset = fdt_add_subnode(fdt, xscom_offset, name); > > + _FDT(offset); > > + g_free(name); > > + > > + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); > > + _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; > > +} > > + > > +/* > > + * These read/write handlers of the OPB address space should be common > > + * with the P9 LPC Controller which uses direct MMIOs. > > + * > > + * TODO: rework to use address_space_stq() and address_space_ldq() > > + * instead. > > + */ > > +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 */ > > + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, > > + data, sz, false); > > + > > + return success; > > +} > > + > > +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, > > + int sz) > > +{ > > + bool success; > > + > > + /* XXX Handle access size limits here */ > > + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, > > + 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 = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; > > + uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; > > + uint8_t data[4]; > > + bool success; > > + > > + if (cmd & ECCB_CTL_READ) { > > + success = opb_read(lpc, opb_addr, data, sz); > > + if (success) { > > + lpc->eccb_stat_reg = 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 = ECCB_STAT_OP_DONE | > > + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); > > + } > > + } else { > > + data[0] = lpc->eccb_data_reg >> 24; > > + data[1] = lpc->eccb_data_reg >> 16; > > + data[2] = lpc->eccb_data_reg >> 8; > > + data[3] = lpc->eccb_data_reg; > > + > > + success = opb_write(lpc, opb_addr, data, sz); > > + lpc->eccb_stat_reg = 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 > > size) > > +{ > > + PnvLpcController *lpc = PNV_LPC(opaque); > > + uint32_t offset = addr >> 3; > > + uint64_t val = 0; > > + > > + switch (offset & 3) { > > + case ECCB_CTL: > > + case ECCB_RESET: > > + val = 0; > > + break; > > + case ECCB_STAT: > > + val = lpc->eccb_stat_reg; > > + lpc->eccb_stat_reg = 0; > > + break; > > + case ECCB_DATA: > > + val = ((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 = PNV_LPC(opaque); > > + uint32_t offset = addr >> 3; > > + > > + 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 = val >> 32; > > + break; > > + } > > +} > > + > > +static const MemoryRegionOps pnv_lpc_xscom_ops = { > > + .read = pnv_lpc_xscom_read, > > + .write = pnv_lpc_xscom_write, > > + .valid.min_access_size = 8, > > + .valid.max_access_size = 8, > > + .impl.min_access_size = 8, > > + .impl.max_access_size = 8, > > + .endianness = DEVICE_BIG_ENDIAN, > > +}; > > + > > +static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) > > +{ > > + PnvLpcController *lpc = opaque; > > + uint64_t val = 0xfffffffffffffffful; > > + > > + switch (addr) { > > + case LPC_HC_FW_SEG_IDSEL: > > + val = lpc->lpc_hc_fw_seg_idsel; > > + break; > > + case LPC_HC_FW_RD_ACC_SIZE: > > + val = lpc->lpc_hc_fw_rd_acc_size; > > + break; > > + case LPC_HC_IRQSER_CTRL: > > + val = lpc->lpc_hc_irqser_ctrl; > > + break; > > + case LPC_HC_IRQMASK: > > + val = lpc->lpc_hc_irqmask; > > + break; > > + case LPC_HC_IRQSTAT: > > + val = lpc->lpc_hc_irqstat; > > + break; > > + case LPC_HC_ERROR_ADDRESS: > > + val = 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 = 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 = val; > > + break; > > + case LPC_HC_FW_RD_ACC_SIZE: > > + lpc->lpc_hc_fw_rd_acc_size = val; > > + break; > > + case LPC_HC_IRQSER_CTRL: > > + lpc->lpc_hc_irqser_ctrl = val; > > + break; > > + case LPC_HC_IRQMASK: > > + lpc->lpc_hc_irqmask = val; > > + break; > > + case LPC_HC_IRQSTAT: > > + lpc->lpc_hc_irqstat &= ~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 = { > > + .read = lpc_hc_read, > > + .write = lpc_hc_write, > > + .endianness = DEVICE_BIG_ENDIAN, > > + .valid = { > > + .min_access_size = 4, > > + .max_access_size = 4, > > + }, > > + .impl = { > > + .min_access_size = 4, > > + .max_access_size = 4, > > + }, > > +}; > > + > > +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) > > +{ > > + PnvLpcController *lpc = opaque; > > + uint64_t val = 0xfffffffffffffffful; > > + > > + switch (addr) { > > + case OPB_MASTER_LS_IRQ_STAT: > > + val = lpc->opb_irq_stat; > > + break; > > + case OPB_MASTER_LS_IRQ_MASK: > > + val = lpc->opb_irq_mask; > > + break; > > + case OPB_MASTER_LS_IRQ_POL: > > + val = lpc->opb_irq_pol; > > + break; > > + case OPB_MASTER_LS_IRQ_INPUT: > > + val = 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 = opaque; > > + > > + switch (addr) { > > + case OPB_MASTER_LS_IRQ_STAT: > > + lpc->opb_irq_stat &= ~val; > > + break; > > + case OPB_MASTER_LS_IRQ_MASK: > > + /* XXX Filter out reserved bits */ > > + lpc->opb_irq_mask = val; > > + break; > > + case OPB_MASTER_LS_IRQ_POL: > > + /* XXX Filter out reserved bits */ > > + lpc->opb_irq_pol = 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 = { > > + .read = opb_master_read, > > + .write = opb_master_write, > > + .endianness = DEVICE_BIG_ENDIAN, > > + .valid = { > > + .min_access_size = 4, > > + .max_access_size = 4, > > + }, > > + .impl = { > > + .min_access_size = 4, > > + .max_access_size = 4, > > + }, > > +}; > > + > > +static void pnv_lpc_realize(DeviceState *dev, Error **errp) > > +{ > > + PnvLpcController *lpc = PNV_LPC(dev); > > + > > + /* Reg inits */ > > + lpc->lpc_hc_fw_rd_acc_size = 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", > > 0x100000000ull); > > + 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_SIZE); > > + > > + /* 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-mem", > > + &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_master_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, lpc, > > + "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_LPC_SIZE << 3); > > +} > > + > > +static void pnv_lpc_class_init(ObjectClass *klass, void *data) > > +{ > > + DeviceClass *dc = DEVICE_CLASS(klass); > > + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); > > + > > + xdc->populate = pnv_lpc_populate; > > + > > + dc->realize = pnv_lpc_realize; > > +} > > + > > +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 }, > > + { } > > + } > > +}; > > + > > +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 f89eddb6e5e0..e586ff4e735e 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" > > > > #define TYPE_PNV_CHIP "powernv-chip" > > #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) > > @@ -52,6 +53,8 @@ typedef struct PnvChip { > > uint32_t nr_cores; > > uint64_t cores_mask; > > void *cores; > > + > > + PnvLpcController lpc; > > } PnvChip; > > > > 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..38e5506975aa > > --- /dev/null > > +++ b/include/hw/ppc/pnv_lpc.h > > @@ -0,0 +1,67 @@ > > +/* > > + * 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 > > <http://www.gnu.org/licenses/>. > > + */ > > +#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; > > + > > + /* XSCOM registers */ > > + 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 79975a6cbe46..3b8a8b4fc6ff 100644 > > --- a/include/hw/ppc/pnv_xscom.h > > +++ b/include/hw/ppc/pnv_xscom.h > > @@ -60,6 +60,9 @@ typedef struct PnvXScomInterfaceClass { > > #define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << > > 24)) > > #define PNV_XSCOM_EX_CORE_SIZE 0x100000 > > > > +#define PNV_XSCOM_LPC_BASE 0xb0020 > > +#define PNV_XSCOM_LPC_SIZE 0x4 > > + > > extern void pnv_xscom_realize(PnvChip *chip, Error **errp); > > extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); > > > -- 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