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