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 "qapi/error.h" 22 #include "sysemu/sysemu.h" 23 #include "qemu/log.h" 24 #include "hw/ppc/xics.h" 25 26 #define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ 27 #define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ 28 #define ICP_MFRR 12 /* 1 byte access only */ 29 30 #define ICP_LINKA 16 /* unused */ 31 #define ICP_LINKB 20 /* unused */ 32 #define ICP_LINKC 24 /* unused */ 33 34 static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) 35 { 36 ICPState *icp = ICP(opaque); 37 PnvICPState *picp = PNV_ICP(opaque); 38 bool byte0 = (width == 1 && (addr & 0x3) == 0); 39 uint64_t val = 0xffffffff; 40 41 switch (addr & 0xffc) { 42 case ICP_XIRR_POLL: 43 val = icp_ipoll(icp, NULL); 44 if (byte0) { 45 val >>= 24; 46 } else if (width != 4) { 47 goto bad_access; 48 } 49 break; 50 case ICP_XIRR: 51 if (byte0) { 52 val = icp_ipoll(icp, NULL) >> 24; 53 } else if (width == 4) { 54 val = icp_accept(icp); 55 } else { 56 goto bad_access; 57 } 58 break; 59 case ICP_MFRR: 60 if (byte0) { 61 val = icp->mfrr; 62 } else { 63 goto bad_access; 64 } 65 break; 66 case ICP_LINKA: 67 if (width == 4) { 68 val = picp->links[0]; 69 } else { 70 goto bad_access; 71 } 72 break; 73 case ICP_LINKB: 74 if (width == 4) { 75 val = picp->links[1]; 76 } else { 77 goto bad_access; 78 } 79 break; 80 case ICP_LINKC: 81 if (width == 4) { 82 val = picp->links[2]; 83 } else { 84 goto bad_access; 85 } 86 break; 87 default: 88 bad_access: 89 qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" 90 HWADDR_PRIx"/%d\n", addr, width); 91 } 92 93 return val; 94 } 95 96 static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, 97 unsigned width) 98 { 99 ICPState *icp = ICP(opaque); 100 PnvICPState *picp = PNV_ICP(opaque); 101 bool byte0 = (width == 1 && (addr & 0x3) == 0); 102 103 switch (addr & 0xffc) { 104 case ICP_XIRR: 105 if (byte0) { 106 icp_set_cppr(icp, val); 107 } else if (width == 4) { 108 icp_eoi(icp, val); 109 } else { 110 goto bad_access; 111 } 112 break; 113 case ICP_MFRR: 114 if (byte0) { 115 icp_set_mfrr(icp, val); 116 } else { 117 goto bad_access; 118 } 119 break; 120 case ICP_LINKA: 121 if (width == 4) { 122 picp->links[0] = val; 123 } else { 124 goto bad_access; 125 } 126 break; 127 case ICP_LINKB: 128 if (width == 4) { 129 picp->links[1] = val; 130 } else { 131 goto bad_access; 132 } 133 break; 134 case ICP_LINKC: 135 if (width == 4) { 136 picp->links[2] = val; 137 } else { 138 goto bad_access; 139 } 140 break; 141 default: 142 bad_access: 143 qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" 144 HWADDR_PRIx"/%d\n", addr, width); 145 } 146 } 147 148 static const MemoryRegionOps pnv_icp_ops = { 149 .read = pnv_icp_read, 150 .write = pnv_icp_write, 151 .endianness = DEVICE_BIG_ENDIAN, 152 .valid = { 153 .min_access_size = 1, 154 .max_access_size = 4, 155 }, 156 .impl = { 157 .min_access_size = 1, 158 .max_access_size = 4, 159 }, 160 }; 161 162 static void pnv_icp_realize(DeviceState *dev, Error **errp) 163 { 164 ICPState *icp = ICP(dev); 165 PnvICPState *pnv_icp = PNV_ICP(icp); 166 ICPStateClass *icpc = ICP_GET_CLASS(icp); 167 Error *local_err = NULL; 168 169 icpc->parent_realize(dev, &local_err); 170 if (local_err) { 171 error_propagate(errp, local_err); 172 return; 173 } 174 175 memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, 176 icp, "icp-thread", 0x1000); 177 } 178 179 static void pnv_icp_class_init(ObjectClass *klass, void *data) 180 { 181 DeviceClass *dc = DEVICE_CLASS(klass); 182 ICPStateClass *icpc = ICP_CLASS(klass); 183 184 device_class_set_parent_realize(dc, pnv_icp_realize, 185 &icpc->parent_realize); 186 dc->desc = "PowerNV ICP"; 187 } 188 189 static const TypeInfo pnv_icp_info = { 190 .name = TYPE_PNV_ICP, 191 .parent = TYPE_ICP, 192 .instance_size = sizeof(PnvICPState), 193 .class_init = pnv_icp_class_init, 194 .class_size = sizeof(ICPStateClass), 195 }; 196 197 static void pnv_icp_register_types(void) 198 { 199 type_register_static(&pnv_icp_info); 200 } 201 202 type_init(pnv_icp_register_types) 203