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 33b5cec4c5SDavid Gibson /* 34b5cec4c5SDavid Gibson * ICP: Presentation layer 35b5cec4c5SDavid Gibson */ 36b5cec4c5SDavid Gibson 37b5cec4c5SDavid Gibson #define XISR_MASK 0x00ffffff 38b5cec4c5SDavid Gibson #define CPPR_MASK 0xff000000 39b5cec4c5SDavid Gibson 40b5cec4c5SDavid Gibson #define XISR(ss) (((ss)->xirr) & XISR_MASK) 41b5cec4c5SDavid Gibson #define CPPR(ss) (((ss)->xirr) >> 24) 42b5cec4c5SDavid Gibson 43c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr); 44c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics); 45c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr); 46b5cec4c5SDavid Gibson 47c04d6cfaSAnthony Liguori static void icp_check_ipi(XICSState *icp, int server) 48b5cec4c5SDavid Gibson { 49c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 50b5cec4c5SDavid Gibson 51b5cec4c5SDavid Gibson if (XISR(ss) && (ss->pending_priority <= ss->mfrr)) { 52b5cec4c5SDavid Gibson return; 53b5cec4c5SDavid Gibson } 54b5cec4c5SDavid Gibson 55500efa23SDavid Gibson trace_xics_icp_check_ipi(server, ss->mfrr); 56500efa23SDavid Gibson 57b5cec4c5SDavid Gibson if (XISR(ss)) { 58b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 59b5cec4c5SDavid Gibson } 60b5cec4c5SDavid Gibson 61b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | XICS_IPI; 62b5cec4c5SDavid Gibson ss->pending_priority = ss->mfrr; 63b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 64b5cec4c5SDavid Gibson } 65b5cec4c5SDavid Gibson 66c04d6cfaSAnthony Liguori static void icp_resend(XICSState *icp, int server) 67b5cec4c5SDavid Gibson { 68c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 69b5cec4c5SDavid Gibson 70b5cec4c5SDavid Gibson if (ss->mfrr < CPPR(ss)) { 71b5cec4c5SDavid Gibson icp_check_ipi(icp, server); 72b5cec4c5SDavid Gibson } 73b5cec4c5SDavid Gibson ics_resend(icp->ics); 74b5cec4c5SDavid Gibson } 75b5cec4c5SDavid Gibson 76c04d6cfaSAnthony Liguori static void icp_set_cppr(XICSState *icp, int server, uint8_t cppr) 77b5cec4c5SDavid Gibson { 78c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 79b5cec4c5SDavid Gibson uint8_t old_cppr; 80b5cec4c5SDavid Gibson uint32_t old_xisr; 81b5cec4c5SDavid Gibson 82b5cec4c5SDavid Gibson old_cppr = CPPR(ss); 83b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (cppr << 24); 84b5cec4c5SDavid Gibson 85b5cec4c5SDavid Gibson if (cppr < old_cppr) { 86b5cec4c5SDavid Gibson if (XISR(ss) && (cppr <= ss->pending_priority)) { 87b5cec4c5SDavid Gibson old_xisr = XISR(ss); 88b5cec4c5SDavid Gibson ss->xirr &= ~XISR_MASK; /* Clear XISR */ 89e03c902cSDavid Gibson ss->pending_priority = 0xff; 90b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 91b5cec4c5SDavid Gibson ics_reject(icp->ics, old_xisr); 92b5cec4c5SDavid Gibson } 93b5cec4c5SDavid Gibson } else { 94b5cec4c5SDavid Gibson if (!XISR(ss)) { 95b5cec4c5SDavid Gibson icp_resend(icp, server); 96b5cec4c5SDavid Gibson } 97b5cec4c5SDavid Gibson } 98b5cec4c5SDavid Gibson } 99b5cec4c5SDavid Gibson 100c04d6cfaSAnthony Liguori static void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr) 101b5cec4c5SDavid Gibson { 102c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 103b5cec4c5SDavid Gibson 104b5cec4c5SDavid Gibson ss->mfrr = mfrr; 105b5cec4c5SDavid Gibson if (mfrr < CPPR(ss)) { 106bf0175deSDavid Gibson icp_check_ipi(icp, server); 107b5cec4c5SDavid Gibson } 108b5cec4c5SDavid Gibson } 109b5cec4c5SDavid Gibson 110c04d6cfaSAnthony Liguori static uint32_t icp_accept(ICPState *ss) 111b5cec4c5SDavid Gibson { 112500efa23SDavid Gibson uint32_t xirr = ss->xirr; 113b5cec4c5SDavid Gibson 114b5cec4c5SDavid Gibson qemu_irq_lower(ss->output); 115b5cec4c5SDavid Gibson ss->xirr = ss->pending_priority << 24; 116e03c902cSDavid Gibson ss->pending_priority = 0xff; 117500efa23SDavid Gibson 118500efa23SDavid Gibson trace_xics_icp_accept(xirr, ss->xirr); 119500efa23SDavid Gibson 120b5cec4c5SDavid Gibson return xirr; 121b5cec4c5SDavid Gibson } 122b5cec4c5SDavid Gibson 123c04d6cfaSAnthony Liguori static void icp_eoi(XICSState *icp, int server, uint32_t xirr) 124b5cec4c5SDavid Gibson { 125c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 126b5cec4c5SDavid Gibson 127b5cec4c5SDavid Gibson /* Send EOI -> ICS */ 128b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); 129500efa23SDavid Gibson trace_xics_icp_eoi(server, xirr, ss->xirr); 130d07fee7eSDavid Gibson ics_eoi(icp->ics, xirr & XISR_MASK); 131b5cec4c5SDavid Gibson if (!XISR(ss)) { 132b5cec4c5SDavid Gibson icp_resend(icp, server); 133b5cec4c5SDavid Gibson } 134b5cec4c5SDavid Gibson } 135b5cec4c5SDavid Gibson 136c04d6cfaSAnthony Liguori static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) 137b5cec4c5SDavid Gibson { 138c04d6cfaSAnthony Liguori ICPState *ss = icp->ss + server; 139b5cec4c5SDavid Gibson 140500efa23SDavid Gibson trace_xics_icp_irq(server, nr, priority); 141500efa23SDavid Gibson 142b5cec4c5SDavid Gibson if ((priority >= CPPR(ss)) 143b5cec4c5SDavid Gibson || (XISR(ss) && (ss->pending_priority <= priority))) { 144b5cec4c5SDavid Gibson ics_reject(icp->ics, nr); 145b5cec4c5SDavid Gibson } else { 146b5cec4c5SDavid Gibson if (XISR(ss)) { 147b5cec4c5SDavid Gibson ics_reject(icp->ics, XISR(ss)); 148b5cec4c5SDavid Gibson } 149b5cec4c5SDavid Gibson ss->xirr = (ss->xirr & ~XISR_MASK) | (nr & XISR_MASK); 150b5cec4c5SDavid Gibson ss->pending_priority = priority; 151500efa23SDavid Gibson trace_xics_icp_raise(ss->xirr, ss->pending_priority); 152b5cec4c5SDavid Gibson qemu_irq_raise(ss->output); 153b5cec4c5SDavid Gibson } 154b5cec4c5SDavid Gibson } 155b5cec4c5SDavid Gibson 156c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_icp_server = { 157c04d6cfaSAnthony Liguori .name = "icp/server", 158c04d6cfaSAnthony Liguori .version_id = 1, 159c04d6cfaSAnthony Liguori .minimum_version_id = 1, 160c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 161c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 162c04d6cfaSAnthony Liguori /* Sanity check */ 163c04d6cfaSAnthony Liguori VMSTATE_UINT32(xirr, ICPState), 164c04d6cfaSAnthony Liguori VMSTATE_UINT8(pending_priority, ICPState), 165c04d6cfaSAnthony Liguori VMSTATE_UINT8(mfrr, ICPState), 166c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 167c04d6cfaSAnthony Liguori }, 168c04d6cfaSAnthony Liguori }; 169c04d6cfaSAnthony Liguori 170c04d6cfaSAnthony Liguori static void icp_reset(DeviceState *dev) 171c04d6cfaSAnthony Liguori { 172c04d6cfaSAnthony Liguori ICPState *icp = ICP(dev); 173c04d6cfaSAnthony Liguori 174c04d6cfaSAnthony Liguori icp->xirr = 0; 175c04d6cfaSAnthony Liguori icp->pending_priority = 0xff; 176c04d6cfaSAnthony Liguori icp->mfrr = 0xff; 177c04d6cfaSAnthony Liguori 178c04d6cfaSAnthony Liguori /* Make all outputs are deasserted */ 179c04d6cfaSAnthony Liguori qemu_set_irq(icp->output, 0); 180c04d6cfaSAnthony Liguori } 181c04d6cfaSAnthony Liguori 182c04d6cfaSAnthony Liguori static void icp_class_init(ObjectClass *klass, void *data) 183c04d6cfaSAnthony Liguori { 184c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 185c04d6cfaSAnthony Liguori 186c04d6cfaSAnthony Liguori dc->reset = icp_reset; 187c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_icp_server; 188c04d6cfaSAnthony Liguori } 189c04d6cfaSAnthony Liguori 190c04d6cfaSAnthony Liguori static TypeInfo icp_info = { 191c04d6cfaSAnthony Liguori .name = TYPE_ICP, 192c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 193c04d6cfaSAnthony Liguori .instance_size = sizeof(ICPState), 194c04d6cfaSAnthony Liguori .class_init = icp_class_init, 195c04d6cfaSAnthony Liguori }; 196c04d6cfaSAnthony Liguori 197b5cec4c5SDavid Gibson /* 198b5cec4c5SDavid Gibson * ICS: Source layer 199b5cec4c5SDavid Gibson */ 200c04d6cfaSAnthony Liguori static int ics_valid_irq(ICSState *ics, uint32_t nr) 201b5cec4c5SDavid Gibson { 202b5cec4c5SDavid Gibson return (nr >= ics->offset) 203b5cec4c5SDavid Gibson && (nr < (ics->offset + ics->nr_irqs)); 204b5cec4c5SDavid Gibson } 205b5cec4c5SDavid Gibson 206c04d6cfaSAnthony Liguori static void resend_msi(ICSState *ics, int srcno) 207b5cec4c5SDavid Gibson { 208c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 209d07fee7eSDavid Gibson 210d07fee7eSDavid Gibson /* FIXME: filter by server#? */ 21198ca8c02SDavid Gibson if (irq->status & XICS_STATUS_REJECTED) { 21298ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_REJECTED; 213d07fee7eSDavid Gibson if (irq->priority != 0xff) { 214d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, 215d07fee7eSDavid Gibson irq->priority); 216d07fee7eSDavid Gibson } 217d07fee7eSDavid Gibson } 218d07fee7eSDavid Gibson } 219d07fee7eSDavid Gibson 220c04d6cfaSAnthony Liguori static void resend_lsi(ICSState *ics, int srcno) 221d07fee7eSDavid Gibson { 222c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 223d07fee7eSDavid Gibson 22498ca8c02SDavid Gibson if ((irq->priority != 0xff) 22598ca8c02SDavid Gibson && (irq->status & XICS_STATUS_ASSERTED) 22698ca8c02SDavid Gibson && !(irq->status & XICS_STATUS_SENT)) { 22798ca8c02SDavid Gibson irq->status |= XICS_STATUS_SENT; 228d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 229d07fee7eSDavid Gibson } 230d07fee7eSDavid Gibson } 231d07fee7eSDavid Gibson 232c04d6cfaSAnthony Liguori static void set_irq_msi(ICSState *ics, int srcno, int val) 233d07fee7eSDavid Gibson { 234c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 235b5cec4c5SDavid Gibson 236500efa23SDavid Gibson trace_xics_set_irq_msi(srcno, srcno + ics->offset); 237500efa23SDavid Gibson 238b5cec4c5SDavid Gibson if (val) { 239b5cec4c5SDavid Gibson if (irq->priority == 0xff) { 24098ca8c02SDavid Gibson irq->status |= XICS_STATUS_MASKED_PENDING; 241500efa23SDavid Gibson trace_xics_masked_pending(); 242b5cec4c5SDavid Gibson } else { 243cc67b9c8SDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 244b5cec4c5SDavid Gibson } 245b5cec4c5SDavid Gibson } 246b5cec4c5SDavid Gibson } 247b5cec4c5SDavid Gibson 248c04d6cfaSAnthony Liguori static void set_irq_lsi(ICSState *ics, int srcno, int val) 249d07fee7eSDavid Gibson { 250c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 251d07fee7eSDavid Gibson 252500efa23SDavid Gibson trace_xics_set_irq_lsi(srcno, srcno + ics->offset); 25398ca8c02SDavid Gibson if (val) { 25498ca8c02SDavid Gibson irq->status |= XICS_STATUS_ASSERTED; 25598ca8c02SDavid Gibson } else { 25698ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_ASSERTED; 25798ca8c02SDavid Gibson } 258d07fee7eSDavid Gibson resend_lsi(ics, srcno); 259d07fee7eSDavid Gibson } 260d07fee7eSDavid Gibson 261d07fee7eSDavid Gibson static void ics_set_irq(void *opaque, int srcno, int val) 262d07fee7eSDavid Gibson { 263c04d6cfaSAnthony Liguori ICSState *ics = (ICSState *)opaque; 264d07fee7eSDavid Gibson 26522a2611cSDavid Gibson if (ics->islsi[srcno]) { 266d07fee7eSDavid Gibson set_irq_lsi(ics, srcno, val); 267d07fee7eSDavid Gibson } else { 268d07fee7eSDavid Gibson set_irq_msi(ics, srcno, val); 269d07fee7eSDavid Gibson } 270d07fee7eSDavid Gibson } 271d07fee7eSDavid Gibson 272c04d6cfaSAnthony Liguori static void write_xive_msi(ICSState *ics, int srcno) 273d07fee7eSDavid Gibson { 274c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 275d07fee7eSDavid Gibson 27698ca8c02SDavid Gibson if (!(irq->status & XICS_STATUS_MASKED_PENDING) 27798ca8c02SDavid Gibson || (irq->priority == 0xff)) { 278d07fee7eSDavid Gibson return; 279d07fee7eSDavid Gibson } 280d07fee7eSDavid Gibson 28198ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_MASKED_PENDING; 282d07fee7eSDavid Gibson icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); 283d07fee7eSDavid Gibson } 284d07fee7eSDavid Gibson 285c04d6cfaSAnthony Liguori static void write_xive_lsi(ICSState *ics, int srcno) 286d07fee7eSDavid Gibson { 287d07fee7eSDavid Gibson resend_lsi(ics, srcno); 288d07fee7eSDavid Gibson } 289d07fee7eSDavid Gibson 290c04d6cfaSAnthony Liguori static void ics_write_xive(ICSState *ics, int nr, int server, 2913fe719f4SDavid Gibson uint8_t priority, uint8_t saved_priority) 292d07fee7eSDavid Gibson { 293d07fee7eSDavid Gibson int srcno = nr - ics->offset; 294c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 295d07fee7eSDavid Gibson 296d07fee7eSDavid Gibson irq->server = server; 297d07fee7eSDavid Gibson irq->priority = priority; 2983fe719f4SDavid Gibson irq->saved_priority = saved_priority; 299d07fee7eSDavid Gibson 300500efa23SDavid Gibson trace_xics_ics_write_xive(nr, srcno, server, priority); 301500efa23SDavid Gibson 30222a2611cSDavid Gibson if (ics->islsi[srcno]) { 303d07fee7eSDavid Gibson write_xive_lsi(ics, srcno); 304d07fee7eSDavid Gibson } else { 305d07fee7eSDavid Gibson write_xive_msi(ics, srcno); 306d07fee7eSDavid Gibson } 307d07fee7eSDavid Gibson } 308d07fee7eSDavid Gibson 309c04d6cfaSAnthony Liguori static void ics_reject(ICSState *ics, int nr) 310b5cec4c5SDavid Gibson { 311c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + nr - ics->offset; 312b5cec4c5SDavid Gibson 313500efa23SDavid Gibson trace_xics_ics_reject(nr, nr - ics->offset); 31498ca8c02SDavid Gibson irq->status |= XICS_STATUS_REJECTED; /* Irrelevant but harmless for LSI */ 31598ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; /* Irrelevant but harmless for MSI */ 316b5cec4c5SDavid Gibson } 317b5cec4c5SDavid Gibson 318c04d6cfaSAnthony Liguori static void ics_resend(ICSState *ics) 319b5cec4c5SDavid Gibson { 320b5cec4c5SDavid Gibson int i; 321b5cec4c5SDavid Gibson 322b5cec4c5SDavid Gibson for (i = 0; i < ics->nr_irqs; i++) { 323b5cec4c5SDavid Gibson /* FIXME: filter by server#? */ 32422a2611cSDavid Gibson if (ics->islsi[i]) { 325d07fee7eSDavid Gibson resend_lsi(ics, i); 326d07fee7eSDavid Gibson } else { 327d07fee7eSDavid Gibson resend_msi(ics, i); 328b5cec4c5SDavid Gibson } 329b5cec4c5SDavid Gibson } 330b5cec4c5SDavid Gibson } 331b5cec4c5SDavid Gibson 332c04d6cfaSAnthony Liguori static void ics_eoi(ICSState *ics, int nr) 333b5cec4c5SDavid Gibson { 334d07fee7eSDavid Gibson int srcno = nr - ics->offset; 335c04d6cfaSAnthony Liguori ICSIRQState *irq = ics->irqs + srcno; 336d07fee7eSDavid Gibson 337500efa23SDavid Gibson trace_xics_ics_eoi(nr); 338500efa23SDavid Gibson 33922a2611cSDavid Gibson if (ics->islsi[srcno]) { 34098ca8c02SDavid Gibson irq->status &= ~XICS_STATUS_SENT; 341d07fee7eSDavid Gibson } 342b5cec4c5SDavid Gibson } 343b5cec4c5SDavid Gibson 344c04d6cfaSAnthony Liguori static void ics_reset(DeviceState *dev) 345c04d6cfaSAnthony Liguori { 346c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 347c04d6cfaSAnthony Liguori int i; 348c04d6cfaSAnthony Liguori 349c04d6cfaSAnthony Liguori memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); 350c04d6cfaSAnthony Liguori for (i = 0; i < ics->nr_irqs; i++) { 351c04d6cfaSAnthony Liguori ics->irqs[i].priority = 0xff; 352c04d6cfaSAnthony Liguori ics->irqs[i].saved_priority = 0xff; 353c04d6cfaSAnthony Liguori } 354c04d6cfaSAnthony Liguori } 355c04d6cfaSAnthony Liguori 356c04d6cfaSAnthony Liguori static int ics_post_load(void *opaque, int version_id) 357c04d6cfaSAnthony Liguori { 358c04d6cfaSAnthony Liguori int i; 359c04d6cfaSAnthony Liguori ICSState *ics = opaque; 360c04d6cfaSAnthony Liguori 361c04d6cfaSAnthony Liguori for (i = 0; i < ics->icp->nr_servers; i++) { 362c04d6cfaSAnthony Liguori icp_resend(ics->icp, i); 363c04d6cfaSAnthony Liguori } 364c04d6cfaSAnthony Liguori 365c04d6cfaSAnthony Liguori return 0; 366c04d6cfaSAnthony Liguori } 367c04d6cfaSAnthony Liguori 368c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics_irq = { 369c04d6cfaSAnthony Liguori .name = "ics/irq", 370c04d6cfaSAnthony Liguori .version_id = 1, 371c04d6cfaSAnthony Liguori .minimum_version_id = 1, 372c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 373c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 374c04d6cfaSAnthony Liguori VMSTATE_UINT32(server, ICSIRQState), 375c04d6cfaSAnthony Liguori VMSTATE_UINT8(priority, ICSIRQState), 376c04d6cfaSAnthony Liguori VMSTATE_UINT8(saved_priority, ICSIRQState), 377c04d6cfaSAnthony Liguori VMSTATE_UINT8(status, ICSIRQState), 378c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 379c04d6cfaSAnthony Liguori }, 380c04d6cfaSAnthony Liguori }; 381c04d6cfaSAnthony Liguori 382c04d6cfaSAnthony Liguori static const VMStateDescription vmstate_ics = { 383c04d6cfaSAnthony Liguori .name = "ics", 384c04d6cfaSAnthony Liguori .version_id = 1, 385c04d6cfaSAnthony Liguori .minimum_version_id = 1, 386c04d6cfaSAnthony Liguori .minimum_version_id_old = 1, 387c04d6cfaSAnthony Liguori .post_load = ics_post_load, 388c04d6cfaSAnthony Liguori .fields = (VMStateField []) { 389c04d6cfaSAnthony Liguori /* Sanity check */ 390c04d6cfaSAnthony Liguori VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), 391c04d6cfaSAnthony Liguori 392c04d6cfaSAnthony Liguori VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, 393c04d6cfaSAnthony Liguori vmstate_ics_irq, ICSIRQState), 394c04d6cfaSAnthony Liguori VMSTATE_END_OF_LIST() 395c04d6cfaSAnthony Liguori }, 396c04d6cfaSAnthony Liguori }; 397c04d6cfaSAnthony Liguori 398c04d6cfaSAnthony Liguori static int ics_realize(DeviceState *dev) 399c04d6cfaSAnthony Liguori { 400c04d6cfaSAnthony Liguori ICSState *ics = ICS(dev); 401c04d6cfaSAnthony Liguori 402c04d6cfaSAnthony Liguori ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); 403c04d6cfaSAnthony Liguori ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); 404c04d6cfaSAnthony Liguori ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); 405c04d6cfaSAnthony Liguori 406c04d6cfaSAnthony Liguori return 0; 407c04d6cfaSAnthony Liguori } 408c04d6cfaSAnthony Liguori 409c04d6cfaSAnthony Liguori static void ics_class_init(ObjectClass *klass, void *data) 410c04d6cfaSAnthony Liguori { 411c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 412c04d6cfaSAnthony Liguori 413c04d6cfaSAnthony Liguori dc->init = ics_realize; 414c04d6cfaSAnthony Liguori dc->vmsd = &vmstate_ics; 415c04d6cfaSAnthony Liguori dc->reset = ics_reset; 416c04d6cfaSAnthony Liguori } 417c04d6cfaSAnthony Liguori 418c04d6cfaSAnthony Liguori static TypeInfo ics_info = { 419c04d6cfaSAnthony Liguori .name = TYPE_ICS, 420c04d6cfaSAnthony Liguori .parent = TYPE_DEVICE, 421c04d6cfaSAnthony Liguori .instance_size = sizeof(ICSState), 422c04d6cfaSAnthony Liguori .class_init = ics_class_init, 423c04d6cfaSAnthony Liguori }; 424c04d6cfaSAnthony Liguori 425b5cec4c5SDavid Gibson /* 426b5cec4c5SDavid Gibson * Exported functions 427b5cec4c5SDavid Gibson */ 428b5cec4c5SDavid Gibson 429c04d6cfaSAnthony Liguori qemu_irq xics_get_qirq(XICSState *icp, int irq) 430b5cec4c5SDavid Gibson { 4311ecbbab4SDavid Gibson if (!ics_valid_irq(icp->ics, irq)) { 432b5cec4c5SDavid Gibson return NULL; 433b5cec4c5SDavid Gibson } 434b5cec4c5SDavid Gibson 435a307d594SAlexey Kardashevskiy return icp->ics->qirqs[irq - icp->ics->offset]; 436a307d594SAlexey Kardashevskiy } 437a307d594SAlexey Kardashevskiy 438c04d6cfaSAnthony Liguori void xics_set_irq_type(XICSState *icp, int irq, bool lsi) 439a307d594SAlexey Kardashevskiy { 4401ecbbab4SDavid Gibson assert(ics_valid_irq(icp->ics, irq)); 441d07fee7eSDavid Gibson 44222a2611cSDavid Gibson icp->ics->islsi[irq - icp->ics->offset] = lsi; 443b5cec4c5SDavid Gibson } 444b5cec4c5SDavid Gibson 445c04d6cfaSAnthony Liguori /* 446c04d6cfaSAnthony Liguori * Guest interfaces 447c04d6cfaSAnthony Liguori */ 448c04d6cfaSAnthony Liguori 449b13ce26dSAndreas Färber static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 450b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 451b5cec4c5SDavid Gibson { 45255e5c285SAndreas Färber CPUState *cs = CPU(cpu); 453b5cec4c5SDavid Gibson target_ulong cppr = args[0]; 454b5cec4c5SDavid Gibson 45555e5c285SAndreas Färber icp_set_cppr(spapr->icp, cs->cpu_index, cppr); 456b5cec4c5SDavid Gibson return H_SUCCESS; 457b5cec4c5SDavid Gibson } 458b5cec4c5SDavid Gibson 459b13ce26dSAndreas Färber static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 460b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 461b5cec4c5SDavid Gibson { 462b5cec4c5SDavid Gibson target_ulong server = args[0]; 463b5cec4c5SDavid Gibson target_ulong mfrr = args[1]; 464b5cec4c5SDavid Gibson 465b5cec4c5SDavid Gibson if (server >= spapr->icp->nr_servers) { 466b5cec4c5SDavid Gibson return H_PARAMETER; 467b5cec4c5SDavid Gibson } 468b5cec4c5SDavid Gibson 469b5cec4c5SDavid Gibson icp_set_mfrr(spapr->icp, server, mfrr); 470b5cec4c5SDavid Gibson return H_SUCCESS; 471b5cec4c5SDavid Gibson } 472b5cec4c5SDavid Gibson 473b13ce26dSAndreas Färber static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, 474b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 475b5cec4c5SDavid Gibson { 47655e5c285SAndreas Färber CPUState *cs = CPU(cpu); 47755e5c285SAndreas Färber uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); 478b5cec4c5SDavid Gibson 479b5cec4c5SDavid Gibson args[0] = xirr; 480b5cec4c5SDavid Gibson return H_SUCCESS; 481b5cec4c5SDavid Gibson } 482b5cec4c5SDavid Gibson 483b13ce26dSAndreas Färber static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, 484b5cec4c5SDavid Gibson target_ulong opcode, target_ulong *args) 485b5cec4c5SDavid Gibson { 48655e5c285SAndreas Färber CPUState *cs = CPU(cpu); 487b5cec4c5SDavid Gibson target_ulong xirr = args[0]; 488b5cec4c5SDavid Gibson 48955e5c285SAndreas Färber icp_eoi(spapr->icp, cs->cpu_index, xirr); 490b5cec4c5SDavid Gibson return H_SUCCESS; 491b5cec4c5SDavid Gibson } 492b5cec4c5SDavid Gibson 493210b580bSAnthony Liguori static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 494210b580bSAnthony Liguori uint32_t token, 495b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 496b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 497b5cec4c5SDavid Gibson { 498c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 499b5cec4c5SDavid Gibson uint32_t nr, server, priority; 500b5cec4c5SDavid Gibson 501b5cec4c5SDavid Gibson if ((nargs != 3) || (nret != 1)) { 502b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 503b5cec4c5SDavid Gibson return; 504b5cec4c5SDavid Gibson } 505b5cec4c5SDavid Gibson 506b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 507b5cec4c5SDavid Gibson server = rtas_ld(args, 1); 508b5cec4c5SDavid Gibson priority = rtas_ld(args, 2); 509b5cec4c5SDavid Gibson 510b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) 511b5cec4c5SDavid Gibson || (priority > 0xff)) { 512b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 513b5cec4c5SDavid Gibson return; 514b5cec4c5SDavid Gibson } 515b5cec4c5SDavid Gibson 5163fe719f4SDavid Gibson ics_write_xive(ics, nr, server, priority, priority); 517b5cec4c5SDavid Gibson 518b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 519b5cec4c5SDavid Gibson } 520b5cec4c5SDavid Gibson 521210b580bSAnthony Liguori static void rtas_get_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, 522210b580bSAnthony Liguori uint32_t token, 523b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 524b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 525b5cec4c5SDavid Gibson { 526c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 527b5cec4c5SDavid Gibson uint32_t nr; 528b5cec4c5SDavid Gibson 529b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 3)) { 530b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 531b5cec4c5SDavid Gibson return; 532b5cec4c5SDavid Gibson } 533b5cec4c5SDavid Gibson 534b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 535b5cec4c5SDavid Gibson 536b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 537b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 538b5cec4c5SDavid Gibson return; 539b5cec4c5SDavid Gibson } 540b5cec4c5SDavid Gibson 541b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 542b5cec4c5SDavid Gibson rtas_st(rets, 1, ics->irqs[nr - ics->offset].server); 543b5cec4c5SDavid Gibson rtas_st(rets, 2, ics->irqs[nr - ics->offset].priority); 544b5cec4c5SDavid Gibson } 545b5cec4c5SDavid Gibson 546210b580bSAnthony Liguori static void rtas_int_off(PowerPCCPU *cpu, sPAPREnvironment *spapr, 547210b580bSAnthony Liguori uint32_t token, 548b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 549b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 550b5cec4c5SDavid Gibson { 551c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 552b5cec4c5SDavid Gibson uint32_t nr; 553b5cec4c5SDavid Gibson 554b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 555b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 556b5cec4c5SDavid Gibson return; 557b5cec4c5SDavid Gibson } 558b5cec4c5SDavid Gibson 559b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 560b5cec4c5SDavid Gibson 561b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 562b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 563b5cec4c5SDavid Gibson return; 564b5cec4c5SDavid Gibson } 565b5cec4c5SDavid Gibson 5663fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 0xff, 5673fe719f4SDavid Gibson ics->irqs[nr - ics->offset].priority); 568b5cec4c5SDavid Gibson 569b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 570b5cec4c5SDavid Gibson } 571b5cec4c5SDavid Gibson 572210b580bSAnthony Liguori static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, 573210b580bSAnthony Liguori uint32_t token, 574b5cec4c5SDavid Gibson uint32_t nargs, target_ulong args, 575b5cec4c5SDavid Gibson uint32_t nret, target_ulong rets) 576b5cec4c5SDavid Gibson { 577c04d6cfaSAnthony Liguori ICSState *ics = spapr->icp->ics; 578b5cec4c5SDavid Gibson uint32_t nr; 579b5cec4c5SDavid Gibson 580b5cec4c5SDavid Gibson if ((nargs != 1) || (nret != 1)) { 581b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 582b5cec4c5SDavid Gibson return; 583b5cec4c5SDavid Gibson } 584b5cec4c5SDavid Gibson 585b5cec4c5SDavid Gibson nr = rtas_ld(args, 0); 586b5cec4c5SDavid Gibson 587b5cec4c5SDavid Gibson if (!ics_valid_irq(ics, nr)) { 588b5cec4c5SDavid Gibson rtas_st(rets, 0, -3); 589b5cec4c5SDavid Gibson return; 590b5cec4c5SDavid Gibson } 591b5cec4c5SDavid Gibson 5923fe719f4SDavid Gibson ics_write_xive(ics, nr, ics->irqs[nr - ics->offset].server, 5933fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority, 5943fe719f4SDavid Gibson ics->irqs[nr - ics->offset].saved_priority); 595b5cec4c5SDavid Gibson 596b5cec4c5SDavid Gibson rtas_st(rets, 0, 0); /* Success */ 597b5cec4c5SDavid Gibson } 598b5cec4c5SDavid Gibson 599c04d6cfaSAnthony Liguori /* 600c04d6cfaSAnthony Liguori * XICS 601c04d6cfaSAnthony Liguori */ 602c04d6cfaSAnthony Liguori 603c04d6cfaSAnthony Liguori static void xics_reset(DeviceState *d) 604256b408aSDavid Gibson { 605c04d6cfaSAnthony Liguori XICSState *icp = XICS(d); 606256b408aSDavid Gibson int i; 607256b408aSDavid Gibson 608256b408aSDavid Gibson for (i = 0; i < icp->nr_servers; i++) { 609c04d6cfaSAnthony Liguori device_reset(DEVICE(&icp->ss[i])); 610256b408aSDavid Gibson } 611256b408aSDavid Gibson 612c04d6cfaSAnthony Liguori device_reset(DEVICE(icp->ics)); 613256b408aSDavid Gibson } 614256b408aSDavid Gibson 615c04d6cfaSAnthony Liguori void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) 616b5cec4c5SDavid Gibson { 6177b565160SDavid Gibson CPUState *cs = CPU(cpu); 6187b565160SDavid Gibson CPUPPCState *env = &cpu->env; 619c04d6cfaSAnthony Liguori ICPState *ss = &icp->ss[cs->cpu_index]; 620b5cec4c5SDavid Gibson 6217b565160SDavid Gibson assert(cs->cpu_index < icp->nr_servers); 622c7a5c0c9SDavid Gibson 623c7a5c0c9SDavid Gibson switch (PPC_INPUT(env)) { 624b5cec4c5SDavid Gibson case PPC_FLAGS_INPUT_POWER7: 625c7a5c0c9SDavid Gibson ss->output = env->irq_inputs[POWER7_INPUT_INT]; 626b5cec4c5SDavid Gibson break; 627b5cec4c5SDavid Gibson 628b5cec4c5SDavid Gibson case PPC_FLAGS_INPUT_970: 629c7a5c0c9SDavid Gibson ss->output = env->irq_inputs[PPC970_INPUT_INT]; 630b5cec4c5SDavid Gibson break; 631b5cec4c5SDavid Gibson 632b5cec4c5SDavid Gibson default: 6337b565160SDavid Gibson fprintf(stderr, "XICS interrupt controller does not support this CPU " 6347b565160SDavid Gibson "bus model\n"); 6357b565160SDavid Gibson abort(); 636b5cec4c5SDavid Gibson } 637b5cec4c5SDavid Gibson } 638b5cec4c5SDavid Gibson 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 645c04d6cfaSAnthony Liguori ics->nr_irqs = icp->nr_irqs; 646bf3bc4c4SBen Herrenschmidt ics->offset = XICS_IRQ_BASE; 647b5cec4c5SDavid Gibson ics->icp = icp; 648c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(ics)); 649b5cec4c5SDavid Gibson 650c04d6cfaSAnthony Liguori icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); 651c04d6cfaSAnthony Liguori for (i = 0; i < icp->nr_servers; i++) { 652c04d6cfaSAnthony Liguori char buffer[32]; 653*213f0c4fSAndreas Färber object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); 654c04d6cfaSAnthony Liguori snprintf(buffer, sizeof(buffer), "icp[%d]", i); 655c04d6cfaSAnthony Liguori object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); 656c04d6cfaSAnthony Liguori qdev_init_nofail(DEVICE(&icp->ss[i])); 657c04d6cfaSAnthony Liguori } 658c04d6cfaSAnthony Liguori } 659b5cec4c5SDavid Gibson 660c04d6cfaSAnthony Liguori static void xics_initfn(Object *obj) 661c04d6cfaSAnthony Liguori { 662c04d6cfaSAnthony Liguori XICSState *xics = XICS(obj); 663c04d6cfaSAnthony Liguori 664c04d6cfaSAnthony Liguori xics->ics = ICS(object_new(TYPE_ICS)); 665c04d6cfaSAnthony Liguori object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); 666c04d6cfaSAnthony Liguori } 667c04d6cfaSAnthony Liguori 668c04d6cfaSAnthony Liguori static Property xics_properties[] = { 669c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1), 670c04d6cfaSAnthony Liguori DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1), 671c04d6cfaSAnthony Liguori DEFINE_PROP_END_OF_LIST(), 672c04d6cfaSAnthony Liguori }; 673c04d6cfaSAnthony Liguori 674c04d6cfaSAnthony Liguori static void xics_class_init(ObjectClass *oc, void *data) 675c04d6cfaSAnthony Liguori { 676c04d6cfaSAnthony Liguori DeviceClass *dc = DEVICE_CLASS(oc); 677c04d6cfaSAnthony Liguori 678c04d6cfaSAnthony Liguori dc->realize = xics_realize; 679c04d6cfaSAnthony Liguori dc->props = xics_properties; 680c04d6cfaSAnthony Liguori dc->reset = xics_reset; 681b5cec4c5SDavid Gibson 682b5cec4c5SDavid Gibson spapr_rtas_register("ibm,set-xive", rtas_set_xive); 683b5cec4c5SDavid Gibson spapr_rtas_register("ibm,get-xive", rtas_get_xive); 684b5cec4c5SDavid Gibson spapr_rtas_register("ibm,int-off", rtas_int_off); 685b5cec4c5SDavid Gibson spapr_rtas_register("ibm,int-on", rtas_int_on); 686b5cec4c5SDavid Gibson 687c04d6cfaSAnthony Liguori spapr_register_hypercall(H_CPPR, h_cppr); 688c04d6cfaSAnthony Liguori spapr_register_hypercall(H_IPI, h_ipi); 689c04d6cfaSAnthony Liguori spapr_register_hypercall(H_XIRR, h_xirr); 690c04d6cfaSAnthony Liguori spapr_register_hypercall(H_EOI, h_eoi); 691b5cec4c5SDavid Gibson } 692c04d6cfaSAnthony Liguori 693c04d6cfaSAnthony Liguori static const TypeInfo xics_info = { 694c04d6cfaSAnthony Liguori .name = TYPE_XICS, 695c04d6cfaSAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 696c04d6cfaSAnthony Liguori .instance_size = sizeof(XICSState), 697c04d6cfaSAnthony Liguori .class_init = xics_class_init, 698c04d6cfaSAnthony Liguori .instance_init = xics_initfn, 699c04d6cfaSAnthony Liguori }; 700c04d6cfaSAnthony Liguori 701c04d6cfaSAnthony Liguori static void xics_register_types(void) 702c04d6cfaSAnthony Liguori { 703c04d6cfaSAnthony Liguori type_register_static(&xics_info); 704c04d6cfaSAnthony Liguori type_register_static(&ics_info); 705c04d6cfaSAnthony Liguori type_register_static(&icp_info); 706c04d6cfaSAnthony Liguori } 707c04d6cfaSAnthony Liguori 708c04d6cfaSAnthony Liguori type_init(xics_register_types) 709