symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl190.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * Arm PrimeCell PL190 Vector Interrupt Controller
       
     3  *
       
     4  * Copyright (c) 2006 CodeSourcery.
       
     5  * Written by Paul Brook
       
     6  *
       
     7  * This code is licenced under the GPL.
       
     8  */
       
     9 
       
    10 #include "hw.h"
       
    11 #include "primecell.h"
       
    12 #include "arm-misc.h"
       
    13 
       
    14 /* The number of virtual priority levels.  16 user vectors plus the
       
    15    unvectored IRQ.  Chained interrupts would require an additional level
       
    16    if implemented.  */
       
    17 
       
    18 #define PL190_NUM_PRIO 17
       
    19 
       
    20 typedef struct {
       
    21     uint32_t level;
       
    22     uint32_t soft_level;
       
    23     uint32_t irq_enable;
       
    24     uint32_t fiq_select;
       
    25     uint32_t default_addr;
       
    26     uint8_t vect_control[16];
       
    27     uint32_t vect_addr[PL190_NUM_PRIO];
       
    28     /* Mask containing interrupts with higher priority than this one.  */
       
    29     uint32_t prio_mask[PL190_NUM_PRIO + 1];
       
    30     int protected;
       
    31     /* Current priority level.  */
       
    32     int priority;
       
    33     int prev_prio[PL190_NUM_PRIO];
       
    34     qemu_irq irq;
       
    35     qemu_irq fiq;
       
    36 } pl190_state;
       
    37 
       
    38 static const unsigned char pl190_id[] =
       
    39 { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
       
    40 
       
    41 static inline uint32_t pl190_irq_level(pl190_state *s)
       
    42 {
       
    43     return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
       
    44 }
       
    45 
       
    46 /* Update interrupts.  */
       
    47 static void pl190_update(pl190_state *s)
       
    48 {
       
    49     uint32_t level = pl190_irq_level(s);
       
    50     int set;
       
    51 
       
    52     set = (level & s->prio_mask[s->priority]) != 0;
       
    53     qemu_set_irq(s->irq, set);
       
    54     set = ((s->level | s->soft_level) & s->fiq_select) != 0;
       
    55     qemu_set_irq(s->fiq, set);
       
    56 }
       
    57 
       
    58 static void pl190_set_irq(void *opaque, int irq, int level)
       
    59 {
       
    60     pl190_state *s = (pl190_state *)opaque;
       
    61 
       
    62     if (level)
       
    63         s->level |= 1u << irq;
       
    64     else
       
    65         s->level &= ~(1u << irq);
       
    66     pl190_update(s);
       
    67 }
       
    68 
       
    69 static void pl190_update_vectors(pl190_state *s)
       
    70 {
       
    71     uint32_t mask;
       
    72     int i;
       
    73     int n;
       
    74 
       
    75     mask = 0;
       
    76     for (i = 0; i < 16; i++)
       
    77       {
       
    78         s->prio_mask[i] = mask;
       
    79         if (s->vect_control[i] & 0x20)
       
    80           {
       
    81             n = s->vect_control[i] & 0x1f;
       
    82             mask |= 1 << n;
       
    83           }
       
    84       }
       
    85     s->prio_mask[16] = mask;
       
    86     pl190_update(s);
       
    87 }
       
    88 
       
    89 static uint32_t pl190_read(void *opaque, target_phys_addr_t offset)
       
    90 {
       
    91     pl190_state *s = (pl190_state *)opaque;
       
    92     int i;
       
    93 
       
    94     if (offset >= 0xfe0 && offset < 0x1000) {
       
    95         return pl190_id[(offset - 0xfe0) >> 2];
       
    96     }
       
    97     if (offset >= 0x100 && offset < 0x140) {
       
    98         return s->vect_addr[(offset - 0x100) >> 2];
       
    99     }
       
   100     if (offset >= 0x200 && offset < 0x240) {
       
   101         return s->vect_control[(offset - 0x200) >> 2];
       
   102     }
       
   103     switch (offset >> 2) {
       
   104     case 0: /* IRQSTATUS */
       
   105         return pl190_irq_level(s);
       
   106     case 1: /* FIQSATUS */
       
   107         return (s->level | s->soft_level) & s->fiq_select;
       
   108     case 2: /* RAWINTR */
       
   109         return s->level | s->soft_level;
       
   110     case 3: /* INTSELECT */
       
   111         return s->fiq_select;
       
   112     case 4: /* INTENABLE */
       
   113         return s->irq_enable;
       
   114     case 6: /* SOFTINT */
       
   115         return s->soft_level;
       
   116     case 8: /* PROTECTION */
       
   117         return s->protected;
       
   118     case 12: /* VECTADDR */
       
   119         /* Read vector address at the start of an ISR.  Increases the
       
   120            current priority level to that of the current interrupt.  */
       
   121         for (i = 0; i < s->priority; i++)
       
   122           {
       
   123             if ((s->level | s->soft_level) & s->prio_mask[i])
       
   124               break;
       
   125           }
       
   126         /* Reading this value with no pending interrupts is undefined.
       
   127            We return the default address.  */
       
   128         if (i == PL190_NUM_PRIO)
       
   129           return s->vect_addr[16];
       
   130         if (i < s->priority)
       
   131           {
       
   132             s->prev_prio[i] = s->priority;
       
   133             s->priority = i;
       
   134             pl190_update(s);
       
   135           }
       
   136         return s->vect_addr[s->priority];
       
   137     case 13: /* DEFVECTADDR */
       
   138         return s->vect_addr[16];
       
   139     default:
       
   140         cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", (int)offset);
       
   141         return 0;
       
   142     }
       
   143 }
       
   144 
       
   145 static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val)
       
   146 {
       
   147     pl190_state *s = (pl190_state *)opaque;
       
   148 
       
   149     if (offset >= 0x100 && offset < 0x140) {
       
   150         s->vect_addr[(offset - 0x100) >> 2] = val;
       
   151         pl190_update_vectors(s);
       
   152         return;
       
   153     }
       
   154     if (offset >= 0x200 && offset < 0x240) {
       
   155         s->vect_control[(offset - 0x200) >> 2] = val;
       
   156         pl190_update_vectors(s);
       
   157         return;
       
   158     }
       
   159     switch (offset >> 2) {
       
   160     case 0: /* SELECT */
       
   161         /* This is a readonly register, but linux tries to write to it
       
   162            anyway.  Ignore the write.  */
       
   163         break;
       
   164     case 3: /* INTSELECT */
       
   165         s->fiq_select = val;
       
   166         break;
       
   167     case 4: /* INTENABLE */
       
   168         s->irq_enable |= val;
       
   169         break;
       
   170     case 5: /* INTENCLEAR */
       
   171         s->irq_enable &= ~val;
       
   172         break;
       
   173     case 6: /* SOFTINT */
       
   174         s->soft_level |= val;
       
   175         break;
       
   176     case 7: /* SOFTINTCLEAR */
       
   177         s->soft_level &= ~val;
       
   178         break;
       
   179     case 8: /* PROTECTION */
       
   180         /* TODO: Protection (supervisor only access) is not implemented.  */
       
   181         s->protected = val & 1;
       
   182         break;
       
   183     case 12: /* VECTADDR */
       
   184         /* Restore the previous priority level.  The value written is
       
   185            ignored.  */
       
   186         if (s->priority < PL190_NUM_PRIO)
       
   187             s->priority = s->prev_prio[s->priority];
       
   188         break;
       
   189     case 13: /* DEFVECTADDR */
       
   190         s->default_addr = val;
       
   191         break;
       
   192     case 0xc0: /* ITCR */
       
   193         if (val)
       
   194             cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n");
       
   195         break;
       
   196     default:
       
   197         cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", (int)offset);
       
   198         return;
       
   199     }
       
   200     pl190_update(s);
       
   201 }
       
   202 
       
   203 static CPUReadMemoryFunc *pl190_readfn[] = {
       
   204    pl190_read,
       
   205    pl190_read,
       
   206    pl190_read
       
   207 };
       
   208 
       
   209 static CPUWriteMemoryFunc *pl190_writefn[] = {
       
   210    pl190_write,
       
   211    pl190_write,
       
   212    pl190_write
       
   213 };
       
   214 
       
   215 static void pl190_reset(pl190_state *s)
       
   216 {
       
   217   int i;
       
   218 
       
   219   for (i = 0; i < 16; i++)
       
   220     {
       
   221       s->vect_addr[i] = 0;
       
   222       s->vect_control[i] = 0;
       
   223     }
       
   224   s->vect_addr[16] = 0;
       
   225   s->prio_mask[17] = 0xffffffff;
       
   226   s->priority = PL190_NUM_PRIO;
       
   227   pl190_update_vectors(s);
       
   228 }
       
   229 
       
   230 qemu_irq *pl190_init(uint32_t base, qemu_irq irq, qemu_irq fiq)
       
   231 {
       
   232     pl190_state *s;
       
   233     qemu_irq *qi;
       
   234     int iomemtype;
       
   235 
       
   236     s = (pl190_state *)qemu_mallocz(sizeof(pl190_state));
       
   237     iomemtype = cpu_register_io_memory(0, pl190_readfn,
       
   238                                        pl190_writefn, s);
       
   239     cpu_register_physical_memory(base, 0x00001000, iomemtype);
       
   240     qi = qemu_allocate_irqs(pl190_set_irq, s, 32);
       
   241     s->irq = irq;
       
   242     s->fiq = fiq;
       
   243     pl190_reset(s);
       
   244     /* ??? Save/restore.  */
       
   245     return qi;
       
   246 }