set SHELL
[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 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 }