xref: /openbmc/qemu/hw/intc/xics_pnv.c (revision 683685e7)
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