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