qemu/hw/intc/xics_pnv.c
<<
>>
Prefs
   1/*
   2 * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model
   3 *
   4 * Copyright (c) 2017, IBM Corporation.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public License
   8 * as published by the Free Software Foundation; either version 2 of
   9 * the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "sysemu/sysemu.h"
  22#include "qemu/log.h"
  23#include "hw/ppc/xics.h"
  24
  25#define ICP_XIRR_POLL    0 /* 1 byte (CPRR) or 4 bytes */
  26#define ICP_XIRR         4 /* 1 byte (CPRR) or 4 bytes */
  27#define ICP_MFRR        12 /* 1 byte access only */
  28
  29#define ICP_LINKA       16 /* unused */
  30#define ICP_LINKB       20 /* unused */
  31#define ICP_LINKC       24 /* unused */
  32
  33static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width)
  34{
  35    ICPState *icp = ICP(opaque);
  36    PnvICPState *picp = PNV_ICP(opaque);
  37    bool byte0 = (width == 1 && (addr & 0x3) == 0);
  38    uint64_t val = 0xffffffff;
  39
  40    switch (addr & 0xffc) {
  41    case ICP_XIRR_POLL:
  42        val = icp_ipoll(icp, NULL);
  43        if (byte0) {
  44            val >>= 24;
  45        } else if (width != 4) {
  46            goto bad_access;
  47        }
  48        break;
  49    case ICP_XIRR:
  50        if (byte0) {
  51            val = icp_ipoll(icp, NULL) >> 24;
  52        } else if (width == 4) {
  53            val = icp_accept(icp);
  54        } else {
  55            goto bad_access;
  56        }
  57        break;
  58    case ICP_MFRR:
  59        if (byte0) {
  60            val = icp->mfrr;
  61        } else {
  62            goto bad_access;
  63        }
  64        break;
  65    case ICP_LINKA:
  66        if (width == 4) {
  67            val = picp->links[0];
  68        } else {
  69            goto bad_access;
  70        }
  71        break;
  72    case ICP_LINKB:
  73        if (width == 4) {
  74            val = picp->links[1];
  75        } else {
  76            goto bad_access;
  77        }
  78        break;
  79    case ICP_LINKC:
  80        if (width == 4) {
  81            val = picp->links[2];
  82        } else {
  83            goto bad_access;
  84        }
  85        break;
  86    default:
  87bad_access:
  88        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
  89                      HWADDR_PRIx"/%d\n", addr, width);
  90    }
  91
  92    return val;
  93}
  94
  95static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val,
  96                              unsigned width)
  97{
  98    ICPState *icp = ICP(opaque);
  99    PnvICPState *picp = PNV_ICP(opaque);
 100    bool byte0 = (width == 1 && (addr & 0x3) == 0);
 101
 102    switch (addr & 0xffc) {
 103    case ICP_XIRR:
 104        if (byte0) {
 105            icp_set_cppr(icp, val);
 106        } else if (width == 4) {
 107            icp_eoi(icp, val);
 108        } else {
 109            goto bad_access;
 110        }
 111        break;
 112    case ICP_MFRR:
 113        if (byte0) {
 114            icp_set_mfrr(icp, val);
 115        } else {
 116            goto bad_access;
 117        }
 118        break;
 119    case ICP_LINKA:
 120        if (width == 4) {
 121            picp->links[0] = val;
 122        } else {
 123            goto bad_access;
 124        }
 125        break;
 126    case ICP_LINKB:
 127        if (width == 4) {
 128            picp->links[1] = val;
 129        } else {
 130            goto bad_access;
 131        }
 132        break;
 133    case ICP_LINKC:
 134        if (width == 4) {
 135            picp->links[2] = val;
 136        } else {
 137            goto bad_access;
 138        }
 139        break;
 140    default:
 141bad_access:
 142        qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
 143                      HWADDR_PRIx"/%d\n", addr, width);
 144    }
 145}
 146
 147static const MemoryRegionOps pnv_icp_ops = {
 148    .read = pnv_icp_read,
 149    .write = pnv_icp_write,
 150    .endianness = DEVICE_BIG_ENDIAN,
 151    .valid = {
 152        .min_access_size = 1,
 153        .max_access_size = 4,
 154    },
 155    .impl = {
 156        .min_access_size = 1,
 157        .max_access_size = 4,
 158    },
 159};
 160
 161static void pnv_icp_realize(ICPState *icp, Error **errp)
 162{
 163    PnvICPState *pnv_icp = PNV_ICP(icp);
 164
 165    memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops,
 166                          icp, "icp-thread", 0x1000);
 167}
 168
 169static void pnv_icp_class_init(ObjectClass *klass, void *data)
 170{
 171    DeviceClass *dc = DEVICE_CLASS(klass);
 172    ICPStateClass *icpc = ICP_CLASS(klass);
 173
 174    icpc->realize = pnv_icp_realize;
 175    dc->desc = "PowerNV ICP";
 176}
 177
 178static const TypeInfo pnv_icp_info = {
 179    .name          = TYPE_PNV_ICP,
 180    .parent        = TYPE_ICP,
 181    .instance_size = sizeof(PnvICPState),
 182    .class_init    = pnv_icp_class_init,
 183    .class_size    = sizeof(ICPStateClass),
 184};
 185
 186static void pnv_icp_register_types(void)
 187{
 188    type_register_static(&pnv_icp_info);
 189}
 190
 191type_init(pnv_icp_register_types)
 192