Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / s3c24xx_irq.c
blobe805f81e2db7dd06fb275538b1f20f315726182e
1 /* hw/s3c24xx_irq.c
3 * Samsung S3C24XX IRQ controller emulation
5 * Copyright 2009 Daniel Silverstone and Vincent Sanders
7 * Copyright 2010, 2013 Stefan Weil
9 * This file is under the terms of the GNU General Public License Version 2.
12 #include "qemu/osdep.h"
13 #include "hw/hw.h"
14 #include "exec/address-spaces.h" /* get_system_memory */
16 #include "s3c24xx.h"
18 /* IRQ request status RW WORD */
19 #define S3C_IRQ_SRCPND 0
20 /* Interrupt mode control WR WORD */
21 #define S3C_IRQ_INTMOD 1
22 /* Interrupt mask control RW WORD */
23 #define S3C_IRQ_INTMSK 2
24 /* IRQ priority control WR WORD */
25 #define S3C_IRQ_PRIORITY 3
26 /* Interrupt request status RW WORD */
27 #define S3C_IRQ_INTPND 4
28 /* Interrupt request source offset RO WORD */
29 #define S3C_IRQ_OFFSET 5
30 /* Sub-source pending RW WORD */
31 #define S3C_IRQ_SUBSRCPND 6
32 /* Interrupt sub-mask RW WORD */
33 #define S3C_IRQ_INTSUBMSK 7
35 /* Interrupt controller state */
36 struct s3c24xx_irq_state_s {
37 MemoryRegion mmio;
38 ARMCPU *cpu;
40 qemu_irq *irqs;
42 uint32_t irq_main_level, irq_subsrc_level;
43 uint32_t irq_reg[8];
47 /* Take the status of the srcpnd register, percolate it through, raise to CPU
48 * if necessary
50 static void
51 s3c24xx_percolate_interrupt(struct s3c24xx_irq_state_s *s)
53 uint32_t ints = (s->irq_reg[S3C_IRQ_SRCPND] & ~s->irq_reg[S3C_IRQ_INTMSK]);
54 int fsb = ctz32(ints) + 1;
56 /* TODO: Priority encoder could go here */
57 if (ints & s->irq_reg[S3C_IRQ_INTMOD]) {
58 /* Detected a FIQ */
59 cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_FIQ);
60 return;
61 } else {
62 /* No FIQ here today */
63 cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_FIQ);
66 /* No FIQ, check for a normal IRQ */
67 if (fsb) {
68 if ((s->irq_reg[S3C_IRQ_INTPND] == 0) ||
69 (s->irq_reg[S3C_IRQ_INTPND] > 1<<(fsb-1))) {
70 /* Current INTPND is lower priority than fsb of ints (or empty) */
71 s->irq_reg[S3C_IRQ_INTPND] = 1<<(fsb-1);
72 s->irq_reg[S3C_IRQ_OFFSET] = fsb-1;
74 } else {
75 /* No FSB, thus no IRQ, thus nothing to do yet */
78 if (s->irq_reg[S3C_IRQ_INTPND] != 0) {
79 cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
80 } else {
81 cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
85 static void
86 s3c24xx_percolate_subsrc_interrupt(struct s3c24xx_irq_state_s *s)
88 uint32_t ints;
90 s->irq_reg[S3C_IRQ_SRCPND] |= s->irq_main_level;
91 s->irq_reg[S3C_IRQ_SUBSRCPND] |= s->irq_subsrc_level;
93 ints = (s->irq_reg[S3C_IRQ_SUBSRCPND] &
94 ~s->irq_reg[S3C_IRQ_INTSUBMSK]);
96 /* If UART0 has asserted, raise that */
97 if (ints & 0x7) {
98 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<28);
101 /* Ditto UART1 */
102 if (ints & 0x7<<3)
103 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<23);
105 /* Ditto UART2 */
106 if (ints & 0x7<<6)
107 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<15);
109 /* And percolate it through */
110 s3c24xx_percolate_interrupt(s);
113 static void s3c24xx_irq_write(void *opaque, hwaddr addr_,
114 uint64_t value, unsigned size)
116 struct s3c24xx_irq_state_s *s = opaque;
117 int addr = (addr_ >> 2) & 7;
119 if (addr == S3C_IRQ_SRCPND ||
120 addr == S3C_IRQ_INTPND ||
121 addr == S3C_IRQ_SUBSRCPND) {
122 s->irq_reg[addr] &= ~value;
123 } else {
124 s->irq_reg[addr] = value;
127 /* Start at the subsrc irqs and percolate from there */
128 s3c24xx_percolate_subsrc_interrupt(s);
131 static uint64_t s3c24xx_irq_read(void *opaque, hwaddr addr_,
132 unsigned size)
134 struct s3c24xx_irq_state_s *s = opaque;
135 int addr = (addr_ >> 2) & 0x7;
137 return s->irq_reg[addr];
140 static const MemoryRegionOps s3c24xx_irq_ops = {
141 .read = s3c24xx_irq_read,
142 .write = s3c24xx_irq_write,
143 .endianness = DEVICE_NATIVE_ENDIAN,
144 .valid = {
145 .min_access_size = 1,
146 .max_access_size = 4
150 static void
151 s3c24xx_irq_set_interrupt_level(struct s3c24xx_irq_state_s *s, int irq_num, int level, int set_level)
153 if (level) {
154 if (set_level)
155 s->irq_main_level |= 1<<irq_num;
156 s->irq_reg[S3C_IRQ_SRCPND] |= 1<<irq_num;
157 } else {
158 s->irq_main_level &= ~(1<<irq_num);
159 s->irq_reg[S3C_IRQ_SRCPND] &= ~(1<<irq_num);
161 s3c24xx_percolate_subsrc_interrupt(s);
164 static void
165 s3c24xx_irq_set_subsrc_interrupt_level(struct s3c24xx_irq_state_s *s, int irq_num, int level, int set_level)
167 if (level) {
168 if (set_level)
169 s->irq_subsrc_level |= 1<<irq_num;
170 s->irq_reg[S3C_IRQ_SUBSRCPND] |= 1<<irq_num;
171 } else {
172 s->irq_subsrc_level &= ~(1<<irq_num);
173 s->irq_reg[S3C_IRQ_SUBSRCPND] &= ~(1<<irq_num);
175 s3c24xx_percolate_subsrc_interrupt(s);
178 static void
179 s3c24xx_irq_handler(void *opaque, int _n, int level)
181 struct s3c24xx_irq_state_s *s = opaque;
182 int irq_num = _n % 32;
183 int is_subsrc = (_n & 32)?1:0;
184 int is_level = (_n & 64)?1:0;
186 if (is_subsrc == 0)
187 s3c24xx_irq_set_interrupt_level(s, irq_num, level, is_level);
188 else
189 s3c24xx_irq_set_subsrc_interrupt_level(s, irq_num, level, is_level);
192 static void s3c24xx_irq_save(QEMUFile *f, void *opaque)
194 struct s3c24xx_irq_state_s *s = opaque;
195 int i;
197 for (i = 0; i < 8; i ++)
198 qemu_put_be32s(f, &s->irq_reg[i]);
201 static int s3c24xx_irq_load(QEMUFile *f, void *opaque, int version_id)
203 struct s3c24xx_irq_state_s *s = opaque;
204 int i;
206 for (i = 0; i < 8; i ++)
207 qemu_get_be32s(f, &s->irq_reg[i]);
209 return 0;
212 struct s3c24xx_irq_state_s *
213 s3c24xx_irq_init(S3CState *soc, hwaddr base_addr)
215 struct s3c24xx_irq_state_s *s = g_new0(struct s3c24xx_irq_state_s, 1);
217 /* Samsung S3C24XX IRQ registration. */
218 memory_region_init_io(&s->mmio, OBJECT(s), &s3c24xx_irq_ops, s,
219 "s3c24xx.irq", 8 * 4);
220 memory_region_add_subregion(get_system_memory(), base_addr, &s->mmio);
221 register_savevm(NULL, "s3c24xx_irq", 0, 0, s3c24xx_irq_save, s3c24xx_irq_load, s);
223 s->cpu = soc->cpu;
225 /* Set up registers to power on values */
226 s->irq_reg[S3C_IRQ_SRCPND] = 0x00;
227 s->irq_reg[S3C_IRQ_INTMOD] = 0x00;
228 s->irq_reg[S3C_IRQ_INTMSK] = 0xFFFFFFFF;
229 s->irq_reg[S3C_IRQ_PRIORITY] = 0x7F;
230 s->irq_reg[S3C_IRQ_INTPND] = 0x00;
231 s->irq_reg[S3C_IRQ_OFFSET] = 0x00;
232 s->irq_reg[S3C_IRQ_SUBSRCPND] = 0x00;
233 s->irq_reg[S3C_IRQ_INTSUBMSK] = 0x7FF;
235 /* Allocate the interrupts and return them. All 64 potential ones.
236 * We return them doubled up because the latter half are level where
237 * the former half are edge.
239 s->irqs = qemu_allocate_irqs(s3c24xx_irq_handler, s, 128);
241 return s;
244 /* get the qemu interrupt from an irq number */
245 qemu_irq
246 s3c24xx_get_irq(struct s3c24xx_irq_state_s *s, unsigned inum)
248 assert(inum < 128);
249 return s->irqs[inum];