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