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" 32b5cec4c5SDavid Gibson 33*8ffe04edSAlexey Kardashevskiy void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) 34*8ffe04edSAlexey Kardashevskiy { 35*8ffe04edSAlexey Kardashevskiy CPUState *cs = CPU(cpu); 36*8ffe04edSAlexey Kardashevskiy CPUPPCState *env = &cpu->env; 37*8ffe04edSAlexey Kardashevskiy ICPState *ss = &icp->ss[cs->cpu_index]; 38*8ffe04edSAlexey Kardashevskiy 39*8ffe04edSAlexey Kardashevskiy assert(cs->cpu_index < icp->nr_servers); 40*8ffe04edSAlexey Kardashevskiy 41*8ffe04edSAlexey Kardashevskiy switch (PPC_INPUT(env)) { 42*8ffe04edSAlexey Kardashevskiy case PPC_FLAGS_INPUT_POWER7: 43*8ffe04edSAlexey Kardashevskiy ss->output = env->irq_inputs[POWER7_INPUT_INT]; 44*8ffe04edSAlexey Kardashevskiy break; 45*8ffe04edSAlexey Kardashevskiy 46*8ffe04edSAlexey Kardashevskiy case PPC_FLAGS_INPUT_970: 47*8ffe04edSAlexey Kardashevskiy ss->output = env->irq_inputs[PPC970_INPUT_INT]; 48*8ffe04edSAlexey Kardashevskiy break; 49*8ffe04edSAlexey Kardashevskiy 50*8ffe04edSAlexey Kardashevskiy default: 51*8ffe04edSAlexey Kardashevskiy fprintf(stderr, "XICS interrupt controller does not support this CPU " 52*8ffe04edSAlexey Kardashevskiy "bus model\n"); 53*8ffe04edSAlexey Kardashevskiy abort(); 54*8ffe04edSAlexey Kardashevskiy } 55*8ffe04edSAlexey Kardashevskiy } 56*8ffe04edSAlexey Kardashevskiy 57*8ffe04edSAlexey Kardashevskiy static void xics_reset(DeviceState *d) 58*8ffe04edSAlexey Kardashevskiy { 59*8ffe04edSAlexey Kardashevskiy XICSState *icp = XICS(d); 60*8ffe04edSAlexey Kardashevskiy int i; 61*8ffe04edSAlexey Kardashevskiy 62*8ffe04edSAlexey Kardashevskiy for (i = 0; i < icp->nr_servers; i++) { 63*8ffe04edSAlexey Kardashevskiy device_reset(DEVICE(&icp->ss[i])); 64*8ffe04edSAlexey Kardashevskiy } 65*8ffe04edSAlexey Kardashevskiy 66*8ffe04edSAlexey Kardashevskiy device_reset(DEVICE(icp->ics)); 67*8ffe04edSAlexey Kardashevskiy } 68*8ffe04edSAlexey Kardashevskiy 69b5cec4c5SDavid Gibson /* 70b5cec4c5SDavid Gibson * ICP: Presentation layer 71b5cec4c5SDavid Gibson */ 72b5cec4c5SDavid Gibson 73b5cec4c5SDavid Gibson #define XISR_MASK 0x00ffffff 74b5cec4c5SDavid Gibson #define CPPR_MASK 0xff000000 75b5cec4c5SDavid Gibson 76b5cec4c5SDavid Gibson #define XISR(ss) (((ss)->xirr) & XISR_MASK) 77b5cec4c5SDavid Gibson #define CPPR(ss) (((ss)->xirr) >> 24) 78b5cec4c5SDavid Gibson 79c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr); 80c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics); 81c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr); 82b5cec4c5SDavid Gibson 83c04d6cfaSAnthony Liguori static void icp_check_ipi(XICSState *icp, int server) 84b5cec4c5SDavid Gibson { 85c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 86b5cec4c5SDavid Gibson 87b5cec4c5SDavid Gibson if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { 88b5cec4c5SDavid Gibson return; 89b5cec4c5SDavid Gibson } 90b5cec4c5SDavid Gibson 91500efa23SDavid Gibson trace_xics_icp_check_ipi(server, ss->mfrr); 92500efa23SDavid Gibson 93b5cec4c5SDavid Gibson if (XISR(ss)) { 94b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 95b5cec4c5SDavid Gibson } 96b5cec4c5SDavid Gibson 97b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; 98b5cec4c5SDavid Gibson ss->pending_priority = ss->mfrr; 99b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 100b5cec4c5SDavid Gibson } 101b5cec4c5SDavid Gibson 102c04d6cfaSAnthony Liguori static void icp_resend(XICSState *icp, int server) 103b5cec4c5SDavid Gibson { 104c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 105b5cec4c5SDavid Gibson 106b5cec4c5SDavid Gibson if (ss->mfrr < CPPR(ss)) { 107b5cec4c5SDavid Gibson icp_check_ipi(icp, server); 108b5cec4c5SDavid Gibson } 109b5cec4c5SDavid Gibson ics_resend(icp->ics); 110b5cec4c5SDavid Gibson } 111b5cec4c5SDavid Gibson 112c04d6cfaSAnthony Liguori static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr) 113b5cec4c5SDavid Gibson { 114c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 115b5cec4c5SDavid Gibson uint8_t old_cppr; 116b5cec4c5SDavid Gibson uint32_t old_xisr; 117b5cec4c5SDavid Gibson 118b5cec4c5SDavid Gibson old_cppr = CPPR(ss); 119b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); 120b5cec4c5SDavid Gibson 121b5cec4c5SDavid Gibson if (cppr < old_cppr) { 122b5cec4c5SDavid Gibson if (XISR(ss) && (cppr <= ss->pending_priority)) { 123b5cec4c5SDavid Gibson old_xisr = XISR(ss); 124b5cec4c5SDavid Gibson ss->xirr &= ~XISR_MASK; /* Clear XISR */ 125e03c902cSDavid Gibson ss->pending_priority = 0xff; 126b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 127b5cec4c5SDavid Gibson ics_reject(icp->ics, old_xisr); 128b5cec4c5SDavid Gibson } 129b5cec4c5SDavid Gibson } else { 130b5cec4c5SDavid Gibson if (!XISR(ss)) { 131b5cec4c5SDavid Gibson icp_resend(icp, server); 132b5cec4c5SDavid Gibson } 133b5cec4c5SDavid Gibson } 134b5cec4c5SDavid Gibson } 135b5cec4c5SDavid Gibson 136c04d6cfaSAnthony Liguori static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr) 137b5cec4c5SDavid Gibson { 138c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 139b5cec4c5SDavid Gibson 140b5cec4c5SDavid Gibson ss->mfrr = mfrr; 141b5cec4c5SDavid Gibson if (mfrr < CPPR(ss)) { 142bf0175deSDavid Gibson icp_check_ipi(icp, server); 143b5cec4c5SDavid Gibson } 144b5cec4c5SDavid Gibson } 145b5cec4c5SDavid Gibson 146c04d6cfaSAnthony Liguori static uint32_t icp_accept(ICPState *ss) 147b5cec4c5SDavid Gibson { 148500efa23SDavid Gibson uint32_t xirr = ss->xirr; 149b5cec4c5SDavid Gibson 150b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 151b5cec4c5SDavid Gibson ss->xirr = ss->pending_priority << 24; 152e03c902cSDavid Gibson ss->pending_priority = 0xff; 153500efa23SDavid Gibson 154500efa23SDavid Gibson trace_xics_icp_accept(xirr, ss->xirr); 155500efa23SDavid Gibson 156b5cec4c5SDavid Gibson return xirr; 157b5cec4c5SDavid Gibson } 158b5cec4c5SDavid Gibson 159c04d6cfaSAnthony Liguori static void icp_eoi(XICSState *icp, int server, uint32_t xirr) 160b5cec4c5SDavid Gibson { 161c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 162b5cec4c5SDavid Gibson 163b5cec4c5SDavid Gibson /* Send EOI -> ICS */ 164b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); 165500efa23SDavid Gibson trace_xics_icp_eoi(server, xirr, ss->xirr); 166d07fee7eSDavid Gibson ics_eoi(icp->ics, xirr & XISR_MASK); 167b5cec4c5SDavid Gibson if (!XISR(ss)) { 168b5cec4c5SDavid Gibson icp_resend(icp, server); 169b5cec4c5SDavid Gibson } 170b5cec4c5SDavid Gibson } 171b5cec4c5SDavid Gibson 172c04d6cfaSAnthony Liguori static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) 173b5cec4c5SDavid Gibson { 174c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 175b5cec4c5SDavid Gibson 176500efa23SDavid Gibson trace_xics_icp_irq(server, nr, priority); 177500efa23SDavid Gibson 178b5cec4c5SDavid Gibson if ((priority >= CPPR(ss)) 179b5cec4c5SDavid Gibson || (XISR(ss) && (ss->pending_priority <= priority))) { 180b5cec4c5SDavid Gibson ics_reject(icp->ics, nr); 181b5cec4c5SDavid Gibson } else { 182b5cec4c5SDavid Gibson if (XISR(ss)) { 183b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 184b5cec4c5SDavid Gibson } 185b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); 186b5cec4c5SDavid Gibson ss->pending_priority = priority; 187500efa23SDavid Gibson trace_xics_icp_raise(ss->xirr, ss->pending_priority); 188b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 189b5cec4c5SDavid Gibson } 190b5cec4c5SDavid Gibson } 191b5cec4c5SDavid Gibson 192c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_icp_server = { 193c04d6cfaSAnthony Liguori .name = "icp/server", 194c04d6cfaSAnthony Liguori .version_id = 1, 195c04d6cfaSAnthony Liguori .minimum_version_id = 1, 196c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 197c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 198c04d6cfaSAnthony Liguori /* Sanity check */ 199c04d6cfaSAnthony Liguori VMSTATE_UINT32(xirr, ICPState), 200c04d6cfaSAnthony Liguori VMSTATE_UINT8(pending_priority, ICPState), 201c04d6cfaSAnthony Liguori VMSTATE_UINT8(mfrr, ICPState), 202c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 203c04d6cfaSAnthony Liguori }, 204c04d6cfaSAnthony Liguori }; 205c04d6cfaSAnthony Liguori 206c04d6cfaSAnthony Liguori static void icp_reset(DeviceState *dev) 207c04d6cfaSAnthony Liguori { 208c04d6cfaSAnthony Liguori ICPState *icp = ICP(dev); 209c04d6cfaSAnthony Liguori 210c04d6cfaSAnthony Liguori icp->xirr = 0; 211c04d6cfaSAnthony Liguori icp->pending_priority = 0xff; 212c04d6cfaSAnthony Liguori icp->mfrr = 0xff; 213c04d6cfaSAnthony Liguori 214c04d6cfaSAnthony Liguori /* Make all outputs are deasserted */ 215c04d6cfaSAnthony Liguori qemu_set_irq(icp->output, 0); 216c04d6cfaSAnthony Liguori } 217c04d6cfaSAnthony Liguori 218c04d6cfaSAnthony Liguori static void icp_class_init(ObjectClass *klass, void *data) 219c04d6cfaSAnthony Liguori { 220c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 221c04d6cfaSAnthony Liguori 222c04d6cfaSAnthony Liguori dc->reset = icp_reset; 223c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_icp_server; 224c04d6cfaSAnthony Liguori } 225c04d6cfaSAnthony Liguori 226c04d6cfaSAnthony Liguori static TypeInfo icp_info = { 227c04d6cfaSAnthony Liguori .name = TYPE_ICP, 228c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 229c04d6cfaSAnthony Liguori .instance_size = sizeof(ICPState), 230c04d6cfaSAnthony Liguori .class_init = icp_class_init, 231c04d6cfaSAnthony Liguori }; 232c04d6cfaSAnthony Liguori 233b5cec4c5SDavid Gibson /* 234b5cec4c5SDavid Gibson * ICS: Source layer 235b5cec4c5SDavid Gibson */ 236c04d6cfaSAnthony Liguori static int ics_valid_irq(ICSState *ics, uint32_t nr) 237b5cec4c5SDavid Gibson { 238b5cec4c5SDavid Gibson return (nr >= ics->offset) 239b5cec4c5SDavid Gibson && (nr < (ics->offset + ics->nr_irqs)); 240b5cec4c5SDavid Gibson } 241b5cec4c5SDavid Gibson 242c04d6cfaSAnthony Liguori static void resend_msi(ICSState *ics, int srcno) 243b5cec4c5SDavid Gibson { 244c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 245d07fee7eSDavid Gibson 246d07fee7eSDavid Gibson /* FIXME: filter by server#? */ 24798ca8c02SDavid Gibson if (irq->status & XICS_STATUS_REJECTED) { 24898ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_REJECTED; 249d07fee7eSDavid Gibson if (irq->priority != 0xff) { 250d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, 251d07fee7eSDavid Gibson irq->priority); 252d07fee7eSDavid Gibson } 253d07fee7eSDavid Gibson } 254d07fee7eSDavid Gibson } 255d07fee7eSDavid Gibson 256c04d6cfaSAnthony Liguori static void resend_lsi(ICSState *ics, int srcno) 257d07fee7eSDavid Gibson { 258c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 259d07fee7eSDavid Gibson 26098ca8c02SDavid Gibson if ((irq->priority != 0xff) 26198ca8c02SDavid Gibson && (irq->status & XICS_STATUS_ASSERTED) 26298ca8c02SDavid Gibson && !(irq->status & XICS_STATUS_SENT)) { 26398ca8c02SDavid Gibson irq->status |= XICS_STATUS_SENT; 264d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 265d07fee7eSDavid Gibson } 266d07fee7eSDavid Gibson } 267d07fee7eSDavid Gibson 268c04d6cfaSAnthony Liguori static void set_irq_msi(ICSState *ics, int srcno, int val) 269d07fee7eSDavid Gibson { 270c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 271b5cec4c5SDavid Gibson 272500efa23SDavid Gibson trace_xics_set_irq_msi(srcno, srcno + ics->offset); 273500efa23SDavid Gibson 274b5cec4c5SDavid Gibson if (val) { 275b5cec4c5SDavid Gibson if (irq->priority == 0xff) { 27698ca8c02SDavid Gibson irq->status |= XICS_STATUS_MASKED_PENDING; 277500efa23SDavid Gibson trace_xics_masked_pending(); 278b5cec4c5SDavid Gibson } else { 279cc67b9c8SDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 280b5cec4c5SDavid Gibson } 281b5cec4c5SDavid Gibson } 282b5cec4c5SDavid Gibson } 283b5cec4c5SDavid Gibson 284c04d6cfaSAnthony Liguori static void set_irq_lsi(ICSState *ics, int srcno, int val) 285d07fee7eSDavid Gibson { 286c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 287d07fee7eSDavid Gibson 288500efa23SDavid Gibson trace_xics_set_irq_lsi(srcno, srcno + ics->offset); 28998ca8c02SDavid Gibson if (val) { 29098ca8c02SDavid Gibson irq->status |= XICS_STATUS_ASSERTED; 29198ca8c02SDavid Gibson } else { 29298ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_ASSERTED; 29398ca8c02SDavid Gibson } 294d07fee7eSDavid Gibson resend_lsi(ics, srcno); 295d07fee7eSDavid Gibson } 296d07fee7eSDavid Gibson 297d07fee7eSDavid Gibson static void ics_set_irq(void *opaque, int srcno, int val) 298d07fee7eSDavid Gibson { 299c04d6cfaSAnthony Liguori ICSState *ics = (ICSState *)opaque; 300d07fee7eSDavid Gibson 30122a2611cSDavid Gibson if (ics->islsi[srcno]) { 302d07fee7eSDavid Gibson set_irq_lsi(ics, srcno, val); 303d07fee7eSDavid Gibson } else { 304d07fee7eSDavid Gibson set_irq_msi(ics, srcno, val); 305d07fee7eSDavid Gibson } 306d07fee7eSDavid Gibson } 307d07fee7eSDavid Gibson 308c04d6cfaSAnthony Liguori static void write_xive_msi(ICSState *ics, int srcno) 309d07fee7eSDavid Gibson { 310c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 311d07fee7eSDavid Gibson 31298ca8c02SDavid Gibson if (!(irq->status & XICS_STATUS_MASKED_PENDING) 31398ca8c02SDavid Gibson || (irq->priority == 0xff)) { 314d07fee7eSDavid Gibson return; 315d07fee7eSDavid Gibson } 316d07fee7eSDavid Gibson 31798ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_MASKED_PENDING; 318d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 319d07fee7eSDavid Gibson } 320d07fee7eSDavid Gibson 321c04d6cfaSAnthony Liguori static void write_xive_lsi(ICSState *ics, int srcno) 322d07fee7eSDavid Gibson { 323d07fee7eSDavid Gibson resend_lsi(ics, srcno); 324d07fee7eSDavid Gibson } 325d07fee7eSDavid Gibson 326c04d6cfaSAnthony Liguori static void ics_write_xive(ICSState *ics, int nr, int server, 3273fe719f4SDavid Gibson uint8_t priority, uint8_t saved_priority) 328d07fee7eSDavid Gibson { 329d07fee7eSDavid Gibson int srcno = nr - ics->offset; 330c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 331d07fee7eSDavid Gibson 332d07fee7eSDavid Gibson irq->server = server; 333d07fee7eSDavid Gibson irq->priority = priority; 3343fe719f4SDavid Gibson irq->saved_priority = saved_priority; 335d07fee7eSDavid Gibson 336500efa23SDavid Gibson trace_xics_ics_write_xive(nr, srcno, server, priority); 337500efa23SDavid Gibson 33822a2611cSDavid Gibson if (ics->islsi[srcno]) { 339d07fee7eSDavid Gibson write_xive_lsi(ics, srcno); 340d07fee7eSDavid Gibson } else { 341d07fee7eSDavid Gibson write_xive_msi(ics, srcno); 342d07fee7eSDavid Gibson } 343d07fee7eSDavid Gibson } 344d07fee7eSDavid Gibson 345c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr) 346b5cec4c5SDavid Gibson { 347c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + nr - ics->offset; 348b5cec4c5SDavid Gibson 349500efa23SDavid Gibson trace_xics_ics_reject(nr, nr - ics->offset); 35098ca8c02SDavid Gibson irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ 35198ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ 352b5cec4c5SDavid Gibson } 353b5cec4c5SDavid Gibson 354c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics) 355b5cec4c5SDavid Gibson { 356b5cec4c5SDavid Gibson int i; 357b5cec4c5SDavid Gibson 358b5cec4c5SDavid Gibson for (i = 0; i < ics->nr_irqs; i++) { 359b5cec4c5SDavid Gibson /* FIXME: filter by server#? */ 36022a2611cSDavid Gibson if (ics->islsi[i]) { 361d07fee7eSDavid Gibson resend_lsi(ics, i); 362d07fee7eSDavid Gibson } else { 363d07fee7eSDavid Gibson resend_msi(ics, i); 364b5cec4c5SDavid Gibson } 365b5cec4c5SDavid Gibson } 366b5cec4c5SDavid Gibson } 367b5cec4c5SDavid Gibson 368c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr) 369b5cec4c5SDavid Gibson { 370d07fee7eSDavid Gibson int srcno = nr - ics->offset; 371c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 372d07fee7eSDavid Gibson 373500efa23SDavid Gibson trace_xics_ics_eoi(nr); 374500efa23SDavid Gibson 37522a2611cSDavid Gibson if (ics->islsi[srcno]) { 37698ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; 377d07fee7eSDavid Gibson } 378b5cec4c5SDavid Gibson } 379b5cec4c5SDavid Gibson 380c04d6cfaSAnthony Liguori static void ics_reset(DeviceState *dev) 381c04d6cfaSAnthony Liguori { 382c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 383c04d6cfaSAnthony Liguori int i; 384c04d6cfaSAnthony Liguori 385c04d6cfaSAnthony Liguori memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); 386c04d6cfaSAnthony Liguori for (i = 0; i < ics->nr_irqs; i++) { 387c04d6cfaSAnthony Liguori ics->irqs[i].priority = 0xff; 388c04d6cfaSAnthony Liguori ics->irqs[i].saved_priority = 0xff; 389c04d6cfaSAnthony Liguori } 390c04d6cfaSAnthony Liguori } 391c04d6cfaSAnthony Liguori 392c04d6cfaSAnthony Liguori static int ics_post_load(void *opaque, int version_id) 393c04d6cfaSAnthony Liguori { 394c04d6cfaSAnthony Liguori int i; 395c04d6cfaSAnthony Liguori ICSState *ics = opaque; 396c04d6cfaSAnthony Liguori 397c04d6cfaSAnthony Liguori for (i = 0; i < ics->icp->nr_servers; i++) { 398c04d6cfaSAnthony Liguori icp_resend(ics->icp, i); 399c04d6cfaSAnthony Liguori } 400c04d6cfaSAnthony Liguori 401c04d6cfaSAnthony Liguori return 0; 402c04d6cfaSAnthony Liguori } 403c04d6cfaSAnthony Liguori 404c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics_irq = { 405c04d6cfaSAnthony Liguori .name = "ics/irq", 406c04d6cfaSAnthony Liguori .version_id = 1, 407c04d6cfaSAnthony Liguori .minimum_version_id = 1, 408c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 409c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 410c04d6cfaSAnthony Liguori VMSTATE_UINT32(server, ICSIRQState), 411c04d6cfaSAnthony Liguori VMSTATE_UINT8(priority, ICSIRQState), 412c04d6cfaSAnthony Liguori VMSTATE_UINT8(saved_priority, ICSIRQState), 413c04d6cfaSAnthony Liguori VMSTATE_UINT8(status, ICSIRQState), 414c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 415c04d6cfaSAnthony Liguori }, 416c04d6cfaSAnthony Liguori }; 417c04d6cfaSAnthony Liguori 418c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics = { 419c04d6cfaSAnthony Liguori .name = "ics", 420c04d6cfaSAnthony Liguori .version_id = 1, 421c04d6cfaSAnthony Liguori .minimum_version_id = 1, 422c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 423c04d6cfaSAnthony Liguori .post_load = ics_post_load, 424c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 425c04d6cfaSAnthony Liguori /* Sanity check */ 426c04d6cfaSAnthony Liguori VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), 427c04d6cfaSAnthony Liguori 428c04d6cfaSAnthony Liguori VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, 429c04d6cfaSAnthony Liguori vmstate_ics_irq, ICSIRQState), 430c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 431c04d6cfaSAnthony Liguori }, 432c04d6cfaSAnthony Liguori }; 433c04d6cfaSAnthony Liguori 434c04d6cfaSAnthony Liguori static int ics_realize(DeviceState *dev) 435c04d6cfaSAnthony Liguori { 436c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 437c04d6cfaSAnthony Liguori 438c04d6cfaSAnthony Liguori ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); 439c04d6cfaSAnthony Liguori ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); 440c04d6cfaSAnthony Liguori ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); 441c04d6cfaSAnthony Liguori 442c04d6cfaSAnthony Liguori return 0; 443c04d6cfaSAnthony Liguori } 444c04d6cfaSAnthony Liguori 445c04d6cfaSAnthony Liguori static void ics_class_init(ObjectClass *klass, void *data) 446c04d6cfaSAnthony Liguori { 447c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 448c04d6cfaSAnthony Liguori 449c04d6cfaSAnthony Liguori dc->init = ics_realize; 450c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_ics; 451c04d6cfaSAnthony Liguori dc->reset = ics_reset; 452c04d6cfaSAnthony Liguori } 453c04d6cfaSAnthony Liguori 454c04d6cfaSAnthony Liguori static TypeInfo ics_info = { 455c04d6cfaSAnthony Liguori .name = TYPE_ICS, 456c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 457c04d6cfaSAnthony Liguori .instance_size = sizeof(ICSState), 458c04d6cfaSAnthony Liguori .class_init = ics_class_init, 459c04d6cfaSAnthony Liguori }; 460c04d6cfaSAnthony Liguori 461b5cec4c5SDavid Gibson /* 462b5cec4c5SDavid Gibson * Exported functions 463b5cec4c5SDavid Gibson */ 464b5cec4c5SDavid Gibson 465c04d6cfaSAnthony Liguori qemu_irq xics_get_qirq(XICSState *icp, int irq) 466b5cec4c5SDavid Gibson { 4671ecbbab4SDavid Gibson if (!ics_valid_irq(icp->ics, irq)) { 468b5cec4c5SDavid Gibson return NULL; 469b5cec4c5SDavid Gibson } 470b5cec4c5SDavid Gibson 471a307d594SAlexey Kardashevskiy return icp->ics->qirqs[irq - icp->ics->offset]; 472a307d594SAlexey Kardashevskiy } 473a307d594SAlexey Kardashevskiy 474c04d6cfaSAnthony Liguori void xics_set_irq_type(XICSState *icp, int irq, bool lsi) 475a307d594SAlexey Kardashevskiy { 4761ecbbab4SDavid Gibson assert(ics_valid_irq(icp->ics, irq)); 477d07fee7eSDavid Gibson 47822a2611cSDavid Gibson icp->ics->islsi[irq - icp->ics->offset] = lsi; 479b5cec4c5SDavid Gibson } 480b5cec4c5SDavid Gibson 481c04d6cfaSAnthony Liguori /* 482c04d6cfaSAnthony Liguori * Guest interfaces 483c04d6cfaSAnthony Liguori */ 484c04d6cfaSAnthony Liguori 485b13ce26dSAndreas Färber static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 486b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 487b5cec4c5SDavid Gibson { 48855e5c285SAndreas Färber CPUState *cs = CPU(cpu); 489b5cec4c5SDavid Gibson target_ulong cppr = args[0]; 490b5cec4c5SDavid Gibson 49155e5c285SAndreas Färber icp_set_cppr(spapr->icp, cs->cpu_index, cppr); 492b5cec4c5SDavid Gibson return H_SUCCESS; 493b5cec4c5SDavid Gibson } 494b5cec4c5SDavid Gibson 495b13ce26dSAndreas Färber static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 496b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 497b5cec4c5SDavid Gibson { 498b5cec4c5SDavid Gibson target_ulong server = args[0]; 499b5cec4c5SDavid Gibson target_ulong mfrr = args[1]; 500b5cec4c5SDavid Gibson 501b5cec4c5SDavid Gibson if (server >= spapr->icp->nr_servers) { 502b5cec4c5SDavid Gibson return H_PARAMETER; 503b5cec4c5SDavid Gibson } 504b5cec4c5SDavid Gibson 505b5cec4c5SDavid Gibson icp_set_mfrr(spapr->icp, server, mfrr); 506b5cec4c5SDavid Gibson return H_SUCCESS; 507b5cec4c5SDavid Gibson } 508b5cec4c5SDavid Gibson 509b13ce26dSAndreas Färber static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 510b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 511b5cec4c5SDavid Gibson { 51255e5c285SAndreas Färber CPUState *cs = CPU(cpu); 51355e5c285SAndreas Färber uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); 514b5cec4c5SDavid Gibson 515b5cec4c5SDavid Gibson args[0] = xirr; 516b5cec4c5SDavid Gibson return H_SUCCESS; 517b5cec4c5SDavid Gibson } 518b5cec4c5SDavid Gibson 519b13ce26dSAndreas Färber static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 520b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 521b5cec4c5SDavid Gibson { 52255e5c285SAndreas Färber CPUState *cs = CPU(cpu); 523b5cec4c5SDavid Gibson target_ulong xirr = args[0]; 524b5cec4c5SDavid Gibson 52555e5c285SAndreas Färber icp_eoi(spapr->icp, cs->cpu_index, xirr); 526b5cec4c5SDavid Gibson return H_SUCCESS; 527b5cec4c5SDavid Gibson } 528b5cec4c5SDavid Gibson 529210b580bSAnthony Liguori static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 530210b580bSAnthony Liguori uint32_t token, 531b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 532b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 533b5cec4c5SDavid Gibson { 534c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 535b5cec4c5SDavid Gibson uint32_t nr, server, priority; 536b5cec4c5SDavid Gibson 537b5cec4c5SDavid Gibson if ((nargs != 3) || (nret != 1)) { 538b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 539b5cec4c5SDavid Gibson return; 540b5cec4c5SDavid Gibson } 541b5cec4c5SDavid Gibson 542b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 543b5cec4c5SDavid Gibson server = rtas_ld(args, 1); 544b5cec4c5SDavid Gibson priority = rtas_ld(args, 2); 545b5cec4c5SDavid Gibson 546b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) 547b5cec4c5SDavid Gibson || (priority > 0xff)) { 548b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 549b5cec4c5SDavid Gibson return; 550b5cec4c5SDavid Gibson } 551b5cec4c5SDavid Gibson 5523fe719f4SDavid Gibson ics_write_xive(ics, nr, server, priority, priority); 553b5cec4c5SDavid Gibson 554b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 555b5cec4c5SDavid Gibson } 556b5cec4c5SDavid Gibson 557210b580bSAnthony Liguori static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 558210b580bSAnthony Liguori uint32_t token, 559b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 560b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 561b5cec4c5SDavid Gibson { 562c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 563b5cec4c5SDavid Gibson uint32_t nr; 564b5cec4c5SDavid Gibson 565b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 3)) { 566b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 567b5cec4c5SDavid Gibson return; 568b5cec4c5SDavid Gibson } 569b5cec4c5SDavid Gibson 570b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 571b5cec4c5SDavid Gibson 572b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 573b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 574b5cec4c5SDavid Gibson return; 575b5cec4c5SDavid Gibson } 576b5cec4c5SDavid Gibson 577b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 578b5cec4c5SDavid Gibson rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); 579b5cec4c5SDavid Gibson rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); 580b5cec4c5SDavid Gibson } 581b5cec4c5SDavid Gibson 582210b580bSAnthony Liguori static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, 583210b580bSAnthony Liguori uint32_t token, 584b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 585b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 586b5cec4c5SDavid Gibson { 587c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 588b5cec4c5SDavid Gibson uint32_t nr; 589b5cec4c5SDavid Gibson 590b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 591b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 592b5cec4c5SDavid Gibson return; 593b5cec4c5SDavid Gibson } 594b5cec4c5SDavid Gibson 595b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 596b5cec4c5SDavid Gibson 597b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 598b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 599b5cec4c5SDavid Gibson return; 600b5cec4c5SDavid Gibson } 601b5cec4c5SDavid Gibson 6023fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, 6033fe719f4SDavid Gibson ics->irqs[nr - ics->offset].priority); 604b5cec4c5SDavid Gibson 605b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 606b5cec4c5SDavid Gibson } 607b5cec4c5SDavid Gibson 608210b580bSAnthony Liguori static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, 609210b580bSAnthony Liguori uint32_t token, 610b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 611b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 612b5cec4c5SDavid Gibson { 613c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 614b5cec4c5SDavid Gibson uint32_t nr; 615b5cec4c5SDavid Gibson 616b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 617b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 618b5cec4c5SDavid Gibson return; 619b5cec4c5SDavid Gibson } 620b5cec4c5SDavid Gibson 621b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 622b5cec4c5SDavid Gibson 623b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 624b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 625b5cec4c5SDavid Gibson return; 626b5cec4c5SDavid Gibson } 627b5cec4c5SDavid Gibson 6283fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 6293fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority, 6303fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority); 631b5cec4c5SDavid Gibson 632b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 633b5cec4c5SDavid Gibson } 634b5cec4c5SDavid Gibson 635c04d6cfaSAnthony Liguori /* 636c04d6cfaSAnthony Liguori * XICS 637c04d6cfaSAnthony Liguori */ 638c04d6cfaSAnthony Liguori 639c04d6cfaSAnthony Liguori static void xics_realize(DeviceState *dev, Error **errp) 6407b565160SDavid Gibson { 641c04d6cfaSAnthony Liguori XICSState *icp = XICS(dev); 642c04d6cfaSAnthony Liguori ICSState *ics = icp->ics; 643c04d6cfaSAnthony Liguori int i; 6447b565160SDavid Gibson 64533a0e5d8SAlexey Kardashevskiy /* Registration of global state belongs into realize */ 64633a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,set-xive", rtas_set_xive); 64733a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,get-xive", rtas_get_xive); 64833a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,int-off", rtas_int_off); 64933a0e5d8SAlexey Kardashevskiy spapr_rtas_register("ibm,int-on", rtas_int_on); 65033a0e5d8SAlexey Kardashevskiy 65133a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_CPPR, h_cppr); 65233a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_IPI, h_ipi); 65333a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_XIRR, h_xirr); 65433a0e5d8SAlexey Kardashevskiy spapr_register_hypercall(H_EOI, h_eoi); 65533a0e5d8SAlexey Kardashevskiy 656c04d6cfaSAnthony Liguori ics->nr_irqs = icp->nr_irqs; 657bf3bc4c4SBen Herrenschmidt ics->offset = XICS_IRQ_BASE; 658b5cec4c5SDavid Gibson ics->icp = icp; 659c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(ics)); 660b5cec4c5SDavid Gibson 661c04d6cfaSAnthony Liguori icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); 662c04d6cfaSAnthony Liguori for (i = 0; i < icp->nr_servers; i++) { 663c04d6cfaSAnthony Liguori char buffer[32]; 664213f0c4fSAndreas Färber object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); 665c04d6cfaSAnthony Liguori snprintf(buffer, sizeof(buffer), "icp[%d]", i); 666c04d6cfaSAnthony Liguori object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); 667c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(&icp->ss[i])); 668c04d6cfaSAnthony Liguori } 669c04d6cfaSAnthony Liguori } 670b5cec4c5SDavid Gibson 671c04d6cfaSAnthony Liguori static void xics_initfn(Object *obj) 672c04d6cfaSAnthony Liguori { 673c04d6cfaSAnthony Liguori XICSState *xics = XICS(obj); 674c04d6cfaSAnthony Liguori 675c04d6cfaSAnthony Liguori xics->ics = ICS(object_new(TYPE_ICS)); 676c04d6cfaSAnthony Liguori object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); 677c04d6cfaSAnthony Liguori } 678c04d6cfaSAnthony Liguori 679c04d6cfaSAnthony Liguori static Property xics_properties[] = { 680c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1), 681c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1), 682c04d6cfaSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 683c04d6cfaSAnthony Liguori }; 684c04d6cfaSAnthony Liguori 685c04d6cfaSAnthony Liguori static void xics_class_init(ObjectClass *oc, void *data) 686c04d6cfaSAnthony Liguori { 687c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(oc); 688c04d6cfaSAnthony Liguori 689c04d6cfaSAnthony Liguori dc->realize = xics_realize; 690c04d6cfaSAnthony Liguori dc->props = xics_properties; 691c04d6cfaSAnthony Liguori dc->reset = xics_reset; 692b5cec4c5SDavid Gibson } 693c04d6cfaSAnthony Liguori 694c04d6cfaSAnthony Liguori static const TypeInfo xics_info = { 695c04d6cfaSAnthony Liguori .name = TYPE_XICS, 696c04d6cfaSAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 697c04d6cfaSAnthony Liguori .instance_size = sizeof(XICSState), 698c04d6cfaSAnthony Liguori .class_init = xics_class_init, 699c04d6cfaSAnthony Liguori .instance_init = xics_initfn, 700c04d6cfaSAnthony Liguori }; 701c04d6cfaSAnthony Liguori 702c04d6cfaSAnthony Liguori static void xics_register_types(void) 703c04d6cfaSAnthony Liguori { 704c04d6cfaSAnthony Liguori type_register_static(&xics_info); 705c04d6cfaSAnthony Liguori type_register_static(&ics_info); 706c04d6cfaSAnthony Liguori type_register_static(&icp_info); 707c04d6cfaSAnthony Liguori } 708c04d6cfaSAnthony Liguori 709c04d6cfaSAnthony Liguori type_init(xics_register_types) 710