199285aaeSCédric Le Goater /*
299285aaeSCédric Le Goater * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model
399285aaeSCédric Le Goater *
499285aaeSCédric Le Goater * Copyright (c) 2017, IBM Corporation.
599285aaeSCédric Le Goater *
699285aaeSCédric Le Goater * This library is free software; you can redistribute it and/or
799285aaeSCédric Le Goater * modify it under the terms of the GNU Lesser General Public License
8*f70c5966SChetan Pant * as published by the Free Software Foundation; either version 2.1 of
999285aaeSCédric Le Goater * the License, or (at your option) any later version.
1099285aaeSCédric Le Goater *
1199285aaeSCédric Le Goater * This library is distributed in the hope that it will be useful, but
1299285aaeSCédric Le Goater * WITHOUT ANY WARRANTY; without even the implied warranty of
1399285aaeSCédric Le Goater * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1499285aaeSCédric Le Goater * Lesser General Public License for more details.
1599285aaeSCédric Le Goater *
1699285aaeSCédric Le Goater * You should have received a copy of the GNU Lesser General Public
1799285aaeSCédric Le Goater * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1899285aaeSCédric Le Goater */
1999285aaeSCédric Le Goater
2099285aaeSCédric Le Goater #include "qemu/osdep.h"
21a028dd42SCédric Le Goater #include "qapi/error.h"
2299285aaeSCédric Le Goater #include "qemu/log.h"
230b8fa32fSMarkus Armbruster #include "qemu/module.h"
2499285aaeSCédric Le Goater #include "hw/ppc/xics.h"
2599285aaeSCédric Le Goater
2699285aaeSCédric Le Goater #define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */
2799285aaeSCédric Le Goater #define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */
2899285aaeSCédric Le Goater #define ICP_MFRR 12 /* 1 byte access only */
2999285aaeSCédric Le Goater
3099285aaeSCédric Le Goater #define ICP_LINKA 16 /* unused */
3199285aaeSCédric Le Goater #define ICP_LINKB 20 /* unused */
3299285aaeSCédric Le Goater #define ICP_LINKC 24 /* unused */
3399285aaeSCédric Le Goater
pnv_icp_read(void * opaque,hwaddr addr,unsigned width)3499285aaeSCédric Le Goater static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width)
3599285aaeSCédric Le Goater {
3699285aaeSCédric Le Goater ICPState *icp = ICP(opaque);
3799285aaeSCédric Le Goater PnvICPState *picp = PNV_ICP(opaque);
3899285aaeSCédric Le Goater bool byte0 = (width == 1 && (addr & 0x3) == 0);
3999285aaeSCédric Le Goater uint64_t val = 0xffffffff;
4099285aaeSCédric Le Goater
4199285aaeSCédric Le Goater switch (addr & 0xffc) {
4299285aaeSCédric Le Goater case ICP_XIRR_POLL:
4399285aaeSCédric Le Goater val = icp_ipoll(icp, NULL);
4499285aaeSCédric Le Goater if (byte0) {
4599285aaeSCédric Le Goater val >>= 24;
4699285aaeSCédric Le Goater } else if (width != 4) {
4799285aaeSCédric Le Goater goto bad_access;
4899285aaeSCédric Le Goater }
4999285aaeSCédric Le Goater break;
5099285aaeSCédric Le Goater case ICP_XIRR:
5199285aaeSCédric Le Goater if (byte0) {
5299285aaeSCédric Le Goater val = icp_ipoll(icp, NULL) >> 24;
5399285aaeSCédric Le Goater } else if (width == 4) {
5499285aaeSCédric Le Goater val = icp_accept(icp);
5599285aaeSCédric Le Goater } else {
5699285aaeSCédric Le Goater goto bad_access;
5799285aaeSCédric Le Goater }
5899285aaeSCédric Le Goater break;
5999285aaeSCédric Le Goater case ICP_MFRR:
6099285aaeSCédric Le Goater if (byte0) {
6199285aaeSCédric Le Goater val = icp->mfrr;
6299285aaeSCédric Le Goater } else {
6399285aaeSCédric Le Goater goto bad_access;
6499285aaeSCédric Le Goater }
6599285aaeSCédric Le Goater break;
6699285aaeSCédric Le Goater case ICP_LINKA:
6799285aaeSCédric Le Goater if (width == 4) {
6899285aaeSCédric Le Goater val = picp->links[0];
6999285aaeSCédric Le Goater } else {
7099285aaeSCédric Le Goater goto bad_access;
7199285aaeSCédric Le Goater }
7299285aaeSCédric Le Goater break;
7399285aaeSCédric Le Goater case ICP_LINKB:
7499285aaeSCédric Le Goater if (width == 4) {
7599285aaeSCédric Le Goater val = picp->links[1];
7699285aaeSCédric Le Goater } else {
7799285aaeSCédric Le Goater goto bad_access;
7899285aaeSCédric Le Goater }
7999285aaeSCédric Le Goater break;
8099285aaeSCédric Le Goater case ICP_LINKC:
8199285aaeSCédric Le Goater if (width == 4) {
8299285aaeSCédric Le Goater val = picp->links[2];
8399285aaeSCédric Le Goater } else {
8499285aaeSCédric Le Goater goto bad_access;
8599285aaeSCédric Le Goater }
8699285aaeSCédric Le Goater break;
8799285aaeSCédric Le Goater default:
8899285aaeSCédric Le Goater bad_access:
8999285aaeSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
9099285aaeSCédric Le Goater HWADDR_PRIx"/%d\n", addr, width);
9199285aaeSCédric Le Goater }
9299285aaeSCédric Le Goater
9399285aaeSCédric Le Goater return val;
9499285aaeSCédric Le Goater }
9599285aaeSCédric Le Goater
pnv_icp_write(void * opaque,hwaddr addr,uint64_t val,unsigned width)9699285aaeSCédric Le Goater static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val,
9799285aaeSCédric Le Goater unsigned width)
9899285aaeSCédric Le Goater {
9999285aaeSCédric Le Goater ICPState *icp = ICP(opaque);
10099285aaeSCédric Le Goater PnvICPState *picp = PNV_ICP(opaque);
10199285aaeSCédric Le Goater bool byte0 = (width == 1 && (addr & 0x3) == 0);
10299285aaeSCédric Le Goater
10399285aaeSCédric Le Goater switch (addr & 0xffc) {
10499285aaeSCédric Le Goater case ICP_XIRR:
10599285aaeSCédric Le Goater if (byte0) {
10699285aaeSCédric Le Goater icp_set_cppr(icp, val);
10799285aaeSCédric Le Goater } else if (width == 4) {
10899285aaeSCédric Le Goater icp_eoi(icp, val);
10999285aaeSCédric Le Goater } else {
11099285aaeSCédric Le Goater goto bad_access;
11199285aaeSCédric Le Goater }
11299285aaeSCédric Le Goater break;
11399285aaeSCédric Le Goater case ICP_MFRR:
11499285aaeSCédric Le Goater if (byte0) {
11599285aaeSCédric Le Goater icp_set_mfrr(icp, val);
11699285aaeSCédric Le Goater } else {
11799285aaeSCédric Le Goater goto bad_access;
11899285aaeSCédric Le Goater }
11999285aaeSCédric Le Goater break;
12099285aaeSCédric Le Goater case ICP_LINKA:
12199285aaeSCédric Le Goater if (width == 4) {
12299285aaeSCédric Le Goater picp->links[0] = val;
12399285aaeSCédric Le Goater } else {
12499285aaeSCédric Le Goater goto bad_access;
12599285aaeSCédric Le Goater }
12699285aaeSCédric Le Goater break;
12799285aaeSCédric Le Goater case ICP_LINKB:
12899285aaeSCédric Le Goater if (width == 4) {
12999285aaeSCédric Le Goater picp->links[1] = val;
13099285aaeSCédric Le Goater } else {
13199285aaeSCédric Le Goater goto bad_access;
13299285aaeSCédric Le Goater }
13399285aaeSCédric Le Goater break;
13499285aaeSCédric Le Goater case ICP_LINKC:
13599285aaeSCédric Le Goater if (width == 4) {
13699285aaeSCédric Le Goater picp->links[2] = val;
13799285aaeSCédric Le Goater } else {
13899285aaeSCédric Le Goater goto bad_access;
13999285aaeSCédric Le Goater }
14099285aaeSCédric Le Goater break;
14199285aaeSCédric Le Goater default:
14299285aaeSCédric Le Goater bad_access:
14399285aaeSCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
14499285aaeSCédric Le Goater HWADDR_PRIx"/%d\n", addr, width);
14599285aaeSCédric Le Goater }
14699285aaeSCédric Le Goater }
14799285aaeSCédric Le Goater
14899285aaeSCédric Le Goater static const MemoryRegionOps pnv_icp_ops = {
14999285aaeSCédric Le Goater .read = pnv_icp_read,
15099285aaeSCédric Le Goater .write = pnv_icp_write,
15199285aaeSCédric Le Goater .endianness = DEVICE_BIG_ENDIAN,
15299285aaeSCédric Le Goater .valid = {
15399285aaeSCédric Le Goater .min_access_size = 1,
15499285aaeSCédric Le Goater .max_access_size = 4,
15599285aaeSCédric Le Goater },
15699285aaeSCédric Le Goater .impl = {
15799285aaeSCédric Le Goater .min_access_size = 1,
15899285aaeSCédric Le Goater .max_access_size = 4,
15999285aaeSCédric Le Goater },
16099285aaeSCédric Le Goater };
16199285aaeSCédric Le Goater
pnv_icp_realize(DeviceState * dev,Error ** errp)162a028dd42SCédric Le Goater static void pnv_icp_realize(DeviceState *dev, Error **errp)
16399285aaeSCédric Le Goater {
164a028dd42SCédric Le Goater ICPState *icp = ICP(dev);
165100f7388SGreg Kurz PnvICPState *pnv_icp = PNV_ICP(icp);
166a028dd42SCédric Le Goater ICPStateClass *icpc = ICP_GET_CLASS(icp);
167a028dd42SCédric Le Goater Error *local_err = NULL;
168a028dd42SCédric Le Goater
169a028dd42SCédric Le Goater icpc->parent_realize(dev, &local_err);
170a028dd42SCédric Le Goater if (local_err) {
171a028dd42SCédric Le Goater error_propagate(errp, local_err);
172a028dd42SCédric Le Goater return;
173a028dd42SCédric Le Goater }
17499285aaeSCédric Le Goater
175100f7388SGreg Kurz memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops,
17699285aaeSCédric Le Goater icp, "icp-thread", 0x1000);
17799285aaeSCédric Le Goater }
17899285aaeSCédric Le Goater
pnv_icp_class_init(ObjectClass * klass,void * data)17999285aaeSCédric Le Goater static void pnv_icp_class_init(ObjectClass *klass, void *data)
18099285aaeSCédric Le Goater {
18199285aaeSCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass);
18299285aaeSCédric Le Goater ICPStateClass *icpc = ICP_CLASS(klass);
18399285aaeSCédric Le Goater
184a028dd42SCédric Le Goater device_class_set_parent_realize(dc, pnv_icp_realize,
185a028dd42SCédric Le Goater &icpc->parent_realize);
18699285aaeSCédric Le Goater dc->desc = "PowerNV ICP";
18799285aaeSCédric Le Goater }
18899285aaeSCédric Le Goater
18999285aaeSCédric Le Goater static const TypeInfo pnv_icp_info = {
19099285aaeSCédric Le Goater .name = TYPE_PNV_ICP,
19199285aaeSCédric Le Goater .parent = TYPE_ICP,
19299285aaeSCédric Le Goater .instance_size = sizeof(PnvICPState),
19399285aaeSCédric Le Goater .class_init = pnv_icp_class_init,
19499285aaeSCédric Le Goater .class_size = sizeof(ICPStateClass),
19599285aaeSCédric Le Goater };
19699285aaeSCédric Le Goater
pnv_icp_register_types(void)19799285aaeSCédric Le Goater static void pnv_icp_register_types(void)
19899285aaeSCédric Le Goater {
19999285aaeSCédric Le Goater type_register_static(&pnv_icp_info);
20099285aaeSCédric Le Goater }
20199285aaeSCédric Le Goater
20299285aaeSCédric Le Goater type_init(pnv_icp_register_types)
203