mirror of
https://github.com/qemu/qemu.git
synced 2024-11-28 22:33:36 +08:00
f70c59668c
There is no "version 2" of the "Lesser" General Public License. It is either "GPL version 2.0" or "Lesser GPL version 2.1". This patch replaces all occurrences of "Lesser GPL version 2" with "Lesser GPL version 2.1" in comment section. Signed-off-by: Chetan Pant <chetan4windows@gmail.com> Message-Id: <20201016145346.27167-1-chetan4windows@gmail.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: Thomas Huth <thuth@redhat.com>
203 lines
5.2 KiB
C
203 lines
5.2 KiB
C
/*
|
|
* 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.1 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 "qapi/error.h"
|
|
#include "qemu/log.h"
|
|
#include "qemu/module.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)
|
|
{
|
|
ICPState *icp = ICP(dev);
|
|
PnvICPState *pnv_icp = PNV_ICP(icp);
|
|
ICPStateClass *icpc = ICP_GET_CLASS(icp);
|
|
Error *local_err = NULL;
|
|
|
|
icpc->parent_realize(dev, &local_err);
|
|
if (local_err) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &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);
|
|
|
|
device_class_set_parent_realize(dc, pnv_icp_realize,
|
|
&icpc->parent_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)
|