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" 331e24429eSMichael Clark 341e24429eSMichael Clark #define RISCV_DEBUG_PLIC 0 351e24429eSMichael Clark 361e24429eSMichael Clark static PLICMode char_to_mode(char c) 371e24429eSMichael Clark { 381e24429eSMichael Clark switch (c) { 391e24429eSMichael Clark case 'U': return PLICMode_U; 401e24429eSMichael Clark case 'S': return PLICMode_S; 411e24429eSMichael Clark case 'H': return PLICMode_H; 421e24429eSMichael Clark case 'M': return PLICMode_M; 431e24429eSMichael Clark default: 441e24429eSMichael Clark error_report("plic: invalid mode '%c'", c); 451e24429eSMichael Clark exit(1); 461e24429eSMichael Clark } 471e24429eSMichael Clark } 481e24429eSMichael Clark 491e24429eSMichael Clark static char mode_to_char(PLICMode m) 501e24429eSMichael Clark { 511e24429eSMichael Clark switch (m) { 521e24429eSMichael Clark case PLICMode_U: return 'U'; 531e24429eSMichael Clark case PLICMode_S: return 'S'; 541e24429eSMichael Clark case PLICMode_H: return 'H'; 551e24429eSMichael Clark case PLICMode_M: return 'M'; 561e24429eSMichael Clark default: return '?'; 571e24429eSMichael Clark } 581e24429eSMichael Clark } 591e24429eSMichael Clark 601e24429eSMichael Clark static void sifive_plic_print_state(SiFivePLICState *plic) 611e24429eSMichael Clark { 621e24429eSMichael Clark int i; 631e24429eSMichael Clark int addrid; 641e24429eSMichael Clark 651e24429eSMichael Clark /* pending */ 661e24429eSMichael Clark qemu_log("pending : "); 671e24429eSMichael Clark for (i = plic->bitfield_words - 1; i >= 0; i--) { 681e24429eSMichael Clark qemu_log("%08x", plic->pending[i]); 691e24429eSMichael Clark } 701e24429eSMichael Clark qemu_log("\n"); 711e24429eSMichael Clark 721e24429eSMichael Clark /* pending */ 731e24429eSMichael Clark qemu_log("claimed : "); 741e24429eSMichael Clark for (i = plic->bitfield_words - 1; i >= 0; i--) { 751e24429eSMichael Clark qemu_log("%08x", plic->claimed[i]); 761e24429eSMichael Clark } 771e24429eSMichael Clark qemu_log("\n"); 781e24429eSMichael Clark 791e24429eSMichael Clark for (addrid = 0; addrid < plic->num_addrs; addrid++) { 801e24429eSMichael Clark qemu_log("hart%d-%c enable: ", 811e24429eSMichael Clark plic->addr_config[addrid].hartid, 821e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode)); 831e24429eSMichael Clark for (i = plic->bitfield_words - 1; i >= 0; i--) { 841e24429eSMichael Clark qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]); 851e24429eSMichael Clark } 861e24429eSMichael Clark qemu_log("\n"); 871e24429eSMichael Clark } 881e24429eSMichael Clark } 891e24429eSMichael Clark 90d78940ecSMichael Clark static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) 911e24429eSMichael Clark { 92d73415a3SStefan Hajnoczi uint32_t old, new, cmp = qatomic_read(a); 93d78940ecSMichael Clark 94d78940ecSMichael Clark do { 95d78940ecSMichael Clark old = cmp; 96d78940ecSMichael Clark new = (old & ~mask) | (value & mask); 97d73415a3SStefan Hajnoczi cmp = qatomic_cmpxchg(a, old, new); 98d78940ecSMichael Clark } while (old != cmp); 99d78940ecSMichael Clark 100d78940ecSMichael Clark return old; 1011e24429eSMichael Clark } 1021e24429eSMichael Clark 103d78940ecSMichael Clark static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) 1041e24429eSMichael Clark { 105d78940ecSMichael Clark atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); 1061e24429eSMichael Clark } 1071e24429eSMichael Clark 108d78940ecSMichael Clark static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) 1091e24429eSMichael Clark { 110d78940ecSMichael Clark atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); 111d78940ecSMichael Clark } 112d78940ecSMichael Clark 113d78940ecSMichael Clark static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid) 114d78940ecSMichael Clark { 115d78940ecSMichael Clark int i, j; 1161e24429eSMichael Clark for (i = 0; i < plic->bitfield_words; i++) { 1171e24429eSMichael Clark uint32_t pending_enabled_not_claimed = 1181e24429eSMichael Clark (plic->pending[i] & ~plic->claimed[i]) & 1191e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + i]; 1201e24429eSMichael Clark if (!pending_enabled_not_claimed) { 1211e24429eSMichael Clark continue; 1221e24429eSMichael Clark } 1231e24429eSMichael Clark for (j = 0; j < 32; j++) { 1241e24429eSMichael Clark int irq = (i << 5) + j; 1251e24429eSMichael Clark uint32_t prio = plic->source_priority[irq]; 1261e24429eSMichael Clark int enabled = pending_enabled_not_claimed & (1 << j); 1271e24429eSMichael Clark if (enabled && prio > plic->target_priority[addrid]) { 128d78940ecSMichael Clark return 1; 1291e24429eSMichael Clark } 1301e24429eSMichael Clark } 1311e24429eSMichael Clark } 132d78940ecSMichael Clark return 0; 1331e24429eSMichael Clark } 1341e24429eSMichael Clark 1351e24429eSMichael Clark static void sifive_plic_update(SiFivePLICState *plic) 1361e24429eSMichael Clark { 1371e24429eSMichael Clark int addrid; 1381e24429eSMichael Clark 1391e24429eSMichael Clark /* raise irq on harts where this irq is enabled */ 1401e24429eSMichael Clark for (addrid = 0; addrid < plic->num_addrs; addrid++) { 1411e24429eSMichael Clark uint32_t hartid = plic->addr_config[addrid].hartid; 1421e24429eSMichael Clark PLICMode mode = plic->addr_config[addrid].mode; 143d78940ecSMichael Clark int level = sifive_plic_irqs_pending(plic, addrid); 144f436ecc3SAlistair Francis 1451e24429eSMichael Clark switch (mode) { 1461e24429eSMichael Clark case PLICMode_M: 147f436ecc3SAlistair Francis qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level); 1481e24429eSMichael Clark break; 1491e24429eSMichael Clark case PLICMode_S: 150f436ecc3SAlistair Francis qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level); 1511e24429eSMichael Clark break; 1521e24429eSMichael Clark default: 1531e24429eSMichael Clark break; 1541e24429eSMichael Clark } 1551e24429eSMichael Clark } 1561e24429eSMichael Clark 1571e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 1581e24429eSMichael Clark sifive_plic_print_state(plic); 1591e24429eSMichael Clark } 1601e24429eSMichael Clark } 1611e24429eSMichael Clark 1621e24429eSMichael Clark static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) 1631e24429eSMichael Clark { 1641e24429eSMichael Clark int i, j; 165aa4d30f6SJessica Clarke uint32_t max_irq = 0; 166aa4d30f6SJessica Clarke uint32_t max_prio = plic->target_priority[addrid]; 167aa4d30f6SJessica Clarke 1681e24429eSMichael Clark for (i = 0; i < plic->bitfield_words; i++) { 1691e24429eSMichael Clark uint32_t pending_enabled_not_claimed = 1701e24429eSMichael Clark (plic->pending[i] & ~plic->claimed[i]) & 1711e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + i]; 1721e24429eSMichael Clark if (!pending_enabled_not_claimed) { 1731e24429eSMichael Clark continue; 1741e24429eSMichael Clark } 1751e24429eSMichael Clark for (j = 0; j < 32; j++) { 1761e24429eSMichael Clark int irq = (i << 5) + j; 1771e24429eSMichael Clark uint32_t prio = plic->source_priority[irq]; 1781e24429eSMichael Clark int enabled = pending_enabled_not_claimed & (1 << j); 179aa4d30f6SJessica Clarke if (enabled && prio > max_prio) { 180aa4d30f6SJessica Clarke max_irq = irq; 181aa4d30f6SJessica Clarke max_prio = prio; 1821e24429eSMichael Clark } 1831e24429eSMichael Clark } 1841e24429eSMichael Clark } 185aa4d30f6SJessica Clarke 186aa4d30f6SJessica Clarke if (max_irq) { 187aa4d30f6SJessica Clarke sifive_plic_set_pending(plic, max_irq, false); 188aa4d30f6SJessica Clarke sifive_plic_set_claimed(plic, max_irq, true); 189aa4d30f6SJessica Clarke } 190aa4d30f6SJessica Clarke return max_irq; 1911e24429eSMichael Clark } 1921e24429eSMichael Clark 1931e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) 1941e24429eSMichael Clark { 1951e24429eSMichael Clark SiFivePLICState *plic = opaque; 1961e24429eSMichael Clark 1971e24429eSMichael Clark /* writes must be 4 byte words */ 1981e24429eSMichael Clark if ((addr & 0x3) != 0) { 1991e24429eSMichael Clark goto err; 2001e24429eSMichael Clark } 2011e24429eSMichael Clark 2021e24429eSMichael Clark if (addr >= plic->priority_base && /* 4 bytes per source */ 2031e24429eSMichael Clark addr < plic->priority_base + (plic->num_sources << 2)) 2041e24429eSMichael Clark { 2050feb4a71SAlistair Francis uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; 2061e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2071e24429eSMichael Clark qemu_log("plic: read priority: irq=%d priority=%d\n", 2081e24429eSMichael Clark irq, plic->source_priority[irq]); 2091e24429eSMichael Clark } 2101e24429eSMichael Clark return plic->source_priority[irq]; 2111e24429eSMichael Clark } else if (addr >= plic->pending_base && /* 1 bit per source */ 2121e24429eSMichael Clark addr < plic->pending_base + (plic->num_sources >> 3)) 2131e24429eSMichael Clark { 214e41848e5SMichael Clark uint32_t word = (addr - plic->pending_base) >> 2; 2151e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2161e24429eSMichael Clark qemu_log("plic: read pending: word=%d value=%d\n", 2171e24429eSMichael Clark word, plic->pending[word]); 2181e24429eSMichael Clark } 2191e24429eSMichael Clark return plic->pending[word]; 2201e24429eSMichael Clark } else if (addr >= plic->enable_base && /* 1 bit per source */ 2211e24429eSMichael Clark addr < plic->enable_base + plic->num_addrs * plic->enable_stride) 2221e24429eSMichael Clark { 2231e24429eSMichael Clark uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 2241e24429eSMichael Clark uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 2251e24429eSMichael Clark if (wordid < plic->bitfield_words) { 2261e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2271e24429eSMichael Clark qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n", 2281e24429eSMichael Clark plic->addr_config[addrid].hartid, 2291e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), wordid, 2301e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + wordid]); 2311e24429eSMichael Clark } 2321e24429eSMichael Clark return plic->enable[addrid * plic->bitfield_words + wordid]; 2331e24429eSMichael Clark } 2341e24429eSMichael Clark } else if (addr >= plic->context_base && /* 1 bit per source */ 2351e24429eSMichael Clark addr < plic->context_base + plic->num_addrs * plic->context_stride) 2361e24429eSMichael Clark { 2371e24429eSMichael Clark uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 2381e24429eSMichael Clark uint32_t contextid = (addr & (plic->context_stride - 1)); 2391e24429eSMichael Clark if (contextid == 0) { 2401e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2411e24429eSMichael Clark qemu_log("plic: read priority: hart%d-%c priority=%x\n", 2421e24429eSMichael Clark plic->addr_config[addrid].hartid, 2431e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), 2441e24429eSMichael Clark plic->target_priority[addrid]); 2451e24429eSMichael Clark } 2461e24429eSMichael Clark return plic->target_priority[addrid]; 2471e24429eSMichael Clark } else if (contextid == 4) { 2481e24429eSMichael Clark uint32_t value = sifive_plic_claim(plic, addrid); 2491e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2501e24429eSMichael Clark qemu_log("plic: read claim: hart%d-%c irq=%x\n", 2511e24429eSMichael Clark plic->addr_config[addrid].hartid, 2521e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), 2531e24429eSMichael Clark value); 2541e24429eSMichael Clark } 25555765822SJessica Clarke sifive_plic_update(plic); 2561e24429eSMichael Clark return value; 2571e24429eSMichael Clark } 2581e24429eSMichael Clark } 2591e24429eSMichael Clark 2601e24429eSMichael Clark err: 26179bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 26279bcac25SAlistair Francis "%s: Invalid register read 0x%" HWADDR_PRIx "\n", 26379bcac25SAlistair Francis __func__, addr); 2641e24429eSMichael Clark return 0; 2651e24429eSMichael Clark } 2661e24429eSMichael Clark 2671e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, 2681e24429eSMichael Clark unsigned size) 2691e24429eSMichael Clark { 2701e24429eSMichael Clark SiFivePLICState *plic = opaque; 2711e24429eSMichael Clark 2721e24429eSMichael Clark /* writes must be 4 byte words */ 2731e24429eSMichael Clark if ((addr & 0x3) != 0) { 2741e24429eSMichael Clark goto err; 2751e24429eSMichael Clark } 2761e24429eSMichael Clark 2771e24429eSMichael Clark if (addr >= plic->priority_base && /* 4 bytes per source */ 2781e24429eSMichael Clark addr < plic->priority_base + (plic->num_sources << 2)) 2791e24429eSMichael Clark { 2800feb4a71SAlistair Francis uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; 2811e24429eSMichael Clark plic->source_priority[irq] = value & 7; 2821e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 2831e24429eSMichael Clark qemu_log("plic: write priority: irq=%d priority=%d\n", 2841e24429eSMichael Clark irq, plic->source_priority[irq]); 2851e24429eSMichael Clark } 28655765822SJessica Clarke sifive_plic_update(plic); 2871e24429eSMichael Clark return; 2881e24429eSMichael Clark } else if (addr >= plic->pending_base && /* 1 bit per source */ 2891e24429eSMichael Clark addr < plic->pending_base + (plic->num_sources >> 3)) 2901e24429eSMichael Clark { 29179bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 29279bcac25SAlistair Francis "%s: invalid pending write: 0x%" HWADDR_PRIx "", 29379bcac25SAlistair Francis __func__, addr); 2941e24429eSMichael Clark return; 2951e24429eSMichael Clark } else if (addr >= plic->enable_base && /* 1 bit per source */ 2961e24429eSMichael Clark addr < plic->enable_base + plic->num_addrs * plic->enable_stride) 2971e24429eSMichael Clark { 2981e24429eSMichael Clark uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; 2991e24429eSMichael Clark uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; 3001e24429eSMichael Clark if (wordid < plic->bitfield_words) { 3011e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + wordid] = value; 3021e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 3031e24429eSMichael Clark qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n", 3041e24429eSMichael Clark plic->addr_config[addrid].hartid, 3051e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), wordid, 3061e24429eSMichael Clark plic->enable[addrid * plic->bitfield_words + wordid]); 3071e24429eSMichael Clark } 3081e24429eSMichael Clark return; 3091e24429eSMichael Clark } 3101e24429eSMichael Clark } else if (addr >= plic->context_base && /* 4 bytes per reg */ 3111e24429eSMichael Clark addr < plic->context_base + plic->num_addrs * plic->context_stride) 3121e24429eSMichael Clark { 3131e24429eSMichael Clark uint32_t addrid = (addr - plic->context_base) / plic->context_stride; 3141e24429eSMichael Clark uint32_t contextid = (addr & (plic->context_stride - 1)); 3151e24429eSMichael Clark if (contextid == 0) { 3161e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 3171e24429eSMichael Clark qemu_log("plic: write priority: hart%d-%c priority=%x\n", 3181e24429eSMichael Clark plic->addr_config[addrid].hartid, 3191e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), 3201e24429eSMichael Clark plic->target_priority[addrid]); 3211e24429eSMichael Clark } 3221e24429eSMichael Clark if (value <= plic->num_priorities) { 3231e24429eSMichael Clark plic->target_priority[addrid] = value; 3241e24429eSMichael Clark sifive_plic_update(plic); 3251e24429eSMichael Clark } 3261e24429eSMichael Clark return; 3271e24429eSMichael Clark } else if (contextid == 4) { 3281e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 3291e24429eSMichael Clark qemu_log("plic: write claim: hart%d-%c irq=%x\n", 3301e24429eSMichael Clark plic->addr_config[addrid].hartid, 3311e24429eSMichael Clark mode_to_char(plic->addr_config[addrid].mode), 3321e24429eSMichael Clark (uint32_t)value); 3331e24429eSMichael Clark } 3341e24429eSMichael Clark if (value < plic->num_sources) { 3351e24429eSMichael Clark sifive_plic_set_claimed(plic, value, false); 3361e24429eSMichael Clark sifive_plic_update(plic); 3371e24429eSMichael Clark } 3381e24429eSMichael Clark return; 3391e24429eSMichael Clark } 3401e24429eSMichael Clark } 3411e24429eSMichael Clark 3421e24429eSMichael Clark err: 34379bcac25SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR, 34479bcac25SAlistair Francis "%s: Invalid register write 0x%" HWADDR_PRIx "\n", 34579bcac25SAlistair Francis __func__, addr); 3461e24429eSMichael Clark } 3471e24429eSMichael Clark 3481e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = { 3491e24429eSMichael Clark .read = sifive_plic_read, 3501e24429eSMichael Clark .write = sifive_plic_write, 3511e24429eSMichael Clark .endianness = DEVICE_LITTLE_ENDIAN, 3521e24429eSMichael Clark .valid = { 3531e24429eSMichael Clark .min_access_size = 4, 3541e24429eSMichael Clark .max_access_size = 4 3551e24429eSMichael Clark } 3561e24429eSMichael Clark }; 3571e24429eSMichael Clark 3581e24429eSMichael Clark /* 3591e24429eSMichael Clark * parse PLIC hart/mode address offset config 3601e24429eSMichael Clark * 3611e24429eSMichael Clark * "M" 1 hart with M mode 3621e24429eSMichael Clark * "MS,MS" 2 harts, 0-1 with M and S mode 3631e24429eSMichael Clark * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode 3641e24429eSMichael Clark */ 3651e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic) 3661e24429eSMichael Clark { 3671e24429eSMichael Clark int addrid, hartid, modes; 3681e24429eSMichael Clark const char *p; 3691e24429eSMichael Clark char c; 3701e24429eSMichael Clark 3711e24429eSMichael Clark /* count and validate hart/mode combinations */ 3721e24429eSMichael Clark addrid = 0, hartid = 0, modes = 0; 3731e24429eSMichael Clark p = plic->hart_config; 3741e24429eSMichael Clark while ((c = *p++)) { 3751e24429eSMichael Clark if (c == ',') { 376244df421SMichael Clark addrid += ctpop8(modes); 3771e24429eSMichael Clark modes = 0; 3781e24429eSMichael Clark hartid++; 3791e24429eSMichael Clark } else { 3801e24429eSMichael Clark int m = 1 << char_to_mode(c); 3811e24429eSMichael Clark if (modes == (modes | m)) { 3821e24429eSMichael Clark error_report("plic: duplicate mode '%c' in config: %s", 3831e24429eSMichael Clark c, plic->hart_config); 3841e24429eSMichael Clark exit(1); 3851e24429eSMichael Clark } 3861e24429eSMichael Clark modes |= m; 3871e24429eSMichael Clark } 3881e24429eSMichael Clark } 3891e24429eSMichael Clark if (modes) { 390244df421SMichael Clark addrid += ctpop8(modes); 3911e24429eSMichael Clark } 3921e24429eSMichael Clark hartid++; 3931e24429eSMichael Clark 3941e24429eSMichael Clark plic->num_addrs = addrid; 395c9270e10SAnup Patel plic->num_harts = hartid; 396c9270e10SAnup Patel 397c9270e10SAnup Patel /* store hart/mode combinations */ 3981e24429eSMichael Clark plic->addr_config = g_new(PLICAddr, plic->num_addrs); 399c9270e10SAnup Patel addrid = 0, hartid = plic->hartid_base; 4001e24429eSMichael Clark p = plic->hart_config; 4011e24429eSMichael Clark while ((c = *p++)) { 4021e24429eSMichael Clark if (c == ',') { 4031e24429eSMichael Clark hartid++; 4041e24429eSMichael Clark } else { 4051e24429eSMichael Clark plic->addr_config[addrid].addrid = addrid; 4061e24429eSMichael Clark plic->addr_config[addrid].hartid = hartid; 4071e24429eSMichael Clark plic->addr_config[addrid].mode = char_to_mode(c); 4081e24429eSMichael Clark addrid++; 4091e24429eSMichael Clark } 4101e24429eSMichael Clark } 4111e24429eSMichael Clark } 4121e24429eSMichael Clark 4131e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level) 4141e24429eSMichael Clark { 4151e24429eSMichael Clark SiFivePLICState *plic = opaque; 4161e24429eSMichael Clark if (RISCV_DEBUG_PLIC) { 4171e24429eSMichael Clark qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); 4181e24429eSMichael Clark } 4191e24429eSMichael Clark sifive_plic_set_pending(plic, irq, level > 0); 4201e24429eSMichael Clark sifive_plic_update(plic); 4211e24429eSMichael Clark } 4221e24429eSMichael Clark 4231e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp) 4241e24429eSMichael Clark { 4251e24429eSMichael Clark SiFivePLICState *plic = SIFIVE_PLIC(dev); 426e3e7039cSMichael Clark int i; 4271e24429eSMichael Clark 4281e24429eSMichael Clark memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, 4291e24429eSMichael Clark TYPE_SIFIVE_PLIC, plic->aperture_size); 4301e24429eSMichael Clark parse_hart_config(plic); 4311e24429eSMichael Clark plic->bitfield_words = (plic->num_sources + 31) >> 5; 432dbd3ec54SYifei Jiang plic->num_enables = plic->bitfield_words * plic->num_addrs; 4331e24429eSMichael Clark plic->source_priority = g_new0(uint32_t, plic->num_sources); 4341e24429eSMichael Clark plic->target_priority = g_new(uint32_t, plic->num_addrs); 4351e24429eSMichael Clark plic->pending = g_new0(uint32_t, plic->bitfield_words); 4361e24429eSMichael Clark plic->claimed = g_new0(uint32_t, plic->bitfield_words); 437dbd3ec54SYifei Jiang plic->enable = g_new0(uint32_t, plic->num_enables); 4381e24429eSMichael Clark sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); 439647a70a1SAlistair Francis qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); 440e3e7039cSMichael Clark 441f436ecc3SAlistair Francis plic->s_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts); 442f436ecc3SAlistair Francis qdev_init_gpio_out(dev, plic->s_external_irqs, plic->num_harts); 443f436ecc3SAlistair Francis 444f436ecc3SAlistair Francis plic->m_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts); 445f436ecc3SAlistair Francis qdev_init_gpio_out(dev, plic->m_external_irqs, plic->num_harts); 446f436ecc3SAlistair Francis 447e3e7039cSMichael Clark /* We can't allow the supervisor to control SEIP as this would allow the 448e3e7039cSMichael Clark * supervisor to clear a pending external interrupt which will result in 449e3e7039cSMichael Clark * lost a interrupt in the case a PLIC is attached. The SEIP bit must be 450e3e7039cSMichael Clark * hardware controlled when a PLIC is attached. 451e3e7039cSMichael Clark */ 452c9270e10SAnup Patel for (i = 0; i < plic->num_harts; i++) { 453c9270e10SAnup Patel RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i)); 454e3e7039cSMichael Clark if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { 455e3e7039cSMichael Clark error_report("SEIP already claimed"); 456e3e7039cSMichael Clark exit(1); 457e3e7039cSMichael Clark } 458e3e7039cSMichael Clark } 45984bdc58cSPeter Maydell 4604f5604c4SAlistair Francis msi_nonbroken = true; 4611e24429eSMichael Clark } 4621e24429eSMichael Clark 463dbd3ec54SYifei Jiang static const VMStateDescription vmstate_sifive_plic = { 464dbd3ec54SYifei Jiang .name = "riscv_sifive_plic", 465dbd3ec54SYifei Jiang .version_id = 1, 466dbd3ec54SYifei Jiang .minimum_version_id = 1, 467dbd3ec54SYifei Jiang .fields = (VMStateField[]) { 468dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState, 469dbd3ec54SYifei Jiang num_sources, 0, 470dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 471dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState, 472dbd3ec54SYifei Jiang num_addrs, 0, 473dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 474dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0, 475dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 476dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0, 477dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 478dbd3ec54SYifei Jiang VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0, 479dbd3ec54SYifei Jiang vmstate_info_uint32, uint32_t), 480dbd3ec54SYifei Jiang VMSTATE_END_OF_LIST() 481dbd3ec54SYifei Jiang } 482dbd3ec54SYifei Jiang }; 483dbd3ec54SYifei Jiang 484*d8c6590fSAlistair Francis static Property sifive_plic_properties[] = { 485*d8c6590fSAlistair Francis DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), 486*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), 487*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), 488*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), 489*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), 490*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), 491*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), 492*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), 493*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), 494*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), 495*d8c6590fSAlistair Francis DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), 496*d8c6590fSAlistair Francis DEFINE_PROP_END_OF_LIST(), 497*d8c6590fSAlistair Francis }; 498*d8c6590fSAlistair Francis 4991e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data) 5001e24429eSMichael Clark { 5011e24429eSMichael Clark DeviceClass *dc = DEVICE_CLASS(klass); 5021e24429eSMichael Clark 5034f67d30bSMarc-André Lureau device_class_set_props(dc, sifive_plic_properties); 5041e24429eSMichael Clark dc->realize = sifive_plic_realize; 505dbd3ec54SYifei Jiang dc->vmsd = &vmstate_sifive_plic; 5061e24429eSMichael Clark } 5071e24429eSMichael Clark 5081e24429eSMichael Clark static const TypeInfo sifive_plic_info = { 5091e24429eSMichael Clark .name = TYPE_SIFIVE_PLIC, 5101e24429eSMichael Clark .parent = TYPE_SYS_BUS_DEVICE, 5111e24429eSMichael Clark .instance_size = sizeof(SiFivePLICState), 5121e24429eSMichael Clark .class_init = sifive_plic_class_init, 5131e24429eSMichael Clark }; 5141e24429eSMichael Clark 5151e24429eSMichael Clark static void sifive_plic_register_types(void) 5161e24429eSMichael Clark { 5171e24429eSMichael Clark type_register_static(&sifive_plic_info); 5181e24429eSMichael Clark } 5191e24429eSMichael Clark 5201e24429eSMichael Clark type_init(sifive_plic_register_types) 5211e24429eSMichael Clark 5221e24429eSMichael Clark /* 5231e24429eSMichael Clark * Create PLIC device. 5241e24429eSMichael Clark */ 5251e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, 526f436ecc3SAlistair Francis uint32_t num_harts, 527c9270e10SAnup Patel uint32_t hartid_base, uint32_t num_sources, 528c9270e10SAnup Patel uint32_t num_priorities, uint32_t priority_base, 529c9270e10SAnup Patel uint32_t pending_base, uint32_t enable_base, 530c9270e10SAnup Patel uint32_t enable_stride, uint32_t context_base, 531c9270e10SAnup Patel uint32_t context_stride, uint32_t aperture_size) 5321e24429eSMichael Clark { 5333e80f690SMarkus Armbruster DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC); 534f436ecc3SAlistair Francis int i; 535f436ecc3SAlistair Francis 5361e24429eSMichael Clark assert(enable_stride == (enable_stride & -enable_stride)); 5371e24429eSMichael Clark assert(context_stride == (context_stride & -context_stride)); 5381e24429eSMichael Clark qdev_prop_set_string(dev, "hart-config", hart_config); 539c9270e10SAnup Patel qdev_prop_set_uint32(dev, "hartid-base", hartid_base); 5401e24429eSMichael Clark qdev_prop_set_uint32(dev, "num-sources", num_sources); 5411e24429eSMichael Clark qdev_prop_set_uint32(dev, "num-priorities", num_priorities); 5421e24429eSMichael Clark qdev_prop_set_uint32(dev, "priority-base", priority_base); 5431e24429eSMichael Clark qdev_prop_set_uint32(dev, "pending-base", pending_base); 5441e24429eSMichael Clark qdev_prop_set_uint32(dev, "enable-base", enable_base); 5451e24429eSMichael Clark qdev_prop_set_uint32(dev, "enable-stride", enable_stride); 5461e24429eSMichael Clark qdev_prop_set_uint32(dev, "context-base", context_base); 5471e24429eSMichael Clark qdev_prop_set_uint32(dev, "context-stride", context_stride); 5481e24429eSMichael Clark qdev_prop_set_uint32(dev, "aperture-size", aperture_size); 5493c6ef471SMarkus Armbruster sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 5501e24429eSMichael Clark sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 551f436ecc3SAlistair Francis 552f436ecc3SAlistair Francis for (i = 0; i < num_harts; i++) { 553f436ecc3SAlistair Francis CPUState *cpu = qemu_get_cpu(hartid_base + i); 554f436ecc3SAlistair Francis 555f436ecc3SAlistair Francis qdev_connect_gpio_out(dev, i, 556f436ecc3SAlistair Francis qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT)); 557f436ecc3SAlistair Francis qdev_connect_gpio_out(dev, num_harts + i, 558f436ecc3SAlistair Francis qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); 559f436ecc3SAlistair Francis } 560f436ecc3SAlistair Francis 5611e24429eSMichael Clark return dev; 5621e24429eSMichael Clark } 563