xref: /openbmc/qemu/hw/intc/armv7m_nvic.c (revision 8779fccb)
1 /*
2  * ARM Nested Vectored Interrupt Controller
3  *
4  * Copyright (c) 2006-2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GPL.
8  *
9  * The ARMv7M System controller is fairly tightly tied in with the
10  * NVIC.  Much of that is also implemented here.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qapi/error.h"
15 #include "qemu-common.h"
16 #include "cpu.h"
17 #include "hw/sysbus.h"
18 #include "qemu/timer.h"
19 #include "hw/arm/arm.h"
20 #include "exec/address-spaces.h"
21 #include "gic_internal.h"
22 #include "qemu/log.h"
23 
24 typedef struct {
25     GICState gic;
26     ARMCPU *cpu;
27     struct {
28         uint32_t control;
29         uint32_t reload;
30         int64_t tick;
31         QEMUTimer *timer;
32     } systick;
33     MemoryRegion sysregmem;
34     MemoryRegion gic_iomem_alias;
35     MemoryRegion container;
36     uint32_t num_irq;
37     qemu_irq sysresetreq;
38 } nvic_state;
39 
40 #define TYPE_NVIC "armv7m_nvic"
41 /**
42  * NVICClass:
43  * @parent_reset: the parent class' reset handler.
44  *
45  * A model of the v7M NVIC and System Controller
46  */
47 typedef struct NVICClass {
48     /*< private >*/
49     ARMGICClass parent_class;
50     /*< public >*/
51     DeviceRealize parent_realize;
52     void (*parent_reset)(DeviceState *dev);
53 } NVICClass;
54 
55 #define NVIC_CLASS(klass) \
56     OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC)
57 #define NVIC_GET_CLASS(obj) \
58     OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC)
59 #define NVIC(obj) \
60     OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC)
61 
62 static const uint8_t nvic_id[] = {
63     0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1
64 };
65 
66 /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
67 #define SYSTICK_SCALE 1000ULL
68 
69 #define SYSTICK_ENABLE    (1 << 0)
70 #define SYSTICK_TICKINT   (1 << 1)
71 #define SYSTICK_CLKSOURCE (1 << 2)
72 #define SYSTICK_COUNTFLAG (1 << 16)
73 
74 int system_clock_scale;
75 
76 /* Conversion factor from qemu timer to SysTick frequencies.  */
77 static inline int64_t systick_scale(nvic_state *s)
78 {
79     if (s->systick.control & SYSTICK_CLKSOURCE)
80         return system_clock_scale;
81     else
82         return 1000;
83 }
84 
85 static void systick_reload(nvic_state *s, int reset)
86 {
87     /* The Cortex-M3 Devices Generic User Guide says that "When the
88      * ENABLE bit is set to 1, the counter loads the RELOAD value from the
89      * SYST RVR register and then counts down". So, we need to check the
90      * ENABLE bit before reloading the value.
91      */
92     if ((s->systick.control & SYSTICK_ENABLE) == 0) {
93         return;
94     }
95 
96     if (reset)
97         s->systick.tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
98     s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
99     timer_mod(s->systick.timer, s->systick.tick);
100 }
101 
102 static void systick_timer_tick(void * opaque)
103 {
104     nvic_state *s = (nvic_state *)opaque;
105     s->systick.control |= SYSTICK_COUNTFLAG;
106     if (s->systick.control & SYSTICK_TICKINT) {
107         /* Trigger the interrupt.  */
108         armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
109     }
110     if (s->systick.reload == 0) {
111         s->systick.control &= ~SYSTICK_ENABLE;
112     } else {
113         systick_reload(s, 0);
114     }
115 }
116 
117 static void systick_reset(nvic_state *s)
118 {
119     s->systick.control = 0;
120     s->systick.reload = 0;
121     s->systick.tick = 0;
122     timer_del(s->systick.timer);
123 }
124 
125 /* The external routines use the hardware vector numbering, ie. the first
126    IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */
127 void armv7m_nvic_set_pending(void *opaque, int irq)
128 {
129     nvic_state *s = (nvic_state *)opaque;
130     if (irq >= 16)
131         irq += 16;
132     gic_set_pending_private(&s->gic, 0, irq);
133 }
134 
135 /* Make pending IRQ active.  */
136 int armv7m_nvic_acknowledge_irq(void *opaque)
137 {
138     nvic_state *s = (nvic_state *)opaque;
139     uint32_t irq;
140 
141     irq = gic_acknowledge_irq(&s->gic, 0, MEMTXATTRS_UNSPECIFIED);
142     if (irq == 1023)
143         hw_error("Interrupt but no vector\n");
144     if (irq >= 32)
145         irq -= 16;
146     return irq;
147 }
148 
149 void armv7m_nvic_complete_irq(void *opaque, int irq)
150 {
151     nvic_state *s = (nvic_state *)opaque;
152     if (irq >= 16)
153         irq += 16;
154     gic_complete_irq(&s->gic, 0, irq, MEMTXATTRS_UNSPECIFIED);
155 }
156 
157 static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
158 {
159     ARMCPU *cpu = s->cpu;
160     uint32_t val;
161     int irq;
162 
163     switch (offset) {
164     case 4: /* Interrupt Control Type.  */
165         return (s->num_irq / 32) - 1;
166     case 0x10: /* SysTick Control and Status.  */
167         val = s->systick.control;
168         s->systick.control &= ~SYSTICK_COUNTFLAG;
169         return val;
170     case 0x14: /* SysTick Reload Value.  */
171         return s->systick.reload;
172     case 0x18: /* SysTick Current Value.  */
173         {
174             int64_t t;
175             if ((s->systick.control & SYSTICK_ENABLE) == 0)
176                 return 0;
177             t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
178             if (t >= s->systick.tick)
179                 return 0;
180             val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
181             /* The interrupt in triggered when the timer reaches zero.
182                However the counter is not reloaded until the next clock
183                tick.  This is a hack to return zero during the first tick.  */
184             if (val > s->systick.reload)
185                 val = 0;
186             return val;
187         }
188     case 0x1c: /* SysTick Calibration Value.  */
189         return 10000;
190     case 0xd00: /* CPUID Base.  */
191         return cpu->midr;
192     case 0xd04: /* Interrupt Control State.  */
193         /* VECTACTIVE */
194         val = cpu->env.v7m.exception;
195         if (val == 1023) {
196             val = 0;
197         } else if (val >= 32) {
198             val -= 16;
199         }
200         /* VECTPENDING */
201         if (s->gic.current_pending[0] != 1023)
202             val |= (s->gic.current_pending[0] << 12);
203         /* ISRPENDING and RETTOBASE */
204         for (irq = 32; irq < s->num_irq; irq++) {
205             if (s->gic.irq_state[irq].pending) {
206                 val |= (1 << 22);
207                 break;
208             }
209             if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) {
210                 val |= (1 << 11);
211             }
212         }
213         /* PENDSTSET */
214         if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
215             val |= (1 << 26);
216         /* PENDSVSET */
217         if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending)
218             val |= (1 << 28);
219         /* NMIPENDSET */
220         if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending)
221             val |= (1 << 31);
222         return val;
223     case 0xd08: /* Vector Table Offset.  */
224         return cpu->env.v7m.vecbase;
225     case 0xd0c: /* Application Interrupt/Reset Control.  */
226         return 0xfa050000;
227     case 0xd10: /* System Control.  */
228         /* TODO: Implement SLEEPONEXIT.  */
229         return 0;
230     case 0xd14: /* Configuration Control.  */
231         return cpu->env.v7m.ccr;
232     case 0xd24: /* System Handler Status.  */
233         val = 0;
234         if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
235         if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
236         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
237         if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
238         if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
239         if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
240         if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
241         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
242         if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
243         if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
244         if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
245         if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
246         if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
247         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
248         return val;
249     case 0xd28: /* Configurable Fault Status.  */
250         return cpu->env.v7m.cfsr;
251     case 0xd2c: /* Hard Fault Status.  */
252         return cpu->env.v7m.hfsr;
253     case 0xd30: /* Debug Fault Status.  */
254         return cpu->env.v7m.dfsr;
255     case 0xd34: /* MMFAR MemManage Fault Address */
256         return cpu->env.v7m.mmfar;
257     case 0xd38: /* Bus Fault Address.  */
258         return cpu->env.v7m.bfar;
259     case 0xd3c: /* Aux Fault Status.  */
260         /* TODO: Implement fault status registers.  */
261         qemu_log_mask(LOG_UNIMP,
262                       "Aux Fault status registers unimplemented\n");
263         return 0;
264     case 0xd40: /* PFR0.  */
265         return 0x00000030;
266     case 0xd44: /* PRF1.  */
267         return 0x00000200;
268     case 0xd48: /* DFR0.  */
269         return 0x00100000;
270     case 0xd4c: /* AFR0.  */
271         return 0x00000000;
272     case 0xd50: /* MMFR0.  */
273         return 0x00000030;
274     case 0xd54: /* MMFR1.  */
275         return 0x00000000;
276     case 0xd58: /* MMFR2.  */
277         return 0x00000000;
278     case 0xd5c: /* MMFR3.  */
279         return 0x00000000;
280     case 0xd60: /* ISAR0.  */
281         return 0x01141110;
282     case 0xd64: /* ISAR1.  */
283         return 0x02111000;
284     case 0xd68: /* ISAR2.  */
285         return 0x21112231;
286     case 0xd6c: /* ISAR3.  */
287         return 0x01111110;
288     case 0xd70: /* ISAR4.  */
289         return 0x01310102;
290     /* TODO: Implement debug registers.  */
291     default:
292         qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad read offset 0x%x\n", offset);
293         return 0;
294     }
295 }
296 
297 static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value)
298 {
299     ARMCPU *cpu = s->cpu;
300     uint32_t oldval;
301     switch (offset) {
302     case 0x10: /* SysTick Control and Status.  */
303         oldval = s->systick.control;
304         s->systick.control &= 0xfffffff8;
305         s->systick.control |= value & 7;
306         if ((oldval ^ value) & SYSTICK_ENABLE) {
307             int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
308             if (value & SYSTICK_ENABLE) {
309                 if (s->systick.tick) {
310                     s->systick.tick += now;
311                     timer_mod(s->systick.timer, s->systick.tick);
312                 } else {
313                     systick_reload(s, 1);
314                 }
315             } else {
316                 timer_del(s->systick.timer);
317                 s->systick.tick -= now;
318                 if (s->systick.tick < 0)
319                   s->systick.tick = 0;
320             }
321         } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
322             /* This is a hack. Force the timer to be reloaded
323                when the reference clock is changed.  */
324             systick_reload(s, 1);
325         }
326         break;
327     case 0x14: /* SysTick Reload Value.  */
328         s->systick.reload = value;
329         break;
330     case 0x18: /* SysTick Current Value.  Writes reload the timer.  */
331         systick_reload(s, 1);
332         s->systick.control &= ~SYSTICK_COUNTFLAG;
333         break;
334     case 0xd04: /* Interrupt Control State.  */
335         if (value & (1 << 31)) {
336             armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
337         }
338         if (value & (1 << 28)) {
339             armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
340         } else if (value & (1 << 27)) {
341             s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
342             gic_update(&s->gic);
343         }
344         if (value & (1 << 26)) {
345             armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
346         } else if (value & (1 << 25)) {
347             s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
348             gic_update(&s->gic);
349         }
350         break;
351     case 0xd08: /* Vector Table Offset.  */
352         cpu->env.v7m.vecbase = value & 0xffffff80;
353         break;
354     case 0xd0c: /* Application Interrupt/Reset Control.  */
355         if ((value >> 16) == 0x05fa) {
356             if (value & 4) {
357                 qemu_irq_pulse(s->sysresetreq);
358             }
359             if (value & 2) {
360                 qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n");
361             }
362             if (value & 1) {
363                 qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n");
364             }
365             if (value & 0x700) {
366                 qemu_log_mask(LOG_UNIMP, "PRIGROUP unimplemented\n");
367             }
368         }
369         break;
370     case 0xd10: /* System Control.  */
371         /* TODO: Implement control registers.  */
372         qemu_log_mask(LOG_UNIMP, "NVIC: SCR unimplemented\n");
373         break;
374     case 0xd14: /* Configuration Control.  */
375         /* Enforce RAZ/WI on reserved and must-RAZ/WI bits */
376         value &= (R_V7M_CCR_STKALIGN_MASK |
377                   R_V7M_CCR_BFHFNMIGN_MASK |
378                   R_V7M_CCR_DIV_0_TRP_MASK |
379                   R_V7M_CCR_UNALIGN_TRP_MASK |
380                   R_V7M_CCR_USERSETMPEND_MASK |
381                   R_V7M_CCR_NONBASETHRDENA_MASK);
382 
383         cpu->env.v7m.ccr = value;
384         break;
385     case 0xd24: /* System Handler Control.  */
386         /* TODO: Real hardware allows you to set/clear the active bits
387            under some circumstances.  We don't implement this.  */
388         s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
389         s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
390         s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
391         break;
392     case 0xd28: /* Configurable Fault Status.  */
393         cpu->env.v7m.cfsr &= ~value; /* W1C */
394         break;
395     case 0xd2c: /* Hard Fault Status.  */
396         cpu->env.v7m.hfsr &= ~value; /* W1C */
397         break;
398     case 0xd30: /* Debug Fault Status.  */
399         cpu->env.v7m.dfsr &= ~value; /* W1C */
400         break;
401     case 0xd34: /* Mem Manage Address.  */
402         cpu->env.v7m.mmfar = value;
403         return;
404     case 0xd38: /* Bus Fault Address.  */
405         cpu->env.v7m.bfar = value;
406         return;
407     case 0xd3c: /* Aux Fault Status.  */
408         qemu_log_mask(LOG_UNIMP,
409                       "NVIC: Aux fault status registers unimplemented\n");
410         break;
411     case 0xf00: /* Software Triggered Interrupt Register */
412         /* user mode can only write to STIR if CCR.USERSETMPEND permits it */
413         if ((value & 0x1ff) < s->num_irq &&
414             (arm_current_el(&cpu->env) ||
415              (cpu->env.v7m.ccr & R_V7M_CCR_USERSETMPEND_MASK))) {
416             gic_set_pending_private(&s->gic, 0, value & 0x1ff);
417         }
418         break;
419     default:
420         qemu_log_mask(LOG_GUEST_ERROR,
421                       "NVIC: Bad write offset 0x%x\n", offset);
422     }
423 }
424 
425 static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr,
426                                  unsigned size)
427 {
428     nvic_state *s = (nvic_state *)opaque;
429     uint32_t offset = addr;
430     int i;
431     uint32_t val;
432 
433     switch (offset) {
434     case 0xd18 ... 0xd23: /* System Handler Priority.  */
435         val = 0;
436         for (i = 0; i < size; i++) {
437             val |= s->gic.priority1[(offset - 0xd14) + i][0] << (i * 8);
438         }
439         return val;
440     case 0xfe0 ... 0xfff: /* ID.  */
441         if (offset & 3) {
442             return 0;
443         }
444         return nvic_id[(offset - 0xfe0) >> 2];
445     }
446     if (size == 4) {
447         return nvic_readl(s, offset);
448     }
449     qemu_log_mask(LOG_GUEST_ERROR,
450                   "NVIC: Bad read of size %d at offset 0x%x\n", size, offset);
451     return 0;
452 }
453 
454 static void nvic_sysreg_write(void *opaque, hwaddr addr,
455                               uint64_t value, unsigned size)
456 {
457     nvic_state *s = (nvic_state *)opaque;
458     uint32_t offset = addr;
459     int i;
460 
461     switch (offset) {
462     case 0xd18 ... 0xd23: /* System Handler Priority.  */
463         for (i = 0; i < size; i++) {
464             s->gic.priority1[(offset - 0xd14) + i][0] =
465                 (value >> (i * 8)) & 0xff;
466         }
467         gic_update(&s->gic);
468         return;
469     }
470     if (size == 4) {
471         nvic_writel(s, offset, value);
472         return;
473     }
474     qemu_log_mask(LOG_GUEST_ERROR,
475                   "NVIC: Bad write of size %d at offset 0x%x\n", size, offset);
476 }
477 
478 static const MemoryRegionOps nvic_sysreg_ops = {
479     .read = nvic_sysreg_read,
480     .write = nvic_sysreg_write,
481     .endianness = DEVICE_NATIVE_ENDIAN,
482 };
483 
484 static const VMStateDescription vmstate_nvic = {
485     .name = "armv7m_nvic",
486     .version_id = 1,
487     .minimum_version_id = 1,
488     .fields = (VMStateField[]) {
489         VMSTATE_UINT32(systick.control, nvic_state),
490         VMSTATE_UINT32(systick.reload, nvic_state),
491         VMSTATE_INT64(systick.tick, nvic_state),
492         VMSTATE_TIMER_PTR(systick.timer, nvic_state),
493         VMSTATE_END_OF_LIST()
494     }
495 };
496 
497 static void armv7m_nvic_reset(DeviceState *dev)
498 {
499     nvic_state *s = NVIC(dev);
500     NVICClass *nc = NVIC_GET_CLASS(s);
501     nc->parent_reset(dev);
502     /* Common GIC reset resets to disabled; the NVIC doesn't have
503      * per-CPU interfaces so mark our non-existent CPU interface
504      * as enabled by default, and with a priority mask which allows
505      * all interrupts through.
506      */
507     s->gic.cpu_ctlr[0] = GICC_CTLR_EN_GRP0;
508     s->gic.priority_mask[0] = 0x100;
509     /* The NVIC as a whole is always enabled. */
510     s->gic.ctlr = 1;
511     systick_reset(s);
512 }
513 
514 static void armv7m_nvic_realize(DeviceState *dev, Error **errp)
515 {
516     nvic_state *s = NVIC(dev);
517     NVICClass *nc = NVIC_GET_CLASS(s);
518     Error *local_err = NULL;
519 
520     s->cpu = ARM_CPU(qemu_get_cpu(0));
521     assert(s->cpu);
522     /* The NVIC always has only one CPU */
523     s->gic.num_cpu = 1;
524     /* Tell the common code we're an NVIC */
525     s->gic.revision = 0xffffffff;
526     s->num_irq = s->gic.num_irq;
527     nc->parent_realize(dev, &local_err);
528     if (local_err) {
529         error_propagate(errp, local_err);
530         return;
531     }
532     gic_init_irqs_and_distributor(&s->gic);
533     /* The NVIC and system controller register area looks like this:
534      *  0..0xff : system control registers, including systick
535      *  0x100..0xcff : GIC-like registers
536      *  0xd00..0xfff : system control registers
537      * We use overlaying to put the GIC like registers
538      * over the top of the system control register region.
539      */
540     memory_region_init(&s->container, OBJECT(s), "nvic", 0x1000);
541     /* The system register region goes at the bottom of the priority
542      * stack as it covers the whole page.
543      */
544     memory_region_init_io(&s->sysregmem, OBJECT(s), &nvic_sysreg_ops, s,
545                           "nvic_sysregs", 0x1000);
546     memory_region_add_subregion(&s->container, 0, &s->sysregmem);
547     /* Alias the GIC region so we can get only the section of it
548      * we need, and layer it on top of the system register region.
549      */
550     memory_region_init_alias(&s->gic_iomem_alias, OBJECT(s),
551                              "nvic-gic", &s->gic.iomem,
552                              0x100, 0xc00);
553     memory_region_add_subregion_overlap(&s->container, 0x100,
554                                         &s->gic_iomem_alias, 1);
555     /* Map the whole thing into system memory at the location required
556      * by the v7M architecture.
557      */
558     memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container);
559     s->systick.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s);
560 }
561 
562 static void armv7m_nvic_instance_init(Object *obj)
563 {
564     /* We have a different default value for the num-irq property
565      * than our superclass. This function runs after qdev init
566      * has set the defaults from the Property array and before
567      * any user-specified property setting, so just modify the
568      * value in the GICState struct.
569      */
570     GICState *s = ARM_GIC_COMMON(obj);
571     DeviceState *dev = DEVICE(obj);
572     nvic_state *nvic = NVIC(obj);
573     /* The ARM v7m may have anything from 0 to 496 external interrupt
574      * IRQ lines. We default to 64. Other boards may differ and should
575      * set the num-irq property appropriately.
576      */
577     s->num_irq = 64;
578     qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1);
579 }
580 
581 static void armv7m_nvic_class_init(ObjectClass *klass, void *data)
582 {
583     NVICClass *nc = NVIC_CLASS(klass);
584     DeviceClass *dc = DEVICE_CLASS(klass);
585 
586     nc->parent_reset = dc->reset;
587     nc->parent_realize = dc->realize;
588     dc->vmsd  = &vmstate_nvic;
589     dc->reset = armv7m_nvic_reset;
590     dc->realize = armv7m_nvic_realize;
591 }
592 
593 static const TypeInfo armv7m_nvic_info = {
594     .name          = TYPE_NVIC,
595     .parent        = TYPE_ARM_GIC_COMMON,
596     .instance_init = armv7m_nvic_instance_init,
597     .instance_size = sizeof(nvic_state),
598     .class_init    = armv7m_nvic_class_init,
599     .class_size    = sizeof(NVICClass),
600 };
601 
602 static void armv7m_nvic_register_types(void)
603 {
604     type_register_static(&armv7m_nvic_info);
605 }
606 
607 type_init(armv7m_nvic_register_types)
608