xref: /qemu/hw/intc/sifive_plic.c (revision f436ecc3156dea7edce97e7c247e3667203f5c8b)
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"
32*f436ecc3SAlistair 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);
144*f436ecc3SAlistair Francis 
1451e24429eSMichael Clark         switch (mode) {
1461e24429eSMichael Clark         case PLICMode_M:
147*f436ecc3SAlistair Francis             qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level);
1481e24429eSMichael Clark             break;
1491e24429eSMichael Clark         case PLICMode_S:
150*f436ecc3SAlistair 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 static Property sifive_plic_properties[] = {
3591e24429eSMichael Clark     DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
360c9270e10SAnup Patel     DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0),
3611e24429eSMichael Clark     DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
3621e24429eSMichael Clark     DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
3631e24429eSMichael Clark     DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
3641e24429eSMichael Clark     DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
3651e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
3661e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
3671e24429eSMichael Clark     DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
3681e24429eSMichael Clark     DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
3691e24429eSMichael Clark     DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
3701e24429eSMichael Clark     DEFINE_PROP_END_OF_LIST(),
3711e24429eSMichael Clark };
3721e24429eSMichael Clark 
3731e24429eSMichael Clark /*
3741e24429eSMichael Clark  * parse PLIC hart/mode address offset config
3751e24429eSMichael Clark  *
3761e24429eSMichael Clark  * "M"              1 hart with M mode
3771e24429eSMichael Clark  * "MS,MS"          2 harts, 0-1 with M and S mode
3781e24429eSMichael Clark  * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
3791e24429eSMichael Clark  */
3801e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic)
3811e24429eSMichael Clark {
3821e24429eSMichael Clark     int addrid, hartid, modes;
3831e24429eSMichael Clark     const char *p;
3841e24429eSMichael Clark     char c;
3851e24429eSMichael Clark 
3861e24429eSMichael Clark     /* count and validate hart/mode combinations */
3871e24429eSMichael Clark     addrid = 0, hartid = 0, modes = 0;
3881e24429eSMichael Clark     p = plic->hart_config;
3891e24429eSMichael Clark     while ((c = *p++)) {
3901e24429eSMichael Clark         if (c == ',') {
391244df421SMichael Clark             addrid += ctpop8(modes);
3921e24429eSMichael Clark             modes = 0;
3931e24429eSMichael Clark             hartid++;
3941e24429eSMichael Clark         } else {
3951e24429eSMichael Clark             int m = 1 << char_to_mode(c);
3961e24429eSMichael Clark             if (modes == (modes | m)) {
3971e24429eSMichael Clark                 error_report("plic: duplicate mode '%c' in config: %s",
3981e24429eSMichael Clark                              c, plic->hart_config);
3991e24429eSMichael Clark                 exit(1);
4001e24429eSMichael Clark             }
4011e24429eSMichael Clark             modes |= m;
4021e24429eSMichael Clark         }
4031e24429eSMichael Clark     }
4041e24429eSMichael Clark     if (modes) {
405244df421SMichael Clark         addrid += ctpop8(modes);
4061e24429eSMichael Clark     }
4071e24429eSMichael Clark     hartid++;
4081e24429eSMichael Clark 
4091e24429eSMichael Clark     plic->num_addrs = addrid;
410c9270e10SAnup Patel     plic->num_harts = hartid;
411c9270e10SAnup Patel 
412c9270e10SAnup Patel     /* store hart/mode combinations */
4131e24429eSMichael Clark     plic->addr_config = g_new(PLICAddr, plic->num_addrs);
414c9270e10SAnup Patel     addrid = 0, hartid = plic->hartid_base;
4151e24429eSMichael Clark     p = plic->hart_config;
4161e24429eSMichael Clark     while ((c = *p++)) {
4171e24429eSMichael Clark         if (c == ',') {
4181e24429eSMichael Clark             hartid++;
4191e24429eSMichael Clark         } else {
4201e24429eSMichael Clark             plic->addr_config[addrid].addrid = addrid;
4211e24429eSMichael Clark             plic->addr_config[addrid].hartid = hartid;
4221e24429eSMichael Clark             plic->addr_config[addrid].mode = char_to_mode(c);
4231e24429eSMichael Clark             addrid++;
4241e24429eSMichael Clark         }
4251e24429eSMichael Clark     }
4261e24429eSMichael Clark }
4271e24429eSMichael Clark 
4281e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level)
4291e24429eSMichael Clark {
4301e24429eSMichael Clark     SiFivePLICState *plic = opaque;
4311e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
4321e24429eSMichael Clark         qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
4331e24429eSMichael Clark     }
4341e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, level > 0);
4351e24429eSMichael Clark     sifive_plic_update(plic);
4361e24429eSMichael Clark }
4371e24429eSMichael Clark 
4381e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp)
4391e24429eSMichael Clark {
4401e24429eSMichael Clark     SiFivePLICState *plic = SIFIVE_PLIC(dev);
441e3e7039cSMichael Clark     int i;
4421e24429eSMichael Clark 
4431e24429eSMichael Clark     memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
4441e24429eSMichael Clark                           TYPE_SIFIVE_PLIC, plic->aperture_size);
4451e24429eSMichael Clark     parse_hart_config(plic);
4461e24429eSMichael Clark     plic->bitfield_words = (plic->num_sources + 31) >> 5;
447dbd3ec54SYifei Jiang     plic->num_enables = plic->bitfield_words * plic->num_addrs;
4481e24429eSMichael Clark     plic->source_priority = g_new0(uint32_t, plic->num_sources);
4491e24429eSMichael Clark     plic->target_priority = g_new(uint32_t, plic->num_addrs);
4501e24429eSMichael Clark     plic->pending = g_new0(uint32_t, plic->bitfield_words);
4511e24429eSMichael Clark     plic->claimed = g_new0(uint32_t, plic->bitfield_words);
452dbd3ec54SYifei Jiang     plic->enable = g_new0(uint32_t, plic->num_enables);
4531e24429eSMichael Clark     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
454647a70a1SAlistair Francis     qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
455e3e7039cSMichael Clark 
456*f436ecc3SAlistair Francis     plic->s_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
457*f436ecc3SAlistair Francis     qdev_init_gpio_out(dev, plic->s_external_irqs, plic->num_harts);
458*f436ecc3SAlistair Francis 
459*f436ecc3SAlistair Francis     plic->m_external_irqs = g_malloc(sizeof(qemu_irq) * plic->num_harts);
460*f436ecc3SAlistair Francis     qdev_init_gpio_out(dev, plic->m_external_irqs, plic->num_harts);
461*f436ecc3SAlistair Francis 
462e3e7039cSMichael Clark     /* We can't allow the supervisor to control SEIP as this would allow the
463e3e7039cSMichael Clark      * supervisor to clear a pending external interrupt which will result in
464e3e7039cSMichael Clark      * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
465e3e7039cSMichael Clark      * hardware controlled when a PLIC is attached.
466e3e7039cSMichael Clark      */
467c9270e10SAnup Patel     for (i = 0; i < plic->num_harts; i++) {
468c9270e10SAnup Patel         RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i));
469e3e7039cSMichael Clark         if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
470e3e7039cSMichael Clark             error_report("SEIP already claimed");
471e3e7039cSMichael Clark             exit(1);
472e3e7039cSMichael Clark         }
473e3e7039cSMichael Clark     }
47484bdc58cSPeter Maydell 
4754f5604c4SAlistair Francis     msi_nonbroken = true;
4761e24429eSMichael Clark }
4771e24429eSMichael Clark 
478dbd3ec54SYifei Jiang static const VMStateDescription vmstate_sifive_plic = {
479dbd3ec54SYifei Jiang     .name = "riscv_sifive_plic",
480dbd3ec54SYifei Jiang     .version_id = 1,
481dbd3ec54SYifei Jiang     .minimum_version_id = 1,
482dbd3ec54SYifei Jiang     .fields = (VMStateField[]) {
483dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState,
484dbd3ec54SYifei Jiang                                   num_sources, 0,
485dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
486dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState,
487dbd3ec54SYifei Jiang                                   num_addrs, 0,
488dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
489dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0,
490dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
491dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0,
492dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
493dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0,
494dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
495dbd3ec54SYifei Jiang             VMSTATE_END_OF_LIST()
496dbd3ec54SYifei Jiang         }
497dbd3ec54SYifei Jiang };
498dbd3ec54SYifei Jiang 
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,
526*f436ecc3SAlistair 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);
534*f436ecc3SAlistair Francis     int i;
535*f436ecc3SAlistair 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);
551*f436ecc3SAlistair Francis 
552*f436ecc3SAlistair Francis     for (i = 0; i < num_harts; i++) {
553*f436ecc3SAlistair Francis         CPUState *cpu = qemu_get_cpu(hartid_base + i);
554*f436ecc3SAlistair Francis 
555*f436ecc3SAlistair Francis         qdev_connect_gpio_out(dev, i,
556*f436ecc3SAlistair Francis                               qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
557*f436ecc3SAlistair Francis         qdev_connect_gpio_out(dev, num_harts + i,
558*f436ecc3SAlistair Francis                               qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
559*f436ecc3SAlistair Francis     }
560*f436ecc3SAlistair Francis 
5611e24429eSMichael Clark     return dev;
5621e24429eSMichael Clark }
563