diff options
author | Cédric Le Goater <clg@kaod.org> | 2017-04-03 09:46:00 +0200 |
---|---|---|
committer | David Gibson <david@gibson.dropbear.id.au> | 2017-04-26 12:00:42 +1000 |
commit | 99285aae1695f77ea55230554f547b66b685e123 (patch) | |
tree | f9830b9732077fbe1a5c29067eef5169f4f617ce /hw/intc/xics_pnv.c | |
parent | 439071a92dc85d67b5b6c55e7d1098dc6c3e8e89 (diff) | |
download | qemu-99285aae1695f77ea55230554f547b66b685e123.zip |
ppc/pnv: add a PnvICPState object
This provides a new ICPState object for the PowerNV machine (POWER8).
Access to the Interrupt Management area is done though a memory
region. It contains the registers of the Interrupt Control Presenters
of each thread which are used to accept, return, forward interrupts in
the system.
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'hw/intc/xics_pnv.c')
-rw-r--r-- | hw/intc/xics_pnv.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c new file mode 100644 index 0000000000..12ae605f10 --- /dev/null +++ b/hw/intc/xics_pnv.c @@ -0,0 +1,192 @@ +/* + * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model + * + * Copyright (c) 2017, 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 "qapi/error.h" +#include "qemu/log.h" +#include "hw/ppc/xics.h" + +#define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ +#define ICP_MFRR 12 /* 1 byte access only */ + +#define ICP_LINKA 16 /* unused */ +#define ICP_LINKB 20 /* unused */ +#define ICP_LINKC 24 /* unused */ + +static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + uint64_t val = 0xffffffff; + + switch (addr & 0xffc) { + case ICP_XIRR_POLL: + val = icp_ipoll(icp, NULL); + if (byte0) { + val >>= 24; + } else if (width != 4) { + goto bad_access; + } + break; + case ICP_XIRR: + if (byte0) { + val = icp_ipoll(icp, NULL) >> 24; + } else if (width == 4) { + val = icp_accept(icp); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + val = icp->mfrr; + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + val = picp->links[0]; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + val = picp->links[1]; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + val = picp->links[2]; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } + + return val; +} + +static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + ICPState *icp = ICP(opaque); + PnvICPState *picp = PNV_ICP(opaque); + bool byte0 = (width == 1 && (addr & 0x3) == 0); + + switch (addr & 0xffc) { + case ICP_XIRR: + if (byte0) { + icp_set_cppr(icp, val); + } else if (width == 4) { + icp_eoi(icp, val); + } else { + goto bad_access; + } + break; + case ICP_MFRR: + if (byte0) { + icp_set_mfrr(icp, val); + } else { + goto bad_access; + } + break; + case ICP_LINKA: + if (width == 4) { + picp->links[0] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKB: + if (width == 4) { + picp->links[1] = val; + } else { + goto bad_access; + } + break; + case ICP_LINKC: + if (width == 4) { + picp->links[2] = val; + } else { + goto bad_access; + } + break; + default: +bad_access: + qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" + HWADDR_PRIx"/%d\n", addr, width); + } +} + +static const MemoryRegionOps pnv_icp_ops = { + .read = pnv_icp_read, + .write = pnv_icp_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void pnv_icp_realize(DeviceState *dev, Error **errp) +{ + PnvICPState *icp = PNV_ICP(dev); + + memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, + icp, "icp-thread", 0x1000); +} + +static void pnv_icp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICPStateClass *icpc = ICP_CLASS(klass); + + icpc->realize = pnv_icp_realize; + dc->desc = "PowerNV ICP"; +} + +static const TypeInfo pnv_icp_info = { + .name = TYPE_PNV_ICP, + .parent = TYPE_ICP, + .instance_size = sizeof(PnvICPState), + .class_init = pnv_icp_class_init, + .class_size = sizeof(ICPStateClass), +}; + +static void pnv_icp_register_types(void) +{ + type_register_static(&pnv_icp_info); +} + +type_init(pnv_icp_register_types) |