| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ARM Nested Vectored Interrupt Controller | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2006-2007 CodeSourcery. | 
					
						
							|  |  |  |  * Written by Paul Brook | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-06-26 12:21:35 +10:00
										 |  |  |  * This code is licensed under the GPL. | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * The ARMv7M System controller is fairly tightly tied in with the | 
					
						
							|  |  |  |  * NVIC.  Much of that is also implemented here. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | #include "sysbus.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-17 17:14:51 +00:00
										 |  |  | #include "qemu-timer.h"
 | 
					
						
							|  |  |  | #include "arm-misc.h"
 | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:17 +03:00
										 |  |  | #include "exec-memory.h"
 | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define NVIC 1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t nvic_readl(void *opaque, uint32_t offset); | 
					
						
							|  |  |  | static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "arm_gic.c"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     gic_state gic; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     struct { | 
					
						
							|  |  |  |         uint32_t control; | 
					
						
							|  |  |  |         uint32_t reload; | 
					
						
							|  |  |  |         int64_t tick; | 
					
						
							|  |  |  |         QEMUTimer *timer; | 
					
						
							|  |  |  |     } systick; | 
					
						
							| 
									
										
										
										
											2012-01-17 10:54:07 +00:00
										 |  |  |     uint32_t num_irq; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | } nvic_state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */ | 
					
						
							|  |  |  | #define SYSTICK_SCALE 1000ULL
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SYSTICK_ENABLE    (1 << 0)
 | 
					
						
							|  |  |  | #define SYSTICK_TICKINT   (1 << 1)
 | 
					
						
							|  |  |  | #define SYSTICK_CLKSOURCE (1 << 2)
 | 
					
						
							|  |  |  | #define SYSTICK_COUNTFLAG (1 << 16)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-17 19:04:14 +00:00
										 |  |  | int system_clock_scale; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-24 03:09:07 +00:00
										 |  |  | /* Conversion factor from qemu timer to SysTick frequencies.  */ | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | static inline int64_t systick_scale(nvic_state *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (s->systick.control & SYSTICK_CLKSOURCE) | 
					
						
							| 
									
										
										
										
											2007-11-24 03:09:07 +00:00
										 |  |  |         return system_clock_scale; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     else | 
					
						
							|  |  |  |         return 1000; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void systick_reload(nvic_state *s, int reset) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (reset) | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         s->systick.tick = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     s->systick.tick += (s->systick.reload + 1) * systick_scale(s); | 
					
						
							|  |  |  |     qemu_mod_timer(s->systick.timer, s->systick.tick); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void systick_timer_tick(void * opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     s->systick.control |= SYSTICK_COUNTFLAG; | 
					
						
							|  |  |  |     if (s->systick.control & SYSTICK_TICKINT) { | 
					
						
							|  |  |  |         /* Trigger the interrupt.  */ | 
					
						
							|  |  |  |         armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s->systick.reload == 0) { | 
					
						
							|  |  |  |         s->systick.control &= ~SYSTICK_ENABLE; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         systick_reload(s, 0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 11:39:09 +00:00
										 |  |  | static void systick_reset(nvic_state *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     s->systick.control = 0; | 
					
						
							|  |  |  |     s->systick.reload = 0; | 
					
						
							|  |  |  |     s->systick.tick = 0; | 
					
						
							|  |  |  |     qemu_del_timer(s->systick.timer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | /* The external routines use the hardware vector numbering, ie. the first
 | 
					
						
							|  |  |  |    IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */ | 
					
						
							|  |  |  | void armv7m_nvic_set_pending(void *opaque, int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     if (irq >= 16) | 
					
						
							|  |  |  |         irq += 16; | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     gic_set_pending_private(&s->gic, 0, irq); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Make pending IRQ active.  */ | 
					
						
							|  |  |  | int armv7m_nvic_acknowledge_irq(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     uint32_t irq; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     irq = gic_acknowledge_irq(&s->gic, 0); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     if (irq == 1023) | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("Interrupt but no vector\n"); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     if (irq >= 32) | 
					
						
							|  |  |  |         irq -= 16; | 
					
						
							|  |  |  |     return irq; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void armv7m_nvic_complete_irq(void *opaque, int irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     if (irq >= 16) | 
					
						
							|  |  |  |         irq += 16; | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     gic_complete_irq(&s->gic, 0, irq); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t nvic_readl(void *opaque, uint32_t offset) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     uint32_t val; | 
					
						
							|  |  |  |     int irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (offset) { | 
					
						
							|  |  |  |     case 4: /* Interrupt Control Type.  */ | 
					
						
							| 
									
										
										
										
											2012-01-17 10:54:07 +00:00
										 |  |  |         return (s->num_irq / 32) - 1; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     case 0x10: /* SysTick Control and Status.  */ | 
					
						
							|  |  |  |         val = s->systick.control; | 
					
						
							|  |  |  |         s->systick.control &= ~SYSTICK_COUNTFLAG; | 
					
						
							|  |  |  |         return val; | 
					
						
							|  |  |  |     case 0x14: /* SysTick Reload Value.  */ | 
					
						
							|  |  |  |         return s->systick.reload; | 
					
						
							|  |  |  |     case 0x18: /* SysTick Current Value.  */ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int64_t t; | 
					
						
							|  |  |  |             if ((s->systick.control & SYSTICK_ENABLE) == 0) | 
					
						
							|  |  |  |                 return 0; | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |             t = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             if (t >= s->systick.tick) | 
					
						
							|  |  |  |                 return 0; | 
					
						
							|  |  |  |             val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1; | 
					
						
							|  |  |  |             /* The interrupt in triggered when the timer reaches zero.
 | 
					
						
							|  |  |  |                However the counter is not reloaded until the next clock | 
					
						
							|  |  |  |                tick.  This is a hack to return zero during the first tick.  */ | 
					
						
							|  |  |  |             if (val > s->systick.reload) | 
					
						
							|  |  |  |                 val = 0; | 
					
						
							|  |  |  |             return val; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     case 0x1c: /* SysTick Calibration Value.  */ | 
					
						
							|  |  |  |         return 10000; | 
					
						
							|  |  |  |     case 0xd00: /* CPUID Base.  */ | 
					
						
							|  |  |  |         return cpu_single_env->cp15.c0_cpuid; | 
					
						
							|  |  |  |     case 0xd04: /* Interrypt Control State.  */ | 
					
						
							|  |  |  |         /* VECTACTIVE */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         val = s->gic.running_irq[0]; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         if (val == 1023) { | 
					
						
							|  |  |  |             val = 0; | 
					
						
							|  |  |  |         } else if (val >= 32) { | 
					
						
							|  |  |  |             val -= 16; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* RETTOBASE */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.running_irq[0] == 1023 | 
					
						
							|  |  |  |                 || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) { | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             val |= (1 << 11); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* VECTPENDING */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.current_pending[0] != 1023) | 
					
						
							|  |  |  |             val |= (s->gic.current_pending[0] << 12); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         /* ISRPENDING */ | 
					
						
							| 
									
										
										
										
											2012-01-17 10:54:07 +00:00
										 |  |  |         for (irq = 32; irq < s->num_irq; irq++) { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |             if (s->gic.irq_state[irq].pending) { | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |                 val |= (1 << 22); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* PENDSTSET */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending) | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             val |= (1 << 26); | 
					
						
							|  |  |  |         /* PENDSVSET */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending) | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             val |= (1 << 28); | 
					
						
							|  |  |  |         /* NMIPENDSET */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending) | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             val |= (1 << 31); | 
					
						
							|  |  |  |         return val; | 
					
						
							|  |  |  |     case 0xd08: /* Vector Table Offset.  */ | 
					
						
							|  |  |  |         return cpu_single_env->v7m.vecbase; | 
					
						
							|  |  |  |     case 0xd0c: /* Application Interrupt/Reset Control.  */ | 
					
						
							|  |  |  |         return 0xfa05000; | 
					
						
							|  |  |  |     case 0xd10: /* System Control.  */ | 
					
						
							|  |  |  |         /* TODO: Implement SLEEPONEXIT.  */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case 0xd14: /* Configuration Control.  */ | 
					
						
							|  |  |  |         /* TODO: Implement Configuration Control bits.  */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */ | 
					
						
							|  |  |  |         irq = offset - 0xd14; | 
					
						
							|  |  |  |         val = 0; | 
					
						
							| 
									
										
										
										
											2010-05-02 18:38:10 +00:00
										 |  |  |         val |= s->gic.priority1[irq++][0]; | 
					
						
							|  |  |  |         val |= s->gic.priority1[irq++][0] << 8; | 
					
						
							|  |  |  |         val |= s->gic.priority1[irq++][0] << 16; | 
					
						
							|  |  |  |         val |= s->gic.priority1[irq][0] << 24; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         return val; | 
					
						
							|  |  |  |     case 0xd24: /* System Handler Status.  */ | 
					
						
							|  |  |  |         val = 0; | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17); | 
					
						
							|  |  |  |         if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         return val; | 
					
						
							|  |  |  |     case 0xd28: /* Configurable Fault Status.  */ | 
					
						
							|  |  |  |         /* TODO: Implement Fault Status.  */ | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("Not implemented: Configurable Fault Status."); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         return 0; | 
					
						
							|  |  |  |     case 0xd2c: /* Hard Fault Status.  */ | 
					
						
							|  |  |  |     case 0xd30: /* Debug Fault Status.  */ | 
					
						
							|  |  |  |     case 0xd34: /* Mem Manage Address.  */ | 
					
						
							|  |  |  |     case 0xd38: /* Bus Fault Address.  */ | 
					
						
							|  |  |  |     case 0xd3c: /* Aux Fault Status.  */ | 
					
						
							|  |  |  |         /* TODO: Implement fault status registers.  */ | 
					
						
							|  |  |  |         goto bad_reg; | 
					
						
							|  |  |  |     case 0xd40: /* PFR0.  */ | 
					
						
							|  |  |  |         return 0x00000030; | 
					
						
							|  |  |  |     case 0xd44: /* PRF1.  */ | 
					
						
							|  |  |  |         return 0x00000200; | 
					
						
							|  |  |  |     case 0xd48: /* DFR0.  */ | 
					
						
							|  |  |  |         return 0x00100000; | 
					
						
							|  |  |  |     case 0xd4c: /* AFR0.  */ | 
					
						
							|  |  |  |         return 0x00000000; | 
					
						
							|  |  |  |     case 0xd50: /* MMFR0.  */ | 
					
						
							|  |  |  |         return 0x00000030; | 
					
						
							|  |  |  |     case 0xd54: /* MMFR1.  */ | 
					
						
							|  |  |  |         return 0x00000000; | 
					
						
							|  |  |  |     case 0xd58: /* MMFR2.  */ | 
					
						
							|  |  |  |         return 0x00000000; | 
					
						
							|  |  |  |     case 0xd5c: /* MMFR3.  */ | 
					
						
							|  |  |  |         return 0x00000000; | 
					
						
							|  |  |  |     case 0xd60: /* ISAR0.  */ | 
					
						
							|  |  |  |         return 0x01141110; | 
					
						
							|  |  |  |     case 0xd64: /* ISAR1.  */ | 
					
						
							|  |  |  |         return 0x02111000; | 
					
						
							|  |  |  |     case 0xd68: /* ISAR2.  */ | 
					
						
							|  |  |  |         return 0x21112231; | 
					
						
							|  |  |  |     case 0xd6c: /* ISAR3.  */ | 
					
						
							|  |  |  |         return 0x01111110; | 
					
						
							|  |  |  |     case 0xd70: /* ISAR4.  */ | 
					
						
							|  |  |  |         return 0x01310102; | 
					
						
							|  |  |  |     /* TODO: Implement debug registers.  */ | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     bad_reg: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("NVIC: Bad read offset 0x%x\n", offset); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void nvic_writel(void *opaque, uint32_t offset, uint32_t value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = (nvic_state *)opaque; | 
					
						
							|  |  |  |     uint32_t oldval; | 
					
						
							|  |  |  |     switch (offset) { | 
					
						
							|  |  |  |     case 0x10: /* SysTick Control and Status.  */ | 
					
						
							|  |  |  |         oldval = s->systick.control; | 
					
						
							|  |  |  |         s->systick.control &= 0xfffffff8; | 
					
						
							|  |  |  |         s->systick.control |= value & 7; | 
					
						
							|  |  |  |         if ((oldval ^ value) & SYSTICK_ENABLE) { | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |             int64_t now = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             if (value & SYSTICK_ENABLE) { | 
					
						
							|  |  |  |                 if (s->systick.tick) { | 
					
						
							|  |  |  |                     s->systick.tick += now; | 
					
						
							|  |  |  |                     qemu_mod_timer(s->systick.timer, s->systick.tick); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     systick_reload(s, 1); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 qemu_del_timer(s->systick.timer); | 
					
						
							|  |  |  |                 s->systick.tick -= now; | 
					
						
							|  |  |  |                 if (s->systick.tick < 0) | 
					
						
							|  |  |  |                   s->systick.tick = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { | 
					
						
							|  |  |  |             /* This is a hack. Force the timer to be reloaded
 | 
					
						
							|  |  |  |                when the reference clock is changed.  */ | 
					
						
							|  |  |  |             systick_reload(s, 1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x14: /* SysTick Reload Value.  */ | 
					
						
							|  |  |  |         s->systick.reload = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x18: /* SysTick Current Value.  Writes reload the timer.  */ | 
					
						
							|  |  |  |         systick_reload(s, 1); | 
					
						
							|  |  |  |         s->systick.control &= ~SYSTICK_COUNTFLAG; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0xd04: /* Interrupt Control State.  */ | 
					
						
							|  |  |  |         if (value & (1 << 31)) { | 
					
						
							|  |  |  |             armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value & (1 << 28)) { | 
					
						
							|  |  |  |             armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); | 
					
						
							|  |  |  |         } else if (value & (1 << 27)) { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |             s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0; | 
					
						
							|  |  |  |             gic_update(&s->gic); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (value & (1 << 26)) { | 
					
						
							|  |  |  |             armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); | 
					
						
							|  |  |  |         } else if (value & (1 << 25)) { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |             s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0; | 
					
						
							|  |  |  |             gic_update(&s->gic); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0xd08: /* Vector Table Offset.  */ | 
					
						
							|  |  |  |         cpu_single_env->v7m.vecbase = value & 0xffffff80; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0xd0c: /* Application Interrupt/Reset Control.  */ | 
					
						
							|  |  |  |         if ((value >> 16) == 0x05fa) { | 
					
						
							|  |  |  |             if (value & 2) { | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |                 hw_error("VECTCLRACTIVE not implemented"); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |             if (value & 5) { | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |                 hw_error("System reset"); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0xd10: /* System Control.  */ | 
					
						
							|  |  |  |     case 0xd14: /* Configuration Control.  */ | 
					
						
							|  |  |  |         /* TODO: Implement control registers.  */ | 
					
						
							|  |  |  |         goto bad_reg; | 
					
						
							|  |  |  |     case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             int irq; | 
					
						
							|  |  |  |             irq = offset - 0xd14; | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |             s->gic.priority1[irq++][0] = value & 0xff; | 
					
						
							|  |  |  |             s->gic.priority1[irq++][0] = (value >> 8) & 0xff; | 
					
						
							|  |  |  |             s->gic.priority1[irq++][0] = (value >> 16) & 0xff; | 
					
						
							|  |  |  |             s->gic.priority1[irq][0] = (value >> 24) & 0xff; | 
					
						
							|  |  |  |             gic_update(&s->gic); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0xd24: /* System Handler Control.  */ | 
					
						
							|  |  |  |         /* TODO: Real hardware allows you to set/clear the active bits
 | 
					
						
							|  |  |  |            under some circumstances.  We don't implement this.  */ | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |         s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; | 
					
						
							|  |  |  |         s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; | 
					
						
							|  |  |  |         s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case 0xd28: /* Configurable Fault Status.  */ | 
					
						
							|  |  |  |     case 0xd2c: /* Hard Fault Status.  */ | 
					
						
							|  |  |  |     case 0xd30: /* Debug Fault Status.  */ | 
					
						
							|  |  |  |     case 0xd34: /* Mem Manage Address.  */ | 
					
						
							|  |  |  |     case 0xd38: /* Bus Fault Address.  */ | 
					
						
							|  |  |  |     case 0xd3c: /* Aux Fault Status.  */ | 
					
						
							|  |  |  |         goto bad_reg; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     bad_reg: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("NVIC: Bad write offset 0x%x\n", offset); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-02 02:17:33 +01:00
										 |  |  | static const VMStateDescription vmstate_nvic = { | 
					
						
							|  |  |  |     .name = "armv7m_nvic", | 
					
						
							|  |  |  |     .version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id_old = 1, | 
					
						
							|  |  |  |     .fields      = (VMStateField[]) { | 
					
						
							|  |  |  |         VMSTATE_UINT32(systick.control, nvic_state), | 
					
						
							|  |  |  |         VMSTATE_UINT32(systick.reload, nvic_state), | 
					
						
							|  |  |  |         VMSTATE_INT64(systick.tick, nvic_state), | 
					
						
							|  |  |  |         VMSTATE_TIMER(systick.timer, nvic_state), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2008-07-02 16:48:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-13 11:39:09 +00:00
										 |  |  | static void armv7m_nvic_reset(DeviceState *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); | 
					
						
							|  |  |  |     gic_reset(&s->gic.busdev.qdev); | 
					
						
							|  |  |  |     systick_reset(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-14 10:36:05 +02:00
										 |  |  | static int armv7m_nvic_init(SysBusDevice *dev) | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  |     nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-17 10:54:07 +00:00
										 |  |  |    /* note that for the M profile gic_init() takes the number of external
 | 
					
						
							|  |  |  |     * interrupt lines only. | 
					
						
							|  |  |  |     */ | 
					
						
							|  |  |  |     gic_init(&s->gic, s->num_irq); | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:17 +03:00
										 |  |  |     memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem); | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |     s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); | 
					
						
							| 
									
										
										
										
											2009-08-14 10:36:05 +02:00
										 |  |  |     return 0; | 
					
						
							| 
									
										
										
										
											2007-11-11 00:04:49 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  | static Property armv7m_nvic_properties[] = { | 
					
						
							|  |  |  |     /* The ARM v7m may have anything from 0 to 496 external interrupt
 | 
					
						
							|  |  |  |      * IRQ lines. We default to 64. Other boards may differ and should | 
					
						
							|  |  |  |      * set this property appropriately. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("num-irq", nvic_state, num_irq, 64), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | static void armv7m_nvic_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  |     SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sdc->init = armv7m_nvic_init; | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     dc->vmsd  = &vmstate_nvic; | 
					
						
							| 
									
										
										
										
											2012-04-13 11:39:09 +00:00
										 |  |  |     dc->reset = armv7m_nvic_reset; | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     dc->props = armv7m_nvic_properties; | 
					
						
							| 
									
										
										
										
											2012-01-24 13:12:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  | static TypeInfo armv7m_nvic_info = { | 
					
						
							|  |  |  |     .name          = "armv7m_nvic", | 
					
						
							|  |  |  |     .parent        = TYPE_SYS_BUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(nvic_state), | 
					
						
							|  |  |  |     .class_init    = armv7m_nvic_class_init, | 
					
						
							| 
									
										
										
										
											2012-01-17 10:54:07 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | static void armv7m_nvic_register_types(void) | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-12-07 21:34:16 -06:00
										 |  |  |     type_register_static(&armv7m_nvic_info); | 
					
						
							| 
									
										
										
										
											2009-05-14 22:35:08 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 15:20:55 +01:00
										 |  |  | type_init(armv7m_nvic_register_types) |