Merge tag 'v2.10.0-rc0'
[qemu/ar7.git] / hw / arm / s3c24xx_irq.c
blob091353fe4b45ecc1fbbafabe2df4cf75f850a433
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 "cpu.h"
14 #include "hw/hw.h"
15 #include "exec/address-spaces.h" /* get_system_memory */
16 #include "migration/register.h" /* register_savevm_live */
18 #include "s3c24xx.h"
20 /* IRQ request status RW WORD */
21 #define S3C_IRQ_SRCPND 0
22 /* Interrupt mode control WR WORD */
23 #define S3C_IRQ_INTMOD 1
24 /* Interrupt mask control RW WORD */
25 #define S3C_IRQ_INTMSK 2
26 /* IRQ priority control WR WORD */
27 #define S3C_IRQ_PRIORITY 3
28 /* Interrupt request status RW WORD */
29 #define S3C_IRQ_INTPND 4
30 /* Interrupt request source offset RO WORD */
31 #define S3C_IRQ_OFFSET 5
32 /* Sub-source pending RW WORD */
33 #define S3C_IRQ_SUBSRCPND 6
34 /* Interrupt sub-mask RW WORD */
35 #define S3C_IRQ_INTSUBMSK 7
37 /* Interrupt controller state */
38 struct s3c24xx_irq_state_s {
39 MemoryRegion mmio;
40 ARMCPU *cpu;
42 qemu_irq *irqs;
44 uint32_t irq_main_level, irq_subsrc_level;
45 uint32_t irq_reg[8];
49 /* Take the status of the srcpnd register, percolate it through, raise to CPU
50 * if necessary
52 static void
53 s3c24xx_percolate_interrupt(struct s3c24xx_irq_state_s *s)
55 uint32_t ints = (s->irq_reg[S3C_IRQ_SRCPND] & ~s->irq_reg[S3C_IRQ_INTMSK]);
56 int fsb = ctz32(ints) + 1;
58 /* TODO: Priority encoder could go here */
59 if (ints & s->irq_reg[S3C_IRQ_INTMOD]) {
60 /* Detected a FIQ */
61 cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_FIQ);
62 return;
63 } else {
64 /* No FIQ here today */
65 cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_FIQ);
68 /* No FIQ, check for a normal IRQ */
69 if (fsb) {
70 if ((s->irq_reg[S3C_IRQ_INTPND] == 0) ||
71 (s->irq_reg[S3C_IRQ_INTPND] > 1<<(fsb-1))) {
72 /* Current INTPND is lower priority than fsb of ints (or empty) */
73 s->irq_reg[S3C_IRQ_INTPND] = 1<<(fsb-1);
74 s->irq_reg[S3C_IRQ_OFFSET] = fsb-1;
76 } else {
77 /* No FSB, thus no IRQ, thus nothing to do yet */
80 if (s->irq_reg[S3C_IRQ_INTPND] != 0) {
81 cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
82 } else {
83 cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD);
87 static void
88 s3c24xx_percolate_subsrc_interrupt(struct s3c24xx_irq_state_s *s)
90 uint32_t ints;
92 s->irq_reg[S3C_IRQ_SRCPND] |= s->irq_main_level;
93 s->irq_reg[S3C_IRQ_SUBSRCPND] |= s->irq_subsrc_level;
95 ints = (s->irq_reg[S3C_IRQ_SUBSRCPND] &
96 ~s->irq_reg[S3C_IRQ_INTSUBMSK]);
98 /* If UART0 has asserted, raise that */
99 if (ints & 0x7) {
100 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<28);
103 /* Ditto UART1 */
104 if (ints & 0x7<<3)
105 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<23);
107 /* Ditto UART2 */
108 if (ints & 0x7<<6)
109 s->irq_reg[S3C_IRQ_SRCPND] |= (1<<15);
111 /* And percolate it through */
112 s3c24xx_percolate_interrupt(s);
115 static void s3c24xx_irq_write(void *opaque, hwaddr addr_,
116 uint64_t value, unsigned size)
118 struct s3c24xx_irq_state_s *s = opaque;
119 int addr = (addr_ >> 2) & 7;
121 if (addr == S3C_IRQ_SRCPND ||
122 addr == S3C_IRQ_INTPND ||
123 addr == S3C_IRQ_SUBSRCPND) {
124 s->irq_reg[addr] &= ~value;
125 } else {
126 s->irq_reg[addr] = value;
129 /* Start at the subsrc irqs and percolate from there */
130 s3c24xx_percolate_subsrc_interrupt(s);
133 static uint64_t s3c24xx_irq_read(void *opaque, hwaddr addr_,
134 unsigned size)
136 struct s3c24xx_irq_state_s *s = opaque;
137 int addr = (addr_ >> 2) & 0x7;
139 return s->irq_reg[addr];
142 static const MemoryRegionOps s3c24xx_irq_ops = {
143 .read = s3c24xx_irq_read,
144 .write = s3c24xx_irq_write,
145 .endianness = DEVICE_NATIVE_ENDIAN,
146 .valid = {
147 .min_access_size = 1,
148 .max_access_size = 4
152 static void
153 s3c24xx_irq_set_interrupt_level(struct s3c24xx_irq_state_s *s, int irq_num, int level, int set_level)
155 if (level) {
156 if (set_level)
157 s->irq_main_level |= 1<<irq_num;
158 s->irq_reg[S3C_IRQ_SRCPND] |= 1<<irq_num;
159 } else {
160 s->irq_main_level &= ~(1<<irq_num);
161 s->irq_reg[S3C_IRQ_SRCPND] &= ~(1<<irq_num);
163 s3c24xx_percolate_subsrc_interrupt(s);
166 static void
167 s3c24xx_irq_set_subsrc_interrupt_level(struct s3c24xx_irq_state_s *s, int irq_num, int level, int set_level)
169 if (level) {
170 if (set_level)
171 s->irq_subsrc_level |= 1<<irq_num;
172 s->irq_reg[S3C_IRQ_SUBSRCPND] |= 1<<irq_num;
173 } else {
174 s->irq_subsrc_level &= ~(1<<irq_num);
175 s->irq_reg[S3C_IRQ_SUBSRCPND] &= ~(1<<irq_num);
177 s3c24xx_percolate_subsrc_interrupt(s);
180 static void
181 s3c24xx_irq_handler(void *opaque, int _n, int level)
183 struct s3c24xx_irq_state_s *s = opaque;
184 int irq_num = _n % 32;
185 int is_subsrc = (_n & 32)?1:0;
186 int is_level = (_n & 64)?1:0;
188 if (is_subsrc == 0)
189 s3c24xx_irq_set_interrupt_level(s, irq_num, level, is_level);
190 else
191 s3c24xx_irq_set_subsrc_interrupt_level(s, irq_num, level, is_level);
194 static void s3c24xx_irq_save(QEMUFile *f, void *opaque)
196 struct s3c24xx_irq_state_s *s = opaque;
197 int i;
199 for (i = 0; i < 8; i ++)
200 qemu_put_be32s(f, &s->irq_reg[i]);
203 static int s3c24xx_irq_load(QEMUFile *f, void *opaque, int version_id)
205 struct s3c24xx_irq_state_s *s = opaque;
206 int i;
208 for (i = 0; i < 8; i ++)
209 qemu_get_be32s(f, &s->irq_reg[i]);
211 return 0;
214 static SaveVMHandlers savevm_s3c24xx_irq = {
215 .save_state = s3c24xx_irq_save,
216 .load_state = s3c24xx_irq_load
219 struct s3c24xx_irq_state_s *
220 s3c24xx_irq_init(S3CState *soc, hwaddr base_addr)
222 struct s3c24xx_irq_state_s *s = g_new0(struct s3c24xx_irq_state_s, 1);
224 /* Samsung S3C24XX IRQ registration. */
225 memory_region_init_io(&s->mmio, OBJECT(s), &s3c24xx_irq_ops, s,
226 "s3c24xx.irq", 8 * 4);
227 memory_region_add_subregion(get_system_memory(), base_addr, &s->mmio);
228 register_savevm_live(NULL, "s3c24xx_irq", 0, 0, &savevm_s3c24xx_irq, s);
230 s->cpu = soc->cpu;
232 /* Set up registers to power on values */
233 s->irq_reg[S3C_IRQ_SRCPND] = 0x00;
234 s->irq_reg[S3C_IRQ_INTMOD] = 0x00;
235 s->irq_reg[S3C_IRQ_INTMSK] = 0xFFFFFFFF;
236 s->irq_reg[S3C_IRQ_PRIORITY] = 0x7F;
237 s->irq_reg[S3C_IRQ_INTPND] = 0x00;
238 s->irq_reg[S3C_IRQ_OFFSET] = 0x00;
239 s->irq_reg[S3C_IRQ_SUBSRCPND] = 0x00;
240 s->irq_reg[S3C_IRQ_INTSUBMSK] = 0x7FF;
242 /* Allocate the interrupts and return them. All 64 potential ones.
243 * We return them doubled up because the latter half are level where
244 * the former half are edge.
246 s->irqs = qemu_allocate_irqs(s3c24xx_irq_handler, s, 128);
248 return s;
251 /* get the qemu interrupt from an irq number */
252 qemu_irq
253 s3c24xx_get_irq(struct s3c24xx_irq_state_s *s, unsigned inum)
255 assert(inum < 128);
256 return s->irqs[inum];