xref: /qemu/hw/intc/sifive_plic.c (revision 12d1a768bdfea6e27a3a829228840d72507613a1)
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"
3332cad1ffSPhilippe Mathieu-Daudé #include "system/kvm.h"
341e24429eSMichael Clark 
addr_between(uint32_t addr,uint32_t base,uint32_t num)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 
char_to_mode(char c)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 
atomic_set_masked(uint32_t * a,uint32_t mask,uint32_t value)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 
sifive_plic_set_pending(SiFivePLICState * plic,int irq,bool level)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 
sifive_plic_set_claimed(SiFivePLICState * plic,int irq,bool level)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 
sifive_plic_claimed(SiFivePLICState * plic,uint32_t addrid)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 
sifive_plic_update(SiFivePLICState * plic)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 
sifive_plic_read(void * opaque,hwaddr addr,unsigned size)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 
sifive_plic_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)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 
sifive_plic_reset(DeviceState * dev)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  */
parse_hart_config(SiFivePLICState * plic)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 
sifive_plic_irq_request(void * opaque,int irq,int level)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 
sifive_plic_realize(DeviceState * dev,Error ** errp)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 
433783e3b21SRichard 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 };
448d8c6590fSAlistair Francis 
sifive_plic_class_init(ObjectClass * klass,const void * data)449*12d1a768SPhilippe Mathieu-Daudé static void sifive_plic_class_init(ObjectClass *klass, const void *data)
4501e24429eSMichael Clark {
4511e24429eSMichael Clark     DeviceClass *dc = DEVICE_CLASS(klass);
4521e24429eSMichael Clark 
453e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, sifive_plic_reset);
4544f67d30bSMarc-André Lureau     device_class_set_props(dc, sifive_plic_properties);
4551e24429eSMichael Clark     dc->realize = sifive_plic_realize;
456dbd3ec54SYifei Jiang     dc->vmsd = &vmstate_sifive_plic;
4571e24429eSMichael Clark }
4581e24429eSMichael Clark 
4591e24429eSMichael Clark static const TypeInfo sifive_plic_info = {
4601e24429eSMichael Clark     .name          = TYPE_SIFIVE_PLIC,
4611e24429eSMichael Clark     .parent        = TYPE_SYS_BUS_DEVICE,
4621e24429eSMichael Clark     .instance_size = sizeof(SiFivePLICState),
4631e24429eSMichael Clark     .class_init    = sifive_plic_class_init,
4641e24429eSMichael Clark };
4651e24429eSMichael Clark 
sifive_plic_register_types(void)4661e24429eSMichael Clark static void sifive_plic_register_types(void)
4671e24429eSMichael Clark {
4681e24429eSMichael Clark     type_register_static(&sifive_plic_info);
4691e24429eSMichael Clark }
4701e24429eSMichael Clark 
type_init(sifive_plic_register_types)4711e24429eSMichael Clark type_init(sifive_plic_register_types)
4721e24429eSMichael Clark 
4731e24429eSMichael Clark /*
4741e24429eSMichael Clark  * Create PLIC device.
4751e24429eSMichael Clark  */
4761e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
477f436ecc3SAlistair Francis     uint32_t num_harts,
478c9270e10SAnup Patel     uint32_t hartid_base, uint32_t num_sources,
479c9270e10SAnup Patel     uint32_t num_priorities, uint32_t priority_base,
480c9270e10SAnup Patel     uint32_t pending_base, uint32_t enable_base,
481c9270e10SAnup Patel     uint32_t enable_stride, uint32_t context_base,
482c9270e10SAnup Patel     uint32_t context_stride, uint32_t aperture_size)
4831e24429eSMichael Clark {
4843e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
48540244040SAlistair Francis     int i;
486ad40be27SYifei Jiang     SiFivePLICState *plic;
487f436ecc3SAlistair Francis 
4881e24429eSMichael Clark     assert(enable_stride == (enable_stride & -enable_stride));
4891e24429eSMichael Clark     assert(context_stride == (context_stride & -context_stride));
4901e24429eSMichael Clark     qdev_prop_set_string(dev, "hart-config", hart_config);
491c9270e10SAnup Patel     qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
4921e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-sources", num_sources);
4931e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
4941e24429eSMichael Clark     qdev_prop_set_uint32(dev, "priority-base", priority_base);
4951e24429eSMichael Clark     qdev_prop_set_uint32(dev, "pending-base", pending_base);
4961e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-base", enable_base);
4971e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
4981e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-base", context_base);
4991e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-stride", context_stride);
5001e24429eSMichael Clark     qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
5013c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
5021e24429eSMichael Clark     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
503f436ecc3SAlistair Francis 
504ad40be27SYifei Jiang     plic = SIFIVE_PLIC(dev);
505f436ecc3SAlistair Francis 
50640244040SAlistair Francis     for (i = 0; i < plic->num_addrs; i++) {
50740244040SAlistair Francis         int cpu_num = plic->addr_config[i].hartid;
50854f21836SAtish Patra         CPUState *cpu = qemu_get_cpu(cpu_num);
50940244040SAlistair Francis 
51040244040SAlistair Francis         if (plic->addr_config[i].mode == PLICMode_M) {
5110a9a6cbaSFrédéric Pétrot             qdev_connect_gpio_out(dev, cpu_num - hartid_base + num_harts,
512f436ecc3SAlistair Francis                                   qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
513f436ecc3SAlistair Francis         }
51440244040SAlistair Francis         if (plic->addr_config[i].mode == PLICMode_S) {
5150a9a6cbaSFrédéric Pétrot             qdev_connect_gpio_out(dev, cpu_num - hartid_base,
516ad40be27SYifei Jiang                                   qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
517ad40be27SYifei Jiang         }
518ad40be27SYifei Jiang     }
519ad40be27SYifei Jiang 
5201e24429eSMichael Clark     return dev;
5211e24429eSMichael Clark }
522