11e24429eSMichael Clark /* 21e24429eSMichael Clark * SiFive PLIC (Platform Level Interrupt Controller) 31e24429eSMichael Clark * 41e24429eSMichael Clark * Copyright (c) 2017 SiFive, Inc. 51e24429eSMichael Clark * 61e24429eSMichael Clark * This provides a parameterizable interrupt controller based on SiFive's PLIC. 71e24429eSMichael Clark * 81e24429eSMichael Clark * This program is free software; you can redistribute it and/or modify it 91e24429eSMichael Clark * under the terms and conditions of the GNU General Public License, 101e24429eSMichael Clark * version 2 or later, as published by the Free Software Foundation. 111e24429eSMichael Clark * 121e24429eSMichael Clark * This program is distributed in the hope it will be useful, but WITHOUT 131e24429eSMichael Clark * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 141e24429eSMichael Clark * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 151e24429eSMichael Clark * more details. 161e24429eSMichael Clark * 171e24429eSMichael Clark * You should have received a copy of the GNU General Public License along with 181e24429eSMichael Clark * this program. If not, see <http://www.gnu.org/licenses/>. 191e24429eSMichael Clark */ 201e24429eSMichael Clark 211e24429eSMichael Clark #include "qemu/osdep.h" 223e80f690SMarkus Armbruster #include "qapi/error.h" 231e24429eSMichael Clark #include "qemu/log.h" 240b8fa32fSMarkus Armbruster #include "qemu/module.h" 251e24429eSMichael Clark #include "qemu/error-report.h" 261e24429eSMichael Clark #include "hw/sysbus.h" 274f5604c4SAlistair Francis #include "hw/pci/msi.h" 28a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 2984fcf3c1SBin Meng #include "hw/intc/sifive_plic.h" 301e24429eSMichael Clark #include "target/riscv/cpu.h" 31dbd3ec54SYifei Jiang #include "migration/vmstate.h" 32f436ecc3SAlistair Francis #include "hw/irq.h" 33ad40be27SYifei Jiang #include "sysemu/kvm.h" 341e24429eSMichael Clark 35fb926d57SAlistair Francis static bool addr_between(uint32_t addr, uint32_t base, uint32_t num) 36fb926d57SAlistair Francis { 37fb926d57SAlistair Francis return addr >= base && addr - base < num; 38fb926d57SAlistair Francis } 39fb926d57SAlistair Francis 401e24429eSMichael Clark static PLICMode char_to_mode(char c) 411e24429eSMichael Clark { 421e24429eSMichael Clark switch (c) { 431e24429eSMichael Clark case 'U': return PLICMode_U; 441e24429eSMichael Clark case 'S': return PLICMode_S; 451e24429eSMichael Clark case 'M': return PLICMode_M; 461e24429eSMichael Clark default: 471e24429eSMichael Clark error_report("plic: invalid mode '%c'", c); 481e24429eSMichael Clark exit(1); 491e24429eSMichael Clark } 501e24429eSMichael Clark } 511e24429eSMichael Clark 52d78940ecSMichael Clark static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) 531e24429eSMichael Clark { 54d73415a3SStefan Hajnoczi uint32_t old, new, cmp = qatomic_read(a); 55d78940ecSMichael Clark 56d78940ecSMichael Clark do { 57d78940ecSMichael Clark old = cmp; 58d78940ecSMichael Clark new = (old & ~mask) | (value & mask); 59d73415a3SStefan Hajnoczi cmp = qatomic_cmpxchg(a, old, new); 60d78940ecSMichael Clark } while (old != cmp); 61d78940ecSMichael Clark 62d78940ecSMichael Clark return old; 631e24429eSMichael Clark } 641e24429eSMichael Clark 65d78940ecSMichael Clark static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) 661e24429eSMichael Clark { 67d78940ecSMichael Clark atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); 681e24429eSMichael Clark } 691e24429eSMichael Clark 70d78940ecSMichael Clark static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) 711e24429eSMichael Clark { 72d78940ecSMichael Clark atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); 73d78940ecSMichael Clark } 74d78940ecSMichael Clark 7541bcc44aSAlistair Francis static uint32_t sifive_plic_claimed(SiFivePLICState *plic, uint32_t addrid) 76d78940ecSMichael Clark { 7741bcc44aSAlistair Francis uint32_t max_irq = 0; 7841bcc44aSAlistair Francis uint32_t max_prio = plic->target_priority[addrid]; 79d78940ecSMichael Clark int i, j; 80bb22d391SJim Shu int num_irq_in_word = 32; 8141bcc44aSAlistair Francis 821e24429eSMichael Clark for (i = 0; i < plic->bitfield_words; i++) { 831e24429eSMichael Clark uint32_t pending_enabled_not_claimed = 841e24429eSMichael Clark (plic->pending[i] & ~plic->claimed[i]) & 851e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + i]; 8641bcc44aSAlistair Francis 871e24429eSMichael Clark if (!pending_enabled_not_claimed) { 881e24429eSMichael Clark continue; 891e24429eSMichael Clark } 9041bcc44aSAlistair Francis 91bb22d391SJim Shu if (i == (plic->bitfield_words - 1)) { 92bb22d391SJim Shu /* 93bb22d391SJim Shu * If plic->num_sources is not multiple of 32, num-of-irq in last 94bb22d391SJim Shu * word is not 32. Compute the num-of-irq of last word to avoid 95bb22d391SJim Shu * out-of-bound access of source_priority array. 96bb22d391SJim Shu */ 97bb22d391SJim Shu num_irq_in_word = plic->num_sources - ((plic->bitfield_words - 1) << 5); 98bb22d391SJim Shu } 99bb22d391SJim Shu 100bb22d391SJim Shu for (j = 0; j < num_irq_in_word; j++) { 1011e24429eSMichael Clark int irq = (i << 5) + j; 1021e24429eSMichael Clark uint32_t prio = plic->source_priority[irq]; 1031e24429eSMichael Clark int enabled = pending_enabled_not_claimed & (1 << j); 10441bcc44aSAlistair Francis 10541bcc44aSAlistair Francis if (enabled && prio > max_prio) { 10641bcc44aSAlistair Francis max_irq = irq; 10741bcc44aSAlistair Francis max_prio = prio; 1081e24429eSMichael Clark } 1091e24429eSMichael Clark } 1101e24429eSMichael Clark } 11141bcc44aSAlistair Francis 11241bcc44aSAlistair Francis return max_irq; 1131e24429eSMichael Clark } 1141e24429eSMichael Clark 1151e24429eSMichael Clark static void sifive_plic_update(SiFivePLICState *plic) 1161e24429eSMichael Clark { 1171e24429eSMichael Clark int addrid; 1181e24429eSMichael Clark 1191e24429eSMichael Clark /* raise irq on harts where this irq is enabled */ 1201e24429eSMichael Clark for (addrid = 0; addrid < plic->num_addrs; addrid++) { 1211e24429eSMichael Clark uint32_t hartid = plic->addr_config[addrid].hartid; 1221e24429eSMichael Clark PLICMode mode = plic->addr_config[addrid].mode; 12341bcc44aSAlistair Francis bool level = !!sifive_plic_claimed(plic, addrid); 124f436ecc3SAlistair Francis 1251e24429eSMichael Clark switch (mode) { 1261e24429eSMichael Clark case PLICMode_M: 127f436ecc3SAlistair Francis qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level); 1281e24429eSMichael Clark break; 1291e24429eSMichael Clark case PLICMode_S: 130f436ecc3SAlistair Francis qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level); 1311e24429eSMichael Clark break; 1321e24429eSMichael Clark default: 1331e24429eSMichael Clark break; 1341e24429eSMichael Clark } 1351e24429eSMichael Clark } 1361e24429eSMichael Clark } 1371e24429eSMichael Clark 1381e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) 1391e24429eSMichael Clark { 1401e24429eSMichael Clark SiFivePLICState *plic = opaque; 1411e24429eSMichael Clark 142b79e1c76SAlistair Francis if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { 1435decd2c5SBin Meng uint32_t irq = (addr - plic->priority_base) >> 2; 144b79e1c76SAlistair Francis 1451e24429eSMichael Clark return plic->source_priority[irq]; 146bc92f261SBin Meng } else if (addr_between(addr, plic->pending_base, 147bc92f261SBin Meng (plic->num_sources + 31) >> 3)) { 148e41848e5SMichael Clark uint32_t word = (addr - plic->pending_base) >> 2; 149b79e1c76SAlistair Francis 1501e24429eSMichael Clark return plic->pending[word]; 151b79e1c76SAlistair Francis } else if (addr_between(addr, plic->enable_base, 152b79e1c76SAlistair Francis plic->num_addrs * plic->enable_stride)) { 1531e24429eSMichael Clark uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 1541e24429eSMichael Clark uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 155b79e1c76SAlistair Francis 1561e24429eSMichael Clark if (wordid < plic->bitfield_words) { 1571e24429eSMichael Clark return plic->enable[addrid * plic->bitfield_words + wordid]; 1581e24429eSMichael Clark } 159b79e1c76SAlistair Francis } else if (addr_between(addr, plic->context_base, 160b79e1c76SAlistair Francis plic->num_addrs * plic->context_stride)) { 1611e24429eSMichael Clark uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 1621e24429eSMichael Clark uint32_t contextid = (addr & (plic->context_stride - 1)); 163b79e1c76SAlistair Francis 1641e24429eSMichael Clark if (contextid == 0) { 1651e24429eSMichael Clark return plic->target_priority[addrid]; 1661e24429eSMichael Clark } else if (contextid == 4) { 16741bcc44aSAlistair Francis uint32_t max_irq = sifive_plic_claimed(plic, addrid); 16841bcc44aSAlistair Francis 16941bcc44aSAlistair Francis if (max_irq) { 17041bcc44aSAlistair Francis sifive_plic_set_pending(plic, max_irq, false); 17141bcc44aSAlistair Francis sifive_plic_set_claimed(plic, max_irq, true); 17241bcc44aSAlistair Francis } 173b79e1c76SAlistair Francis 17455765822SJessica Clarke sifive_plic_update(plic); 17541bcc44aSAlistair Francis return max_irq; 1761e24429eSMichael Clark } 1771e24429eSMichael Clark } 1781e24429eSMichael Clark 17979bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 18079bcac25SAlistair Francis "%s: Invalid register read 0x%" HWADDR_PRIx "\n", 18179bcac25SAlistair Francis __func__, addr); 1821e24429eSMichael Clark return 0; 1831e24429eSMichael Clark } 1841e24429eSMichael Clark 1851e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, 1861e24429eSMichael Clark unsigned size) 1871e24429eSMichael Clark { 1881e24429eSMichael Clark SiFivePLICState *plic = opaque; 1891e24429eSMichael Clark 190fb926d57SAlistair Francis if (addr_between(addr, plic->priority_base, plic->num_sources << 2)) { 1915decd2c5SBin Meng uint32_t irq = (addr - plic->priority_base) >> 2; 19241fc1f02SSergey Makarov if (irq == 0) { 19341fc1f02SSergey Makarov /* IRQ 0 source prioority is reserved */ 19441fc1f02SSergey Makarov qemu_log_mask(LOG_GUEST_ERROR, 19541fc1f02SSergey Makarov "%s: Invalid source priority write 0x%" 19641fc1f02SSergey Makarov HWADDR_PRIx "\n", __func__, addr); 19741fc1f02SSergey Makarov return; 19841fc1f02SSergey Makarov } else if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { 1991aae4a12SJim Shu /* 2001aae4a12SJim Shu * if "num_priorities + 1" is power-of-2, make each register bit of 2011aae4a12SJim Shu * interrupt priority WARL (Write-Any-Read-Legal). Just filter 2021aae4a12SJim Shu * out the access to unsupported priority bits. 2031aae4a12SJim Shu */ 2041aae4a12SJim Shu plic->source_priority[irq] = value % (plic->num_priorities + 1); 2051aae4a12SJim Shu sifive_plic_update(plic); 2061aae4a12SJim Shu } else if (value <= plic->num_priorities) { 20755144a1fSJim Shu plic->source_priority[irq] = value; 20855765822SJessica Clarke sifive_plic_update(plic); 20955144a1fSJim Shu } 210fb926d57SAlistair Francis } else if (addr_between(addr, plic->pending_base, 211bc92f261SBin Meng (plic->num_sources + 31) >> 3)) { 21279bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 21379bcac25SAlistair Francis "%s: invalid pending write: 0x%" HWADDR_PRIx "", 21479bcac25SAlistair Francis __func__, addr); 215fb926d57SAlistair Francis } else if (addr_between(addr, plic->enable_base, 216fb926d57SAlistair Francis plic->num_addrs * plic->enable_stride)) { 2171e24429eSMichael Clark uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 2181e24429eSMichael Clark uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 219fb926d57SAlistair Francis 2201e24429eSMichael Clark if (wordid < plic->bitfield_words) { 2211e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + wordid] = value; 222fb926d57SAlistair Francis } else { 223fb926d57SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 224fb926d57SAlistair Francis "%s: Invalid enable write 0x%" HWADDR_PRIx "\n", 225fb926d57SAlistair Francis __func__, addr); 2261e24429eSMichael Clark } 227fb926d57SAlistair Francis } else if (addr_between(addr, plic->context_base, 228fb926d57SAlistair Francis plic->num_addrs * plic->context_stride)) { 2291e24429eSMichael Clark uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 2301e24429eSMichael Clark uint32_t contextid = (addr & (plic->context_stride - 1)); 231fb926d57SAlistair Francis 2321e24429eSMichael Clark if (contextid == 0) { 2331aae4a12SJim Shu if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { 2341aae4a12SJim Shu /* 2351aae4a12SJim Shu * if "num_priorities + 1" is power-of-2, each register bit of 2361aae4a12SJim Shu * interrupt priority is WARL (Write-Any-Read-Legal). Just 2371aae4a12SJim Shu * filter out the access to unsupported priority bits. 2381aae4a12SJim Shu */ 2391aae4a12SJim Shu plic->target_priority[addrid] = value % 2401aae4a12SJim Shu (plic->num_priorities + 1); 2411aae4a12SJim Shu sifive_plic_update(plic); 2421aae4a12SJim Shu } else if (value <= plic->num_priorities) { 2431e24429eSMichael Clark plic->target_priority[addrid] = value; 2441e24429eSMichael Clark sifive_plic_update(plic); 2451e24429eSMichael Clark } 2461e24429eSMichael Clark } else if (contextid == 4) { 2471e24429eSMichael Clark if (value < plic->num_sources) { 2481e24429eSMichael Clark sifive_plic_set_claimed(plic, value, false); 2491e24429eSMichael Clark sifive_plic_update(plic); 2501e24429eSMichael Clark } 251fb926d57SAlistair Francis } else { 252fb926d57SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 253fb926d57SAlistair Francis "%s: Invalid context write 0x%" HWADDR_PRIx "\n", 254fb926d57SAlistair Francis __func__, addr); 2551e24429eSMichael Clark } 256fb926d57SAlistair Francis } else { 25779bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 25879bcac25SAlistair Francis "%s: Invalid register write 0x%" HWADDR_PRIx "\n", 25979bcac25SAlistair Francis __func__, addr); 2601e24429eSMichael Clark } 261fb926d57SAlistair Francis } 2621e24429eSMichael Clark 2631e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = { 2641e24429eSMichael Clark .read = sifive_plic_read, 2651e24429eSMichael Clark .write = sifive_plic_write, 2661e24429eSMichael Clark .endianness = DEVICE_LITTLE_ENDIAN, 2671e24429eSMichael Clark .valid = { 2681e24429eSMichael Clark .min_access_size = 4, 2691e24429eSMichael Clark .max_access_size = 4 2701e24429eSMichael Clark } 2711e24429eSMichael Clark }; 2721e24429eSMichael Clark 27383b92b8eSAlistair Francis static void sifive_plic_reset(DeviceState *dev) 27483b92b8eSAlistair Francis { 27583b92b8eSAlistair Francis SiFivePLICState *s = SIFIVE_PLIC(dev); 27683b92b8eSAlistair Francis int i; 27783b92b8eSAlistair Francis 27883b92b8eSAlistair Francis memset(s->source_priority, 0, sizeof(uint32_t) * s->num_sources); 27983b92b8eSAlistair Francis memset(s->target_priority, 0, sizeof(uint32_t) * s->num_addrs); 28083b92b8eSAlistair Francis memset(s->pending, 0, sizeof(uint32_t) * s->bitfield_words); 28183b92b8eSAlistair Francis memset(s->claimed, 0, sizeof(uint32_t) * s->bitfield_words); 28283b92b8eSAlistair Francis memset(s->enable, 0, sizeof(uint32_t) * s->num_enables); 28383b92b8eSAlistair Francis 28483b92b8eSAlistair Francis for (i = 0; i < s->num_harts; i++) { 28583b92b8eSAlistair Francis qemu_set_irq(s->m_external_irqs[i], 0); 28683b92b8eSAlistair Francis qemu_set_irq(s->s_external_irqs[i], 0); 28783b92b8eSAlistair Francis } 28883b92b8eSAlistair Francis } 28983b92b8eSAlistair Francis 2901e24429eSMichael Clark /* 2911e24429eSMichael Clark * parse PLIC hart/mode address offset config 2921e24429eSMichael Clark * 2931e24429eSMichael Clark * "M" 1 hart with M mode 2941e24429eSMichael Clark * "MS,MS" 2 harts, 0-1 with M and S mode 2951e24429eSMichael Clark * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode 2961e24429eSMichael Clark */ 2971e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic) 2981e24429eSMichael Clark { 2997b0f26e4SBin Meng int addrid, hartid, modes, m; 3001e24429eSMichael Clark const char *p; 3011e24429eSMichael Clark char c; 3021e24429eSMichael Clark 3031e24429eSMichael Clark /* count and validate hart/mode combinations */ 3041e24429eSMichael Clark addrid = 0, hartid = 0, modes = 0; 3051e24429eSMichael Clark p = plic->hart_config; 3061e24429eSMichael Clark while ((c = *p++)) { 3071e24429eSMichael Clark if (c == ',') { 3087b0f26e4SBin Meng if (modes) { 309244df421SMichael Clark addrid += ctpop8(modes); 3101e24429eSMichael Clark hartid++; 3117b0f26e4SBin Meng modes = 0; 3127b0f26e4SBin Meng } 3131e24429eSMichael Clark } else { 3147b0f26e4SBin Meng m = 1 << char_to_mode(c); 3151e24429eSMichael Clark if (modes == (modes | m)) { 3161e24429eSMichael Clark error_report("plic: duplicate mode '%c' in config: %s", 3171e24429eSMichael Clark c, plic->hart_config); 3181e24429eSMichael Clark exit(1); 3191e24429eSMichael Clark } 3201e24429eSMichael Clark modes |= m; 3211e24429eSMichael Clark } 3221e24429eSMichael Clark } 3231e24429eSMichael Clark if (modes) { 324244df421SMichael Clark addrid += ctpop8(modes); 3251e24429eSMichael Clark hartid++; 3267b0f26e4SBin Meng modes = 0; 3277b0f26e4SBin Meng } 3281e24429eSMichael Clark 3291e24429eSMichael Clark plic->num_addrs = addrid; 330c9270e10SAnup Patel plic->num_harts = hartid; 331c9270e10SAnup Patel 332c9270e10SAnup Patel /* store hart/mode combinations */ 3331e24429eSMichael Clark plic->addr_config = g_new(PLICAddr, plic->num_addrs); 334c9270e10SAnup Patel addrid = 0, hartid = plic->hartid_base; 3351e24429eSMichael Clark p = plic->hart_config; 3361e24429eSMichael Clark while ((c = *p++)) { 3371e24429eSMichael Clark if (c == ',') { 3387b0f26e4SBin Meng if (modes) { 3391e24429eSMichael Clark hartid++; 3407b0f26e4SBin Meng modes = 0; 3417b0f26e4SBin Meng } 3421e24429eSMichael Clark } else { 3437b0f26e4SBin Meng m = char_to_mode(c); 3441e24429eSMichael Clark plic->addr_config[addrid].addrid = addrid; 3451e24429eSMichael Clark plic->addr_config[addrid].hartid = hartid; 3467b0f26e4SBin Meng plic->addr_config[addrid].mode = m; 3477b0f26e4SBin Meng modes |= (1 << m); 3481e24429eSMichael Clark addrid++; 3491e24429eSMichael Clark } 3501e24429eSMichael Clark } 3511e24429eSMichael Clark } 3521e24429eSMichael Clark 3531e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level) 3541e24429eSMichael Clark { 3558d3dae16SAlistair Francis SiFivePLICState *s = opaque; 3568d3dae16SAlistair Francis 357a84be2baSSergey Makarov if (level > 0) { 358a84be2baSSergey Makarov sifive_plic_set_pending(s, irq, true); 3598d3dae16SAlistair Francis sifive_plic_update(s); 3601e24429eSMichael Clark } 361a84be2baSSergey Makarov } 3621e24429eSMichael Clark 3631e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp) 3641e24429eSMichael Clark { 365d680ff66SAlistair Francis SiFivePLICState *s = SIFIVE_PLIC(dev); 366e3e7039cSMichael Clark int i; 3671e24429eSMichael Clark 368d680ff66SAlistair Francis memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_plic_ops, s, 369d680ff66SAlistair Francis TYPE_SIFIVE_PLIC, s->aperture_size); 370d680ff66SAlistair Francis sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 371e3e7039cSMichael Clark 372d680ff66SAlistair Francis parse_hart_config(s); 373f436ecc3SAlistair Francis 374e8fe2bc1SBin Meng if (!s->num_sources) { 375e8fe2bc1SBin Meng error_setg(errp, "plic: invalid number of interrupt sources"); 376e8fe2bc1SBin Meng return; 377e8fe2bc1SBin Meng } 378e8fe2bc1SBin Meng 379d680ff66SAlistair Francis s->bitfield_words = (s->num_sources + 31) >> 5; 380d680ff66SAlistair Francis s->num_enables = s->bitfield_words * s->num_addrs; 381d680ff66SAlistair Francis s->source_priority = g_new0(uint32_t, s->num_sources); 382d680ff66SAlistair Francis s->target_priority = g_new(uint32_t, s->num_addrs); 383d680ff66SAlistair Francis s->pending = g_new0(uint32_t, s->bitfield_words); 384d680ff66SAlistair Francis s->claimed = g_new0(uint32_t, s->bitfield_words); 385d680ff66SAlistair Francis s->enable = g_new0(uint32_t, s->num_enables); 386d680ff66SAlistair Francis 387d680ff66SAlistair Francis qdev_init_gpio_in(dev, sifive_plic_irq_request, s->num_sources); 388d680ff66SAlistair Francis 389d680ff66SAlistair Francis s->s_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); 390d680ff66SAlistair Francis qdev_init_gpio_out(dev, s->s_external_irqs, s->num_harts); 391d680ff66SAlistair Francis 392d680ff66SAlistair Francis s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts); 393d680ff66SAlistair Francis qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts); 394f436ecc3SAlistair Francis 39535401578SBin Meng /* 39635401578SBin Meng * We can't allow the supervisor to control SEIP as this would allow the 397e3e7039cSMichael Clark * supervisor to clear a pending external interrupt which will result in 398e3e7039cSMichael Clark * lost a interrupt in the case a PLIC is attached. The SEIP bit must be 399e3e7039cSMichael Clark * hardware controlled when a PLIC is attached. 400e3e7039cSMichael Clark */ 401d680ff66SAlistair Francis for (i = 0; i < s->num_harts; i++) { 402d680ff66SAlistair Francis RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i)); 403e3e7039cSMichael Clark if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { 40435401578SBin Meng error_setg(errp, "SEIP already claimed"); 40535401578SBin Meng return; 406e3e7039cSMichael Clark } 407e3e7039cSMichael Clark } 40884bdc58cSPeter Maydell 4094f5604c4SAlistair Francis msi_nonbroken = true; 4101e24429eSMichael Clark } 4111e24429eSMichael Clark 412dbd3ec54SYifei Jiang static const VMStateDescription vmstate_sifive_plic = { 413dbd3ec54SYifei Jiang .name = "riscv_sifive_plic", 414dbd3ec54SYifei Jiang .version_id = 1, 415dbd3ec54SYifei Jiang .minimum_version_id = 1, 41645b1f81dSRichard Henderson .fields = (const VMStateField[]) { 417dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, 418dbd3ec54SYifei Jiang num_sources, 0, 419dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 420dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState, 421dbd3ec54SYifei Jiang num_addrs, 0, 422dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 423dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0, 424dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 425dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0, 426dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 427dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0, 428dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 429dbd3ec54SYifei Jiang VMSTATE_END_OF_LIST() 430dbd3ec54SYifei Jiang } 431dbd3ec54SYifei Jiang }; 432dbd3ec54SYifei Jiang 433*783e3b21SRichard Henderson static const Property sifive_plic_properties[] = { 434d8c6590fSAlistair Francis DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), 435d8c6590fSAlistair Francis DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), 436e8fe2bc1SBin Meng /* number of interrupt sources including interrupt source 0 */ 437e8fe2bc1SBin Meng DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 1), 438d8c6590fSAlistair Francis DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), 4395decd2c5SBin Meng /* interrupt priority register base starting from source 0 */ 440d8c6590fSAlistair Francis DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), 441d8c6590fSAlistair Francis DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), 442d8c6590fSAlistair Francis DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), 443d8c6590fSAlistair Francis DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), 444d8c6590fSAlistair Francis DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), 445d8c6590fSAlistair Francis DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), 446d8c6590fSAlistair Francis DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), 447d8c6590fSAlistair Francis DEFINE_PROP_END_OF_LIST(), 448d8c6590fSAlistair Francis }; 449d8c6590fSAlistair Francis 4501e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data) 4511e24429eSMichael Clark { 4521e24429eSMichael Clark DeviceClass *dc = DEVICE_CLASS(klass); 4531e24429eSMichael Clark 454e3d08143SPeter Maydell device_class_set_legacy_reset(dc, sifive_plic_reset); 4554f67d30bSMarc-André Lureau device_class_set_props(dc, sifive_plic_properties); 4561e24429eSMichael Clark dc->realize = sifive_plic_realize; 457dbd3ec54SYifei Jiang dc->vmsd = &vmstate_sifive_plic; 4581e24429eSMichael Clark } 4591e24429eSMichael Clark 4601e24429eSMichael Clark static const TypeInfo sifive_plic_info = { 4611e24429eSMichael Clark .name = TYPE_SIFIVE_PLIC, 4621e24429eSMichael Clark .parent = TYPE_SYS_BUS_DEVICE, 4631e24429eSMichael Clark .instance_size = sizeof(SiFivePLICState), 4641e24429eSMichael Clark .class_init = sifive_plic_class_init, 4651e24429eSMichael Clark }; 4661e24429eSMichael Clark 4671e24429eSMichael Clark static void sifive_plic_register_types(void) 4681e24429eSMichael Clark { 4691e24429eSMichael Clark type_register_static(&sifive_plic_info); 4701e24429eSMichael Clark } 4711e24429eSMichael Clark 4721e24429eSMichael Clark type_init(sifive_plic_register_types) 4731e24429eSMichael Clark 4741e24429eSMichael Clark /* 4751e24429eSMichael Clark * Create PLIC device. 4761e24429eSMichael Clark */ 4771e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, 478f436ecc3SAlistair Francis uint32_t num_harts, 479c9270e10SAnup Patel uint32_t hartid_base, uint32_t num_sources, 480c9270e10SAnup Patel uint32_t num_priorities, uint32_t priority_base, 481c9270e10SAnup Patel uint32_t pending_base, uint32_t enable_base, 482c9270e10SAnup Patel uint32_t enable_stride, uint32_t context_base, 483c9270e10SAnup Patel uint32_t context_stride, uint32_t aperture_size) 4841e24429eSMichael Clark { 4853e80f690SMarkus Armbruster DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC); 48640244040SAlistair Francis int i; 487ad40be27SYifei Jiang SiFivePLICState *plic; 488f436ecc3SAlistair Francis 4891e24429eSMichael Clark assert(enable_stride == (enable_stride & -enable_stride)); 4901e24429eSMichael Clark assert(context_stride == (context_stride & -context_stride)); 4911e24429eSMichael Clark qdev_prop_set_string(dev, "hart-config", hart_config); 492c9270e10SAnup Patel qdev_prop_set_uint32(dev, "hartid-base", hartid_base); 4931e24429eSMichael Clark qdev_prop_set_uint32(dev, "num-sources", num_sources); 4941e24429eSMichael Clark qdev_prop_set_uint32(dev, "num-priorities", num_priorities); 4951e24429eSMichael Clark qdev_prop_set_uint32(dev, "priority-base", priority_base); 4961e24429eSMichael Clark qdev_prop_set_uint32(dev, "pending-base", pending_base); 4971e24429eSMichael Clark qdev_prop_set_uint32(dev, "enable-base", enable_base); 4981e24429eSMichael Clark qdev_prop_set_uint32(dev, "enable-stride", enable_stride); 4991e24429eSMichael Clark qdev_prop_set_uint32(dev, "context-base", context_base); 5001e24429eSMichael Clark qdev_prop_set_uint32(dev, "context-stride", context_stride); 5011e24429eSMichael Clark qdev_prop_set_uint32(dev, "aperture-size", aperture_size); 5023c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 5031e24429eSMichael Clark sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 504f436ecc3SAlistair Francis 505ad40be27SYifei Jiang plic = SIFIVE_PLIC(dev); 506f436ecc3SAlistair Francis 50740244040SAlistair Francis for (i = 0; i < plic->num_addrs; i++) { 50840244040SAlistair Francis int cpu_num = plic->addr_config[i].hartid; 50954f21836SAtish Patra CPUState *cpu = qemu_get_cpu(cpu_num); 51040244040SAlistair Francis 51140244040SAlistair Francis if (plic->addr_config[i].mode == PLICMode_M) { 5120a9a6cbaSFrédéric Pétrot qdev_connect_gpio_out(dev, cpu_num - hartid_base + num_harts, 513f436ecc3SAlistair Francis qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); 514f436ecc3SAlistair Francis } 51540244040SAlistair Francis if (plic->addr_config[i].mode == PLICMode_S) { 5160a9a6cbaSFrédéric Pétrot qdev_connect_gpio_out(dev, cpu_num - hartid_base, 517ad40be27SYifei Jiang qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT)); 518ad40be27SYifei Jiang } 519ad40be27SYifei Jiang } 520ad40be27SYifei Jiang 5211e24429eSMichael Clark return dev; 5221e24429eSMichael Clark } 523