symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/armv7m_nvic.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * ARM Nested Vectored Interrupt Controller
       
     3  *
       
     4  * Copyright (c) 2006-2007 CodeSourcery.
       
     5  * Written by Paul Brook
       
     6  *
       
     7  * This code is licenced 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 "hw.h"
       
    14 #include "qemu-timer.h"
       
    15 #include "arm-misc.h"
       
    16 
       
    17 /* 32 internal lines (16 used for system exceptions) plus 64 external
       
    18    interrupt lines.  */
       
    19 #define GIC_NIRQ 96
       
    20 #define NCPU 1
       
    21 #define NVIC 1
       
    22 
       
    23 /* Only a single "CPU" interface is present.  */
       
    24 static inline int
       
    25 gic_get_current_cpu(void)
       
    26 {
       
    27     return 0;
       
    28 }
       
    29 
       
    30 static uint32_t nvic_readl(void *opaque, uint32_t offset);
       
    31 static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
       
    32 
       
    33 #include "arm_gic.c"
       
    34 
       
    35 typedef struct {
       
    36     struct {
       
    37         uint32_t control;
       
    38         uint32_t reload;
       
    39         int64_t tick;
       
    40         QEMUTimer *timer;
       
    41     } systick;
       
    42     gic_state *gic;
       
    43 } nvic_state;
       
    44 
       
    45 /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
       
    46 #define SYSTICK_SCALE 1000ULL
       
    47 
       
    48 #define SYSTICK_ENABLE    (1 << 0)
       
    49 #define SYSTICK_TICKINT   (1 << 1)
       
    50 #define SYSTICK_CLKSOURCE (1 << 2)
       
    51 #define SYSTICK_COUNTFLAG (1 << 16)
       
    52 
       
    53 int system_clock_scale;
       
    54 
       
    55 /* Conversion factor from qemu timer to SysTick frequencies.  */
       
    56 static inline int64_t systick_scale(nvic_state *s)
       
    57 {
       
    58     if (s->systick.control & SYSTICK_CLKSOURCE)
       
    59         return system_clock_scale;
       
    60     else
       
    61         return 1000;
       
    62 }
       
    63 
       
    64 static void systick_reload(nvic_state *s, int reset)
       
    65 {
       
    66     if (reset)
       
    67         s->systick.tick = qemu_get_clock(vm_clock);
       
    68     s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
       
    69     qemu_mod_timer(s->systick.timer, s->systick.tick);
       
    70 }
       
    71 
       
    72 static void systick_timer_tick(void * opaque)
       
    73 {
       
    74     nvic_state *s = (nvic_state *)opaque;
       
    75     s->systick.control |= SYSTICK_COUNTFLAG;
       
    76     if (s->systick.control & SYSTICK_TICKINT) {
       
    77         /* Trigger the interrupt.  */
       
    78         armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
       
    79     }
       
    80     if (s->systick.reload == 0) {
       
    81         s->systick.control &= ~SYSTICK_ENABLE;
       
    82     } else {
       
    83         systick_reload(s, 0);
       
    84     }
       
    85 }
       
    86 
       
    87 /* The external routines use the hardware vector numbering, ie. the first
       
    88    IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */
       
    89 void armv7m_nvic_set_pending(void *opaque, int irq)
       
    90 {
       
    91     nvic_state *s = (nvic_state *)opaque;
       
    92     if (irq >= 16)
       
    93         irq += 16;
       
    94     gic_set_pending_private(s->gic, 0, irq);
       
    95 }
       
    96 
       
    97 /* Make pending IRQ active.  */
       
    98 int armv7m_nvic_acknowledge_irq(void *opaque)
       
    99 {
       
   100     nvic_state *s = (nvic_state *)opaque;
       
   101     uint32_t irq;
       
   102 
       
   103     irq = gic_acknowledge_irq(s->gic, 0);
       
   104     if (irq == 1023)
       
   105         cpu_abort(cpu_single_env, "Interrupt but no vector\n");
       
   106     if (irq >= 32)
       
   107         irq -= 16;
       
   108     return irq;
       
   109 }
       
   110 
       
   111 void armv7m_nvic_complete_irq(void *opaque, int irq)
       
   112 {
       
   113     nvic_state *s = (nvic_state *)opaque;
       
   114     if (irq >= 16)
       
   115         irq += 16;
       
   116     gic_complete_irq(s->gic, 0, irq);
       
   117 }
       
   118 
       
   119 static uint32_t nvic_readl(void *opaque, uint32_t offset)
       
   120 {
       
   121     nvic_state *s = (nvic_state *)opaque;
       
   122     uint32_t val;
       
   123     int irq;
       
   124 
       
   125     switch (offset) {
       
   126     case 4: /* Interrupt Control Type.  */
       
   127         return (GIC_NIRQ / 32) - 1;
       
   128     case 0x10: /* SysTick Control and Status.  */
       
   129         val = s->systick.control;
       
   130         s->systick.control &= ~SYSTICK_COUNTFLAG;
       
   131         return val;
       
   132     case 0x14: /* SysTick Reload Value.  */
       
   133         return s->systick.reload;
       
   134     case 0x18: /* SysTick Current Value.  */
       
   135         {
       
   136             int64_t t;
       
   137             if ((s->systick.control & SYSTICK_ENABLE) == 0)
       
   138                 return 0;
       
   139             t = qemu_get_clock(vm_clock);
       
   140             if (t >= s->systick.tick)
       
   141                 return 0;
       
   142             val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
       
   143             /* The interrupt in triggered when the timer reaches zero.
       
   144                However the counter is not reloaded until the next clock
       
   145                tick.  This is a hack to return zero during the first tick.  */
       
   146             if (val > s->systick.reload)
       
   147                 val = 0;
       
   148             return val;
       
   149         }
       
   150     case 0x1c: /* SysTick Calibration Value.  */
       
   151         return 10000;
       
   152     case 0xd00: /* CPUID Base.  */
       
   153         return cpu_single_env->cp15.c0_cpuid;
       
   154     case 0xd04: /* Interrypt Control State.  */
       
   155         /* VECTACTIVE */
       
   156         val = s->gic->running_irq[0];
       
   157         if (val == 1023) {
       
   158             val = 0;
       
   159         } else if (val >= 32) {
       
   160             val -= 16;
       
   161         }
       
   162         /* RETTOBASE */
       
   163         if (s->gic->running_irq[0] == 1023
       
   164                 || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
       
   165             val |= (1 << 11);
       
   166         }
       
   167         /* VECTPENDING */
       
   168         if (s->gic->current_pending[0] != 1023)
       
   169             val |= (s->gic->current_pending[0] << 12);
       
   170         /* ISRPENDING */
       
   171         for (irq = 32; irq < GIC_NIRQ; irq++) {
       
   172             if (s->gic->irq_state[irq].pending) {
       
   173                 val |= (1 << 22);
       
   174                 break;
       
   175             }
       
   176         }
       
   177         /* PENDSTSET */
       
   178         if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
       
   179             val |= (1 << 26);
       
   180         /* PENDSVSET */
       
   181         if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
       
   182             val |= (1 << 28);
       
   183         /* NMIPENDSET */
       
   184         if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
       
   185             val |= (1 << 31);
       
   186         return val;
       
   187     case 0xd08: /* Vector Table Offset.  */
       
   188         return cpu_single_env->v7m.vecbase;
       
   189     case 0xd0c: /* Application Interrupt/Reset Control.  */
       
   190         return 0xfa05000;
       
   191     case 0xd10: /* System Control.  */
       
   192         /* TODO: Implement SLEEPONEXIT.  */
       
   193         return 0;
       
   194     case 0xd14: /* Configuration Control.  */
       
   195         /* TODO: Implement Configuration Control bits.  */
       
   196         return 0;
       
   197     case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
       
   198         irq = offset - 0xd14;
       
   199         val = 0;
       
   200         val = s->gic->priority1[irq++][0];
       
   201         val = s->gic->priority1[irq++][0] << 8;
       
   202         val = s->gic->priority1[irq++][0] << 16;
       
   203         val = s->gic->priority1[irq][0] << 24;
       
   204         return val;
       
   205     case 0xd24: /* System Handler Status.  */
       
   206         val = 0;
       
   207         if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
       
   208         if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
       
   209         if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
       
   210         if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
       
   211         if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
       
   212         if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
       
   213         if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
       
   214         if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
       
   215         if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
       
   216         if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
       
   217         if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
       
   218         if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
       
   219         if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
       
   220         if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
       
   221         return val;
       
   222     case 0xd28: /* Configurable Fault Status.  */
       
   223         /* TODO: Implement Fault Status.  */
       
   224         cpu_abort(cpu_single_env,
       
   225                   "Not implemented: Configurable Fault Status.");
       
   226         return 0;
       
   227     case 0xd2c: /* Hard Fault Status.  */
       
   228     case 0xd30: /* Debug Fault Status.  */
       
   229     case 0xd34: /* Mem Manage Address.  */
       
   230     case 0xd38: /* Bus Fault Address.  */
       
   231     case 0xd3c: /* Aux Fault Status.  */
       
   232         /* TODO: Implement fault status registers.  */
       
   233         goto bad_reg;
       
   234     case 0xd40: /* PFR0.  */
       
   235         return 0x00000030;
       
   236     case 0xd44: /* PRF1.  */
       
   237         return 0x00000200;
       
   238     case 0xd48: /* DFR0.  */
       
   239         return 0x00100000;
       
   240     case 0xd4c: /* AFR0.  */
       
   241         return 0x00000000;
       
   242     case 0xd50: /* MMFR0.  */
       
   243         return 0x00000030;
       
   244     case 0xd54: /* MMFR1.  */
       
   245         return 0x00000000;
       
   246     case 0xd58: /* MMFR2.  */
       
   247         return 0x00000000;
       
   248     case 0xd5c: /* MMFR3.  */
       
   249         return 0x00000000;
       
   250     case 0xd60: /* ISAR0.  */
       
   251         return 0x01141110;
       
   252     case 0xd64: /* ISAR1.  */
       
   253         return 0x02111000;
       
   254     case 0xd68: /* ISAR2.  */
       
   255         return 0x21112231;
       
   256     case 0xd6c: /* ISAR3.  */
       
   257         return 0x01111110;
       
   258     case 0xd70: /* ISAR4.  */
       
   259         return 0x01310102;
       
   260     /* TODO: Implement debug registers.  */
       
   261     default:
       
   262     bad_reg:
       
   263         cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
       
   264     }
       
   265 }
       
   266 
       
   267 static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
       
   268 {
       
   269     nvic_state *s = (nvic_state *)opaque;
       
   270     uint32_t oldval;
       
   271     switch (offset) {
       
   272     case 0x10: /* SysTick Control and Status.  */
       
   273         oldval = s->systick.control;
       
   274         s->systick.control &= 0xfffffff8;
       
   275         s->systick.control |= value & 7;
       
   276         if ((oldval ^ value) & SYSTICK_ENABLE) {
       
   277             int64_t now = qemu_get_clock(vm_clock);
       
   278             if (value & SYSTICK_ENABLE) {
       
   279                 if (s->systick.tick) {
       
   280                     s->systick.tick += now;
       
   281                     qemu_mod_timer(s->systick.timer, s->systick.tick);
       
   282                 } else {
       
   283                     systick_reload(s, 1);
       
   284                 }
       
   285             } else {
       
   286                 qemu_del_timer(s->systick.timer);
       
   287                 s->systick.tick -= now;
       
   288                 if (s->systick.tick < 0)
       
   289                   s->systick.tick = 0;
       
   290             }
       
   291         } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
       
   292             /* This is a hack. Force the timer to be reloaded
       
   293                when the reference clock is changed.  */
       
   294             systick_reload(s, 1);
       
   295         }
       
   296         break;
       
   297     case 0x14: /* SysTick Reload Value.  */
       
   298         s->systick.reload = value;
       
   299         break;
       
   300     case 0x18: /* SysTick Current Value.  Writes reload the timer.  */
       
   301         systick_reload(s, 1);
       
   302         s->systick.control &= ~SYSTICK_COUNTFLAG;
       
   303         break;
       
   304     case 0xd04: /* Interrupt Control State.  */
       
   305         if (value & (1 << 31)) {
       
   306             armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
       
   307         }
       
   308         if (value & (1 << 28)) {
       
   309             armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
       
   310         } else if (value & (1 << 27)) {
       
   311             s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
       
   312             gic_update(s->gic);
       
   313         }
       
   314         if (value & (1 << 26)) {
       
   315             armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
       
   316         } else if (value & (1 << 25)) {
       
   317             s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
       
   318             gic_update(s->gic);
       
   319         }
       
   320         break;
       
   321     case 0xd08: /* Vector Table Offset.  */
       
   322         cpu_single_env->v7m.vecbase = value & 0xffffff80;
       
   323         break;
       
   324     case 0xd0c: /* Application Interrupt/Reset Control.  */
       
   325         if ((value >> 16) == 0x05fa) {
       
   326             if (value & 2) {
       
   327                 cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
       
   328             }
       
   329             if (value & 5) {
       
   330                 cpu_abort(cpu_single_env, "System reset");
       
   331             }
       
   332         }
       
   333         break;
       
   334     case 0xd10: /* System Control.  */
       
   335     case 0xd14: /* Configuration Control.  */
       
   336         /* TODO: Implement control registers.  */
       
   337         goto bad_reg;
       
   338     case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
       
   339         {
       
   340             int irq;
       
   341             irq = offset - 0xd14;
       
   342             s->gic->priority1[irq++][0] = value & 0xff;
       
   343             s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
       
   344             s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
       
   345             s->gic->priority1[irq][0] = (value >> 24) & 0xff;
       
   346             gic_update(s->gic);
       
   347         }
       
   348         break;
       
   349     case 0xd24: /* System Handler Control.  */
       
   350         /* TODO: Real hardware allows you to set/clear the active bits
       
   351            under some circumstances.  We don't implement this.  */
       
   352         s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
       
   353         s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
       
   354         s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
       
   355         break;
       
   356     case 0xd28: /* Configurable Fault Status.  */
       
   357     case 0xd2c: /* Hard Fault Status.  */
       
   358     case 0xd30: /* Debug Fault Status.  */
       
   359     case 0xd34: /* Mem Manage Address.  */
       
   360     case 0xd38: /* Bus Fault Address.  */
       
   361     case 0xd3c: /* Aux Fault Status.  */
       
   362         goto bad_reg;
       
   363     default:
       
   364     bad_reg:
       
   365         cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
       
   366     }
       
   367 }
       
   368 
       
   369 static void nvic_save(QEMUFile *f, void *opaque)
       
   370 {
       
   371     nvic_state *s = (nvic_state *)opaque;
       
   372 
       
   373     qemu_put_be32(f, s->systick.control);
       
   374     qemu_put_be32(f, s->systick.reload);
       
   375     qemu_put_be64(f, s->systick.tick);
       
   376     qemu_put_timer(f, s->systick.timer);
       
   377 }
       
   378 
       
   379 static int nvic_load(QEMUFile *f, void *opaque, int version_id)
       
   380 {
       
   381     nvic_state *s = (nvic_state *)opaque;
       
   382 
       
   383     if (version_id != 1)
       
   384         return -EINVAL;
       
   385 
       
   386     s->systick.control = qemu_get_be32(f);
       
   387     s->systick.reload = qemu_get_be32(f);
       
   388     s->systick.tick = qemu_get_be64(f);
       
   389     qemu_get_timer(f, s->systick.timer);
       
   390 
       
   391     return 0;
       
   392 }
       
   393 
       
   394 qemu_irq *armv7m_nvic_init(CPUState *env)
       
   395 {
       
   396     nvic_state *s;
       
   397     qemu_irq *parent;
       
   398 
       
   399     parent = arm_pic_init_cpu(env);
       
   400     s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
       
   401     s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
       
   402     s->gic->nvic = s;
       
   403     s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
       
   404     if (env->v7m.nvic)
       
   405         cpu_abort(env, "CPU can only have one NVIC\n");
       
   406     env->v7m.nvic = s;
       
   407     register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
       
   408     return s->gic->in;
       
   409 }