1b5cec4c5SDavid Gibson /* 2b5cec4c5SDavid Gibson * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator 3b5cec4c5SDavid Gibson * 4b5cec4c5SDavid Gibson * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics 5b5cec4c5SDavid Gibson * 6b5cec4c5SDavid Gibson * Copyright (c) 2010,2011 David Gibson, IBM Corporation. 7b5cec4c5SDavid Gibson * 8b5cec4c5SDavid Gibson * Permission is hereby granted, free of charge, to any person obtaining a copy 9b5cec4c5SDavid Gibson * of this software and associated documentation files (the "Software"), to deal 10b5cec4c5SDavid Gibson * in the Software without restriction, including without limitation the rights 11b5cec4c5SDavid Gibson * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12b5cec4c5SDavid Gibson * copies of the Software, and to permit persons to whom the Software is 13b5cec4c5SDavid Gibson * furnished to do so, subject to the following conditions: 14b5cec4c5SDavid Gibson * 15b5cec4c5SDavid Gibson * The above copyright notice and this permission notice shall be included in 16b5cec4c5SDavid Gibson * all copies or substantial portions of the Software. 17b5cec4c5SDavid Gibson * 18b5cec4c5SDavid Gibson * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19b5cec4c5SDavid Gibson * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20b5cec4c5SDavid Gibson * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21b5cec4c5SDavid Gibson * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22b5cec4c5SDavid Gibson * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23b5cec4c5SDavid Gibson * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24b5cec4c5SDavid Gibson * THE SOFTWARE. 25b5cec4c5SDavid Gibson * 26b5cec4c5SDavid Gibson */ 27b5cec4c5SDavid Gibson 2883c9f4caSPaolo Bonzini #include "hw/hw.h" 29500efa23SDavid Gibson #include "trace.h" 300d09e41aSPaolo Bonzini #include "hw/ppc/spapr.h" 310d09e41aSPaolo Bonzini #include "hw/ppc/xics.h" 32*9ccff2a4SAlexey Kardashevskiy #include "qemu/error-report.h" 33b5cec4c5SDavid Gibson 348ffe04edSAlexey Kardashevskiy void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) 358ffe04edSAlexey Kardashevskiy { 368ffe04edSAlexey Kardashevskiy CPUState *cs = CPU(cpu); 378ffe04edSAlexey Kardashevskiy CPUPPCState *env = &cpu->env; 388ffe04edSAlexey Kardashevskiy ICPState *ss = &icp->ss[cs->cpu_index]; 398ffe04edSAlexey Kardashevskiy 408ffe04edSAlexey Kardashevskiy assert(cs->cpu_index < icp->nr_servers); 418ffe04edSAlexey Kardashevskiy 428ffe04edSAlexey Kardashevskiy switch (PPC_INPUT(env)) { 438ffe04edSAlexey Kardashevskiy case PPC_FLAGS_INPUT_POWER7: 448ffe04edSAlexey Kardashevskiy ss->output = env->irq_inputs[POWER7_INPUT_INT]; 458ffe04edSAlexey Kardashevskiy break; 468ffe04edSAlexey Kardashevskiy 478ffe04edSAlexey Kardashevskiy case PPC_FLAGS_INPUT_970: 488ffe04edSAlexey Kardashevskiy ss->output = env->irq_inputs[PPC970_INPUT_INT]; 498ffe04edSAlexey Kardashevskiy break; 508ffe04edSAlexey Kardashevskiy 518ffe04edSAlexey Kardashevskiy default: 52*9ccff2a4SAlexey Kardashevskiy error_report("XICS interrupt controller does not support this CPU " 53*9ccff2a4SAlexey Kardashevskiy "bus model"); 548ffe04edSAlexey Kardashevskiy abort(); 558ffe04edSAlexey Kardashevskiy } 568ffe04edSAlexey Kardashevskiy } 578ffe04edSAlexey Kardashevskiy 588ffe04edSAlexey Kardashevskiy static void xics_reset(DeviceState *d) 598ffe04edSAlexey Kardashevskiy { 608ffe04edSAlexey Kardashevskiy XICSState *icp = XICS(d); 618ffe04edSAlexey Kardashevskiy int i; 628ffe04edSAlexey Kardashevskiy 638ffe04edSAlexey Kardashevskiy for (i = 0; i < icp->nr_servers; i++) { 648ffe04edSAlexey Kardashevskiy device_reset(DEVICE(&icp->ss[i])); 658ffe04edSAlexey Kardashevskiy } 668ffe04edSAlexey Kardashevskiy 678ffe04edSAlexey Kardashevskiy device_reset(DEVICE(icp->ics)); 688ffe04edSAlexey Kardashevskiy } 698ffe04edSAlexey Kardashevskiy 70b5cec4c5SDavid Gibson /* 71b5cec4c5SDavid Gibson * ICP: Presentation layer 72b5cec4c5SDavid Gibson */ 73b5cec4c5SDavid Gibson 74b5cec4c5SDavid Gibson #define XISR_MASK 0x00ffffff 75b5cec4c5SDavid Gibson #define CPPR_MASK 0xff000000 76b5cec4c5SDavid Gibson 77b5cec4c5SDavid Gibson #define XISR(ss) (((ss)->xirr) & XISR_MASK) 78b5cec4c5SDavid Gibson #define CPPR(ss) (((ss)->xirr) >> 24) 79b5cec4c5SDavid Gibson 80c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr); 81c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics); 82c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr); 83b5cec4c5SDavid Gibson 84c04d6cfaSAnthony Liguori static void icp_check_ipi(XICSState *icp, int server) 85b5cec4c5SDavid Gibson { 86c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 87b5cec4c5SDavid Gibson 88b5cec4c5SDavid Gibson if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { 89b5cec4c5SDavid Gibson return; 90b5cec4c5SDavid Gibson } 91b5cec4c5SDavid Gibson 92500efa23SDavid Gibson trace_xics_icp_check_ipi(server, ss->mfrr); 93500efa23SDavid Gibson 94b5cec4c5SDavid Gibson if (XISR(ss)) { 95b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 96b5cec4c5SDavid Gibson } 97b5cec4c5SDavid Gibson 98b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; 99b5cec4c5SDavid Gibson ss->pending_priority = ss->mfrr; 100b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 101b5cec4c5SDavid Gibson } 102b5cec4c5SDavid Gibson 103c04d6cfaSAnthony Liguori static void icp_resend(XICSState *icp, int server) 104b5cec4c5SDavid Gibson { 105c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 106b5cec4c5SDavid Gibson 107b5cec4c5SDavid Gibson if (ss->mfrr < CPPR(ss)) { 108b5cec4c5SDavid Gibson icp_check_ipi(icp, server); 109b5cec4c5SDavid Gibson } 110b5cec4c5SDavid Gibson ics_resend(icp->ics); 111b5cec4c5SDavid Gibson } 112b5cec4c5SDavid Gibson 113c04d6cfaSAnthony Liguori static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr) 114b5cec4c5SDavid Gibson { 115c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 116b5cec4c5SDavid Gibson uint8_t old_cppr; 117b5cec4c5SDavid Gibson uint32_t old_xisr; 118b5cec4c5SDavid Gibson 119b5cec4c5SDavid Gibson old_cppr = CPPR(ss); 120b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); 121b5cec4c5SDavid Gibson 122b5cec4c5SDavid Gibson if (cppr < old_cppr) { 123b5cec4c5SDavid Gibson if (XISR(ss) && (cppr <= ss->pending_priority)) { 124b5cec4c5SDavid Gibson old_xisr = XISR(ss); 125b5cec4c5SDavid Gibson ss->xirr &= ~XISR_MASK; /* Clear XISR */ 126e03c902cSDavid Gibson ss->pending_priority = 0xff; 127b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 128b5cec4c5SDavid Gibson ics_reject(icp->ics, old_xisr); 129b5cec4c5SDavid Gibson } 130b5cec4c5SDavid Gibson } else { 131b5cec4c5SDavid Gibson if (!XISR(ss)) { 132b5cec4c5SDavid Gibson icp_resend(icp, server); 133b5cec4c5SDavid Gibson } 134b5cec4c5SDavid Gibson } 135b5cec4c5SDavid Gibson } 136b5cec4c5SDavid Gibson 137c04d6cfaSAnthony Liguori static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr) 138b5cec4c5SDavid Gibson { 139c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 140b5cec4c5SDavid Gibson 141b5cec4c5SDavid Gibson ss->mfrr = mfrr; 142b5cec4c5SDavid Gibson if (mfrr < CPPR(ss)) { 143bf0175deSDavid Gibson icp_check_ipi(icp, server); 144b5cec4c5SDavid Gibson } 145b5cec4c5SDavid Gibson } 146b5cec4c5SDavid Gibson 147c04d6cfaSAnthony Liguori static uint32_t icp_accept(ICPState *ss) 148b5cec4c5SDavid Gibson { 149500efa23SDavid Gibson uint32_t xirr = ss->xirr; 150b5cec4c5SDavid Gibson 151b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 152b5cec4c5SDavid Gibson ss->xirr = ss->pending_priority << 24; 153e03c902cSDavid Gibson ss->pending_priority = 0xff; 154500efa23SDavid Gibson 155500efa23SDavid Gibson trace_xics_icp_accept(xirr, ss->xirr); 156500efa23SDavid Gibson 157b5cec4c5SDavid Gibson return xirr; 158b5cec4c5SDavid Gibson } 159b5cec4c5SDavid Gibson 160c04d6cfaSAnthony Liguori static void icp_eoi(XICSState *icp, int server, uint32_t xirr) 161b5cec4c5SDavid Gibson { 162c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 163b5cec4c5SDavid Gibson 164b5cec4c5SDavid Gibson /* Send EOI -> ICS */ 165b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); 166500efa23SDavid Gibson trace_xics_icp_eoi(server, xirr, ss->xirr); 167d07fee7eSDavid Gibson ics_eoi(icp->ics, xirr & XISR_MASK); 168b5cec4c5SDavid Gibson if (!XISR(ss)) { 169b5cec4c5SDavid Gibson icp_resend(icp, server); 170b5cec4c5SDavid Gibson } 171b5cec4c5SDavid Gibson } 172b5cec4c5SDavid Gibson 173c04d6cfaSAnthony Liguori static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) 174b5cec4c5SDavid Gibson { 175c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 176b5cec4c5SDavid Gibson 177500efa23SDavid Gibson trace_xics_icp_irq(server, nr, priority); 178500efa23SDavid Gibson 179b5cec4c5SDavid Gibson if ((priority >= CPPR(ss)) 180b5cec4c5SDavid Gibson || (XISR(ss) && (ss->pending_priority <= priority))) { 181b5cec4c5SDavid Gibson ics_reject(icp->ics, nr); 182b5cec4c5SDavid Gibson } else { 183b5cec4c5SDavid Gibson if (XISR(ss)) { 184b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 185b5cec4c5SDavid Gibson } 186b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); 187b5cec4c5SDavid Gibson ss->pending_priority = priority; 188500efa23SDavid Gibson trace_xics_icp_raise(ss->xirr, ss->pending_priority); 189b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 190b5cec4c5SDavid Gibson } 191b5cec4c5SDavid Gibson } 192b5cec4c5SDavid Gibson 193c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_icp_server = { 194c04d6cfaSAnthony Liguori .name = "icp/server", 195c04d6cfaSAnthony Liguori .version_id = 1, 196c04d6cfaSAnthony Liguori .minimum_version_id = 1, 197c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 198c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 199c04d6cfaSAnthony Liguori /* Sanity check */ 200c04d6cfaSAnthony Liguori VMSTATE_UINT32(xirr, ICPState), 201c04d6cfaSAnthony Liguori VMSTATE_UINT8(pending_priority, ICPState), 202c04d6cfaSAnthony Liguori VMSTATE_UINT8(mfrr, ICPState), 203c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 204c04d6cfaSAnthony Liguori }, 205c04d6cfaSAnthony Liguori }; 206c04d6cfaSAnthony Liguori 207c04d6cfaSAnthony Liguori static void icp_reset(DeviceState *dev) 208c04d6cfaSAnthony Liguori { 209c04d6cfaSAnthony Liguori ICPState *icp = ICP(dev); 210c04d6cfaSAnthony Liguori 211c04d6cfaSAnthony Liguori icp->xirr = 0; 212c04d6cfaSAnthony Liguori icp->pending_priority = 0xff; 213c04d6cfaSAnthony Liguori icp->mfrr = 0xff; 214c04d6cfaSAnthony Liguori 215c04d6cfaSAnthony Liguori /* Make all outputs are deasserted */ 216c04d6cfaSAnthony Liguori qemu_set_irq(icp->output, 0); 217c04d6cfaSAnthony Liguori } 218c04d6cfaSAnthony Liguori 219c04d6cfaSAnthony Liguori static void icp_class_init(ObjectClass *klass, void *data) 220c04d6cfaSAnthony Liguori { 221c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 222c04d6cfaSAnthony Liguori 223c04d6cfaSAnthony Liguori dc->reset = icp_reset; 224c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_icp_server; 225c04d6cfaSAnthony Liguori } 226c04d6cfaSAnthony Liguori 227c04d6cfaSAnthony Liguori static TypeInfo icp_info = { 228c04d6cfaSAnthony Liguori .name = TYPE_ICP, 229c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 230c04d6cfaSAnthony Liguori .instance_size = sizeof(ICPState), 231c04d6cfaSAnthony Liguori .class_init = icp_class_init, 232c04d6cfaSAnthony Liguori }; 233c04d6cfaSAnthony Liguori 234b5cec4c5SDavid Gibson /* 235b5cec4c5SDavid Gibson * ICS: Source layer 236b5cec4c5SDavid Gibson */ 237c04d6cfaSAnthony Liguori static int ics_valid_irq(ICSState *ics, uint32_t nr) 238b5cec4c5SDavid Gibson { 239b5cec4c5SDavid Gibson return (nr >= ics->offset) 240b5cec4c5SDavid Gibson && (nr < (ics->offset + ics->nr_irqs)); 241b5cec4c5SDavid Gibson } 242b5cec4c5SDavid Gibson 243c04d6cfaSAnthony Liguori static void resend_msi(ICSState *ics, int srcno) 244b5cec4c5SDavid Gibson { 245c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 246d07fee7eSDavid Gibson 247d07fee7eSDavid Gibson /* FIXME: filter by server#? */ 24898ca8c02SDavid Gibson if (irq->status & XICS_STATUS_REJECTED) { 24998ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_REJECTED; 250d07fee7eSDavid Gibson if (irq->priority != 0xff) { 251d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, 252d07fee7eSDavid Gibson irq->priority); 253d07fee7eSDavid Gibson } 254d07fee7eSDavid Gibson } 255d07fee7eSDavid Gibson } 256d07fee7eSDavid Gibson 257c04d6cfaSAnthony Liguori static void resend_lsi(ICSState *ics, int srcno) 258d07fee7eSDavid Gibson { 259c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 260d07fee7eSDavid Gibson 26198ca8c02SDavid Gibson if ((irq->priority != 0xff) 26298ca8c02SDavid Gibson && (irq->status & XICS_STATUS_ASSERTED) 26398ca8c02SDavid Gibson && !(irq->status & XICS_STATUS_SENT)) { 26498ca8c02SDavid Gibson irq->status |= XICS_STATUS_SENT; 265d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 266d07fee7eSDavid Gibson } 267d07fee7eSDavid Gibson } 268d07fee7eSDavid Gibson 269c04d6cfaSAnthony Liguori static void set_irq_msi(ICSState *ics, int srcno, int val) 270d07fee7eSDavid Gibson { 271c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 272b5cec4c5SDavid Gibson 273500efa23SDavid Gibson trace_xics_set_irq_msi(srcno, srcno + ics->offset); 274500efa23SDavid Gibson 275b5cec4c5SDavid Gibson if (val) { 276b5cec4c5SDavid Gibson if (irq->priority == 0xff) { 27798ca8c02SDavid Gibson irq->status |= XICS_STATUS_MASKED_PENDING; 278500efa23SDavid Gibson trace_xics_masked_pending(); 279b5cec4c5SDavid Gibson } else { 280cc67b9c8SDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 281b5cec4c5SDavid Gibson } 282b5cec4c5SDavid Gibson } 283b5cec4c5SDavid Gibson } 284b5cec4c5SDavid Gibson 285c04d6cfaSAnthony Liguori static void set_irq_lsi(ICSState *ics, int srcno, int val) 286d07fee7eSDavid Gibson { 287c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 288d07fee7eSDavid Gibson 289500efa23SDavid Gibson trace_xics_set_irq_lsi(srcno, srcno + ics->offset); 29098ca8c02SDavid Gibson if (val) { 29198ca8c02SDavid Gibson irq->status |= XICS_STATUS_ASSERTED; 29298ca8c02SDavid Gibson } else { 29398ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_ASSERTED; 29498ca8c02SDavid Gibson } 295d07fee7eSDavid Gibson resend_lsi(ics, srcno); 296d07fee7eSDavid Gibson } 297d07fee7eSDavid Gibson 298d07fee7eSDavid Gibson static void ics_set_irq(void *opaque, int srcno, int val) 299d07fee7eSDavid Gibson { 300c04d6cfaSAnthony Liguori ICSState *ics = (ICSState *)opaque; 301d07fee7eSDavid Gibson 30222a2611cSDavid Gibson if (ics->islsi[srcno]) { 303d07fee7eSDavid Gibson set_irq_lsi(ics, srcno, val); 304d07fee7eSDavid Gibson } else { 305d07fee7eSDavid Gibson set_irq_msi(ics, srcno, val); 306d07fee7eSDavid Gibson } 307d07fee7eSDavid Gibson } 308d07fee7eSDavid Gibson 309c04d6cfaSAnthony Liguori static void write_xive_msi(ICSState *ics, int srcno) 310d07fee7eSDavid Gibson { 311c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 312d07fee7eSDavid Gibson 31398ca8c02SDavid Gibson if (!(irq->status & XICS_STATUS_MASKED_PENDING) 31498ca8c02SDavid Gibson || (irq->priority == 0xff)) { 315d07fee7eSDavid Gibson return; 316d07fee7eSDavid Gibson } 317d07fee7eSDavid Gibson 31898ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_MASKED_PENDING; 319d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 320d07fee7eSDavid Gibson } 321d07fee7eSDavid Gibson 322c04d6cfaSAnthony Liguori static void write_xive_lsi(ICSState *ics, int srcno) 323d07fee7eSDavid Gibson { 324d07fee7eSDavid Gibson resend_lsi(ics, srcno); 325d07fee7eSDavid Gibson } 326d07fee7eSDavid Gibson 327c04d6cfaSAnthony Liguori static void ics_write_xive(ICSState *ics, int nr, int server, 3283fe719f4SDavid Gibson uint8_t priority, uint8_t saved_priority) 329d07fee7eSDavid Gibson { 330d07fee7eSDavid Gibson int srcno = nr - ics->offset; 331c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 332d07fee7eSDavid Gibson 333d07fee7eSDavid Gibson irq->server = server; 334d07fee7eSDavid Gibson irq->priority = priority; 3353fe719f4SDavid Gibson irq->saved_priority = saved_priority; 336d07fee7eSDavid Gibson 337500efa23SDavid Gibson trace_xics_ics_write_xive(nr, srcno, server, priority); 338500efa23SDavid Gibson 33922a2611cSDavid Gibson if (ics->islsi[srcno]) { 340d07fee7eSDavid Gibson write_xive_lsi(ics, srcno); 341d07fee7eSDavid Gibson } else { 342d07fee7eSDavid Gibson write_xive_msi(ics, srcno); 343d07fee7eSDavid Gibson } 344d07fee7eSDavid Gibson } 345d07fee7eSDavid Gibson 346c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr) 347b5cec4c5SDavid Gibson { 348c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + nr - ics->offset; 349b5cec4c5SDavid Gibson 350500efa23SDavid Gibson trace_xics_ics_reject(nr, nr - ics->offset); 35198ca8c02SDavid Gibson irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ 35298ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ 353b5cec4c5SDavid Gibson } 354b5cec4c5SDavid Gibson 355c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics) 356b5cec4c5SDavid Gibson { 357b5cec4c5SDavid Gibson int i; 358b5cec4c5SDavid Gibson 359b5cec4c5SDavid Gibson for (i = 0; i < ics->nr_irqs; i++) { 360b5cec4c5SDavid Gibson /* FIXME: filter by server#? */ 36122a2611cSDavid Gibson if (ics->islsi[i]) { 362d07fee7eSDavid Gibson resend_lsi(ics, i); 363d07fee7eSDavid Gibson } else { 364d07fee7eSDavid Gibson resend_msi(ics, i); 365b5cec4c5SDavid Gibson } 366b5cec4c5SDavid Gibson } 367b5cec4c5SDavid Gibson } 368b5cec4c5SDavid Gibson 369c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr) 370b5cec4c5SDavid Gibson { 371d07fee7eSDavid Gibson int srcno = nr - ics->offset; 372c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 373d07fee7eSDavid Gibson 374500efa23SDavid Gibson trace_xics_ics_eoi(nr); 375500efa23SDavid Gibson 37622a2611cSDavid Gibson if (ics->islsi[srcno]) { 37798ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; 378d07fee7eSDavid Gibson } 379b5cec4c5SDavid Gibson } 380b5cec4c5SDavid Gibson 381c04d6cfaSAnthony Liguori static void ics_reset(DeviceState *dev) 382c04d6cfaSAnthony Liguori { 383c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 384c04d6cfaSAnthony Liguori int i; 385c04d6cfaSAnthony Liguori 386c04d6cfaSAnthony Liguori memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); 387c04d6cfaSAnthony Liguori for (i = 0; i < ics->nr_irqs; i++) { 388c04d6cfaSAnthony Liguori ics->irqs[i].priority = 0xff; 389c04d6cfaSAnthony Liguori ics->irqs[i].saved_priority = 0xff; 390c04d6cfaSAnthony Liguori } 391c04d6cfaSAnthony Liguori } 392c04d6cfaSAnthony Liguori 393c04d6cfaSAnthony Liguori static int ics_post_load(void *opaque, int version_id) 394c04d6cfaSAnthony Liguori { 395c04d6cfaSAnthony Liguori int i; 396c04d6cfaSAnthony Liguori ICSState *ics = opaque; 397c04d6cfaSAnthony Liguori 398c04d6cfaSAnthony Liguori for (i = 0; i < ics->icp->nr_servers; i++) { 399c04d6cfaSAnthony Liguori icp_resend(ics->icp, i); 400c04d6cfaSAnthony Liguori } 401c04d6cfaSAnthony Liguori 402c04d6cfaSAnthony Liguori return 0; 403c04d6cfaSAnthony Liguori } 404c04d6cfaSAnthony Liguori 405c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics_irq = { 406c04d6cfaSAnthony Liguori .name = "ics/irq", 407c04d6cfaSAnthony Liguori .version_id = 1, 408c04d6cfaSAnthony Liguori .minimum_version_id = 1, 409c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 410c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 411c04d6cfaSAnthony Liguori VMSTATE_UINT32(server, ICSIRQState), 412c04d6cfaSAnthony Liguori VMSTATE_UINT8(priority, ICSIRQState), 413c04d6cfaSAnthony Liguori VMSTATE_UINT8(saved_priority, ICSIRQState), 414c04d6cfaSAnthony Liguori VMSTATE_UINT8(status, ICSIRQState), 415c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 416c04d6cfaSAnthony Liguori }, 417c04d6cfaSAnthony Liguori }; 418c04d6cfaSAnthony Liguori 419c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics = { 420c04d6cfaSAnthony Liguori .name = "ics", 421c04d6cfaSAnthony Liguori .version_id = 1, 422c04d6cfaSAnthony Liguori .minimum_version_id = 1, 423c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 424c04d6cfaSAnthony Liguori .post_load = ics_post_load, 425c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 426c04d6cfaSAnthony Liguori /* Sanity check */ 427c04d6cfaSAnthony Liguori VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), 428c04d6cfaSAnthony Liguori 429c04d6cfaSAnthony Liguori VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, 430c04d6cfaSAnthony Liguori vmstate_ics_irq, ICSIRQState), 431c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 432c04d6cfaSAnthony Liguori }, 433c04d6cfaSAnthony Liguori }; 434c04d6cfaSAnthony Liguori 435c04d6cfaSAnthony Liguori static int ics_realize(DeviceState *dev) 436c04d6cfaSAnthony Liguori { 437c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 438c04d6cfaSAnthony Liguori 439c04d6cfaSAnthony Liguori ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); 440c04d6cfaSAnthony Liguori ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); 441c04d6cfaSAnthony Liguori ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); 442c04d6cfaSAnthony Liguori 443c04d6cfaSAnthony Liguori return 0; 444c04d6cfaSAnthony Liguori } 445c04d6cfaSAnthony Liguori 446c04d6cfaSAnthony Liguori static void ics_class_init(ObjectClass *klass, void *data) 447c04d6cfaSAnthony Liguori { 448c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 449c04d6cfaSAnthony Liguori 450c04d6cfaSAnthony Liguori dc->init = ics_realize; 451c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_ics; 452c04d6cfaSAnthony Liguori dc->reset = ics_reset; 453c04d6cfaSAnthony Liguori } 454c04d6cfaSAnthony Liguori 455c04d6cfaSAnthony Liguori static TypeInfo ics_info = { 456c04d6cfaSAnthony Liguori .name = TYPE_ICS, 457c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 458c04d6cfaSAnthony Liguori .instance_size = sizeof(ICSState), 459c04d6cfaSAnthony Liguori .class_init = ics_class_init, 460c04d6cfaSAnthony Liguori }; 461c04d6cfaSAnthony Liguori 462b5cec4c5SDavid Gibson /* 463b5cec4c5SDavid Gibson * Exported functions 464b5cec4c5SDavid Gibson */ 465b5cec4c5SDavid Gibson 466c04d6cfaSAnthony Liguori qemu_irq xics_get_qirq(XICSState *icp, int irq) 467b5cec4c5SDavid Gibson { 4681ecbbab4SDavid Gibson if (!ics_valid_irq(icp->ics, irq)) { 469b5cec4c5SDavid Gibson return NULL; 470b5cec4c5SDavid Gibson } 471b5cec4c5SDavid Gibson 472a307d594SAlexey Kardashevskiy return icp->ics->qirqs[irq - icp->ics->offset]; 473a307d594SAlexey Kardashevskiy } 474a307d594SAlexey Kardashevskiy 475c04d6cfaSAnthony Liguori void xics_set_irq_type(XICSState *icp, int irq, bool lsi) 476a307d594SAlexey Kardashevskiy { 4771ecbbab4SDavid Gibson assert(ics_valid_irq(icp->ics, irq)); 478d07fee7eSDavid Gibson 47922a2611cSDavid Gibson icp->ics->islsi[irq - icp->ics->offset] = lsi; 480b5cec4c5SDavid Gibson } 481b5cec4c5SDavid Gibson 482c04d6cfaSAnthony Liguori /* 483c04d6cfaSAnthony Liguori * Guest interfaces 484c04d6cfaSAnthony Liguori */ 485c04d6cfaSAnthony Liguori 486b13ce26dSAndreas Färber static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 487b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 488b5cec4c5SDavid Gibson { 48955e5c285SAndreas Färber CPUState *cs = CPU(cpu); 490b5cec4c5SDavid Gibson target_ulong cppr = args[0]; 491b5cec4c5SDavid Gibson 49255e5c285SAndreas Färber icp_set_cppr(spapr->icp, cs->cpu_index, cppr); 493b5cec4c5SDavid Gibson return H_SUCCESS; 494b5cec4c5SDavid Gibson } 495b5cec4c5SDavid Gibson 496b13ce26dSAndreas Färber static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 497b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 498b5cec4c5SDavid Gibson { 499b5cec4c5SDavid Gibson target_ulong server = args[0]; 500b5cec4c5SDavid Gibson target_ulong mfrr = args[1]; 501b5cec4c5SDavid Gibson 502b5cec4c5SDavid Gibson if (server >= spapr->icp->nr_servers) { 503b5cec4c5SDavid Gibson return H_PARAMETER; 504b5cec4c5SDavid Gibson } 505b5cec4c5SDavid Gibson 506b5cec4c5SDavid Gibson icp_set_mfrr(spapr->icp, server, mfrr); 507b5cec4c5SDavid Gibson return H_SUCCESS; 508b5cec4c5SDavid Gibson } 509b5cec4c5SDavid Gibson 510b13ce26dSAndreas Färber static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 511b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 512b5cec4c5SDavid Gibson { 51355e5c285SAndreas Färber CPUState *cs = CPU(cpu); 51455e5c285SAndreas Färber uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); 515b5cec4c5SDavid Gibson 516b5cec4c5SDavid Gibson args[0] = xirr; 517b5cec4c5SDavid Gibson return H_SUCCESS; 518b5cec4c5SDavid Gibson } 519b5cec4c5SDavid Gibson 520b13ce26dSAndreas Färber static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 521b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 522b5cec4c5SDavid Gibson { 52355e5c285SAndreas Färber CPUState *cs = CPU(cpu); 524b5cec4c5SDavid Gibson target_ulong xirr = args[0]; 525b5cec4c5SDavid Gibson 52655e5c285SAndreas Färber icp_eoi(spapr->icp, cs->cpu_index, xirr); 527b5cec4c5SDavid Gibson return H_SUCCESS; 528b5cec4c5SDavid Gibson } 529b5cec4c5SDavid Gibson 530210b580bSAnthony Liguori static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 531210b580bSAnthony Liguori uint32_t token, 532b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 533b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 534b5cec4c5SDavid Gibson { 535c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 536b5cec4c5SDavid Gibson uint32_t nr, server, priority; 537b5cec4c5SDavid Gibson 538b5cec4c5SDavid Gibson if ((nargs != 3) || (nret != 1)) { 539b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 540b5cec4c5SDavid Gibson return; 541b5cec4c5SDavid Gibson } 542b5cec4c5SDavid Gibson 543b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 544b5cec4c5SDavid Gibson server = rtas_ld(args, 1); 545b5cec4c5SDavid Gibson priority = rtas_ld(args, 2); 546b5cec4c5SDavid Gibson 547b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) 548b5cec4c5SDavid Gibson || (priority > 0xff)) { 549b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 550b5cec4c5SDavid Gibson return; 551b5cec4c5SDavid Gibson } 552b5cec4c5SDavid Gibson 5533fe719f4SDavid Gibson ics_write_xive(ics, nr, server, priority, priority); 554b5cec4c5SDavid Gibson 555b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 556b5cec4c5SDavid Gibson } 557b5cec4c5SDavid Gibson 558210b580bSAnthony Liguori static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 559210b580bSAnthony Liguori uint32_t token, 560b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 561b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 562b5cec4c5SDavid Gibson { 563c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 564b5cec4c5SDavid Gibson uint32_t nr; 565b5cec4c5SDavid Gibson 566b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 3)) { 567b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 568b5cec4c5SDavid Gibson return; 569b5cec4c5SDavid Gibson } 570b5cec4c5SDavid Gibson 571b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 572b5cec4c5SDavid Gibson 573b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 574b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 575b5cec4c5SDavid Gibson return; 576b5cec4c5SDavid Gibson } 577b5cec4c5SDavid Gibson 578b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 579b5cec4c5SDavid Gibson rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); 580b5cec4c5SDavid Gibson rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); 581b5cec4c5SDavid Gibson } 582b5cec4c5SDavid Gibson 583210b580bSAnthony Liguori static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, 584210b580bSAnthony Liguori uint32_t token, 585b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 586b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 587b5cec4c5SDavid Gibson { 588c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 589b5cec4c5SDavid Gibson uint32_t nr; 590b5cec4c5SDavid Gibson 591b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 592b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 593b5cec4c5SDavid Gibson return; 594b5cec4c5SDavid Gibson } 595b5cec4c5SDavid Gibson 596b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 597b5cec4c5SDavid Gibson 598b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 599b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 600b5cec4c5SDavid Gibson return; 601b5cec4c5SDavid Gibson } 602b5cec4c5SDavid Gibson 6033fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, 6043fe719f4SDavid Gibson ics->irqs[nr - ics->offset].priority); 605b5cec4c5SDavid Gibson 606b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 607b5cec4c5SDavid Gibson } 608b5cec4c5SDavid Gibson 609210b580bSAnthony Liguori static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, 610210b580bSAnthony Liguori uint32_t token, 611b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 612b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 613b5cec4c5SDavid Gibson { 614c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 615b5cec4c5SDavid Gibson uint32_t nr; 616b5cec4c5SDavid Gibson 617b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 618b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 619b5cec4c5SDavid Gibson return; 620b5cec4c5SDavid Gibson } 621b5cec4c5SDavid Gibson 622b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 623b5cec4c5SDavid Gibson 624b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 625b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 626b5cec4c5SDavid Gibson return; 627b5cec4c5SDavid Gibson } 628b5cec4c5SDavid Gibson 6293fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 6303fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority, 6313fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority); 632b5cec4c5SDavid Gibson 633b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 634b5cec4c5SDavid Gibson } 635b5cec4c5SDavid Gibson 636c04d6cfaSAnthony Liguori /* 637c04d6cfaSAnthony Liguori * XICS 638c04d6cfaSAnthony Liguori */ 639c04d6cfaSAnthony Liguori 640c04d6cfaSAnthony Liguori static void xics_realize(DeviceState *dev, Error **errp) 6417b565160SDavid Gibson { 642c04d6cfaSAnthony Liguori XICSState *icp = XICS(dev); 643c04d6cfaSAnthony Liguori ICSState *ics = icp->ics; 644c04d6cfaSAnthony Liguori int i; 6457b565160SDavid Gibson 64633a0e5d8SAlexey Kardashevskiy /* Registration of global state belongs into realize */ 64733a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,set-xive", rtas_set_xive); 64833a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,get-xive", rtas_get_xive); 64933a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,int-off", rtas_int_off); 65033a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,int-on", rtas_int_on); 65133a0e5d8SAlexey Kardashevskiy 65233a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_CPPR, h_cppr); 65333a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_IPI, h_ipi); 65433a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_XIRR, h_xirr); 65533a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_EOI, h_eoi); 65633a0e5d8SAlexey Kardashevskiy 657c04d6cfaSAnthony Liguori ics->nr_irqs = icp->nr_irqs; 658bf3bc4c4SBen Herrenschmidt ics->offset = XICS_IRQ_BASE; 659b5cec4c5SDavid Gibson ics->icp = icp; 660c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(ics)); 661b5cec4c5SDavid Gibson 662c04d6cfaSAnthony Liguori icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); 663c04d6cfaSAnthony Liguori for (i = 0; i < icp->nr_servers; i++) { 664c04d6cfaSAnthony Liguori char buffer[32]; 665213f0c4fSAndreas Färber object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); 666c04d6cfaSAnthony Liguori snprintf(buffer, sizeof(buffer), "icp[%d]", i); 667c04d6cfaSAnthony Liguori object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); 668c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(&icp->ss[i])); 669c04d6cfaSAnthony Liguori } 670c04d6cfaSAnthony Liguori } 671b5cec4c5SDavid Gibson 672c04d6cfaSAnthony Liguori static void xics_initfn(Object *obj) 673c04d6cfaSAnthony Liguori { 674c04d6cfaSAnthony Liguori XICSState *xics = XICS(obj); 675c04d6cfaSAnthony Liguori 676c04d6cfaSAnthony Liguori xics->ics = ICS(object_new(TYPE_ICS)); 677c04d6cfaSAnthony Liguori object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); 678c04d6cfaSAnthony Liguori } 679c04d6cfaSAnthony Liguori 680c04d6cfaSAnthony Liguori static Property xics_properties[] = { 681c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1), 682c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1), 683c04d6cfaSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 684c04d6cfaSAnthony Liguori }; 685c04d6cfaSAnthony Liguori 686c04d6cfaSAnthony Liguori static void xics_class_init(ObjectClass *oc, void *data) 687c04d6cfaSAnthony Liguori { 688c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(oc); 689c04d6cfaSAnthony Liguori 690c04d6cfaSAnthony Liguori dc->realize = xics_realize; 691c04d6cfaSAnthony Liguori dc->props = xics_properties; 692c04d6cfaSAnthony Liguori dc->reset = xics_reset; 693b5cec4c5SDavid Gibson } 694c04d6cfaSAnthony Liguori 695c04d6cfaSAnthony Liguori static const TypeInfo xics_info = { 696c04d6cfaSAnthony Liguori .name = TYPE_XICS, 697c04d6cfaSAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 698c04d6cfaSAnthony Liguori .instance_size = sizeof(XICSState), 699c04d6cfaSAnthony Liguori .class_init = xics_class_init, 700c04d6cfaSAnthony Liguori .instance_init = xics_initfn, 701c04d6cfaSAnthony Liguori }; 702c04d6cfaSAnthony Liguori 703c04d6cfaSAnthony Liguori static void xics_register_types(void) 704c04d6cfaSAnthony Liguori { 705c04d6cfaSAnthony Liguori type_register_static(&xics_info); 706c04d6cfaSAnthony Liguori type_register_static(&ics_info); 707c04d6cfaSAnthony Liguori type_register_static(&icp_info); 708c04d6cfaSAnthony Liguori } 709c04d6cfaSAnthony Liguori 710c04d6cfaSAnthony Liguori type_init(xics_register_types) 711