2 * Arm PrimeCell PL190 Vector Interrupt Controller
4 * Copyright (c) 2006 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licenced under the GPL.
12 /* The number of virtual priority levels. 16 user vectors plus the
13 unvectored IRQ. Chained interrupts would require an additional level
16 #define PL190_NUM_PRIO 17
24 uint32_t default_addr
;
25 uint8_t vect_control
[16];
26 uint32_t vect_addr
[PL190_NUM_PRIO
];
27 /* Mask containing interrupts with higher priority than this one. */
28 uint32_t prio_mask
[PL190_NUM_PRIO
+ 1];
30 /* Current priority level. */
32 int prev_prio
[PL190_NUM_PRIO
];
37 static const unsigned char pl190_id
[] =
38 { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
40 static inline uint32_t pl190_irq_level(pl190_state
*s
)
42 return (s
->level
| s
->soft_level
) & s
->irq_enable
& ~s
->fiq_select
;
45 /* Update interrupts. */
46 static void pl190_update(pl190_state
*s
)
48 uint32_t level
= pl190_irq_level(s
);
51 set
= (level
& s
->prio_mask
[s
->priority
]) != 0;
52 qemu_set_irq(s
->irq
, set
);
53 set
= ((s
->level
| s
->soft_level
) & s
->fiq_select
) != 0;
54 qemu_set_irq(s
->fiq
, set
);
57 static void pl190_set_irq(void *opaque
, int irq
, int level
)
59 pl190_state
*s
= (pl190_state
*)opaque
;
62 s
->level
|= 1u << irq
;
64 s
->level
&= ~(1u << irq
);
68 static void pl190_update_vectors(pl190_state
*s
)
75 for (i
= 0; i
< 16; i
++)
77 s
->prio_mask
[i
] = mask
;
78 if (s
->vect_control
[i
] & 0x20)
80 n
= s
->vect_control
[i
] & 0x1f;
84 s
->prio_mask
[16] = mask
;
88 static uint32_t pl190_read(void *opaque
, target_phys_addr_t offset
)
90 pl190_state
*s
= (pl190_state
*)opaque
;
93 if (offset
>= 0xfe0 && offset
< 0x1000) {
94 return pl190_id
[(offset
- 0xfe0) >> 2];
96 if (offset
>= 0x100 && offset
< 0x140) {
97 return s
->vect_addr
[(offset
- 0x100) >> 2];
99 if (offset
>= 0x200 && offset
< 0x240) {
100 return s
->vect_control
[(offset
- 0x200) >> 2];
102 switch (offset
>> 2) {
103 case 0: /* IRQSTATUS */
104 return pl190_irq_level(s
);
105 case 1: /* FIQSATUS */
106 return (s
->level
| s
->soft_level
) & s
->fiq_select
;
107 case 2: /* RAWINTR */
108 return s
->level
| s
->soft_level
;
109 case 3: /* INTSELECT */
110 return s
->fiq_select
;
111 case 4: /* INTENABLE */
112 return s
->irq_enable
;
113 case 6: /* SOFTINT */
114 return s
->soft_level
;
115 case 8: /* PROTECTION */
117 case 12: /* VECTADDR */
118 /* Read vector address at the start of an ISR. Increases the
119 current priority level to that of the current interrupt. */
120 for (i
= 0; i
< s
->priority
; i
++)
122 if ((s
->level
| s
->soft_level
) & s
->prio_mask
[i
])
125 /* Reading this value with no pending interrupts is undefined.
126 We return the default address. */
127 if (i
== PL190_NUM_PRIO
)
128 return s
->vect_addr
[16];
131 s
->prev_prio
[i
] = s
->priority
;
135 return s
->vect_addr
[s
->priority
];
136 case 13: /* DEFVECTADDR */
137 return s
->vect_addr
[16];
139 hw_error("pl190_read: Bad offset %x\n", (int)offset
);
144 static void pl190_write(void *opaque
, target_phys_addr_t offset
, uint32_t val
)
146 pl190_state
*s
= (pl190_state
*)opaque
;
148 if (offset
>= 0x100 && offset
< 0x140) {
149 s
->vect_addr
[(offset
- 0x100) >> 2] = val
;
150 pl190_update_vectors(s
);
153 if (offset
>= 0x200 && offset
< 0x240) {
154 s
->vect_control
[(offset
- 0x200) >> 2] = val
;
155 pl190_update_vectors(s
);
158 switch (offset
>> 2) {
160 /* This is a readonly register, but linux tries to write to it
161 anyway. Ignore the write. */
163 case 3: /* INTSELECT */
166 case 4: /* INTENABLE */
167 s
->irq_enable
|= val
;
169 case 5: /* INTENCLEAR */
170 s
->irq_enable
&= ~val
;
172 case 6: /* SOFTINT */
173 s
->soft_level
|= val
;
175 case 7: /* SOFTINTCLEAR */
176 s
->soft_level
&= ~val
;
178 case 8: /* PROTECTION */
179 /* TODO: Protection (supervisor only access) is not implemented. */
180 s
->protected = val
& 1;
182 case 12: /* VECTADDR */
183 /* Restore the previous priority level. The value written is
185 if (s
->priority
< PL190_NUM_PRIO
)
186 s
->priority
= s
->prev_prio
[s
->priority
];
188 case 13: /* DEFVECTADDR */
189 s
->default_addr
= val
;
191 case 0xc0: /* ITCR */
193 hw_error("pl190: Test mode not implemented\n");
197 hw_error("pl190_write: Bad offset %x\n", (int)offset
);
203 static CPUReadMemoryFunc
*pl190_readfn
[] = {
209 static CPUWriteMemoryFunc
*pl190_writefn
[] = {
215 static void pl190_reset(pl190_state
*s
)
219 for (i
= 0; i
< 16; i
++)
222 s
->vect_control
[i
] = 0;
224 s
->vect_addr
[16] = 0;
225 s
->prio_mask
[17] = 0xffffffff;
226 s
->priority
= PL190_NUM_PRIO
;
227 pl190_update_vectors(s
);
230 static void pl190_init(SysBusDevice
*dev
)
232 pl190_state
*s
= FROM_SYSBUS(pl190_state
, dev
);
235 iomemtype
= cpu_register_io_memory(0, pl190_readfn
,
237 sysbus_init_mmio(dev
, 0x1000, iomemtype
);
238 qdev_init_gpio_in(&dev
->qdev
, pl190_set_irq
, 32);
239 sysbus_init_irq(dev
, &s
->irq
);
240 sysbus_init_irq(dev
, &s
->fiq
);
242 /* ??? Save/restore. */
245 static void pl190_register_devices(void)
247 sysbus_register_dev("pl190", sizeof(pl190_state
), pl190_init
);
250 device_init(pl190_register_devices
)