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"
14 #include "exec/address-spaces.h" /* get_system_memory */
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
{
42 uint32_t irq_main_level
, irq_subsrc_level
;
47 /* Take the status of the srcpnd register, percolate it through, raise to CPU
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
]) {
59 cpu_interrupt(CPU(s
->cpu
), CPU_INTERRUPT_FIQ
);
62 /* No FIQ here today */
63 cpu_reset_interrupt(CPU(s
->cpu
), CPU_INTERRUPT_FIQ
);
66 /* No FIQ, check for a normal IRQ */
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;
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
);
81 cpu_reset_interrupt(CPU(s
->cpu
), CPU_INTERRUPT_HARD
);
86 s3c24xx_percolate_subsrc_interrupt(struct s3c24xx_irq_state_s
*s
)
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 */
98 s
->irq_reg
[S3C_IRQ_SRCPND
] |= (1<<28);
103 s
->irq_reg
[S3C_IRQ_SRCPND
] |= (1<<23);
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
;
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_
,
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
,
145 .min_access_size
= 1,
151 s3c24xx_irq_set_interrupt_level(struct s3c24xx_irq_state_s
*s
, int irq_num
, int level
, int set_level
)
155 s
->irq_main_level
|= 1<<irq_num
;
156 s
->irq_reg
[S3C_IRQ_SRCPND
] |= 1<<irq_num
;
158 s
->irq_main_level
&= ~(1<<irq_num
);
159 s
->irq_reg
[S3C_IRQ_SRCPND
] &= ~(1<<irq_num
);
161 s3c24xx_percolate_subsrc_interrupt(s
);
165 s3c24xx_irq_set_subsrc_interrupt_level(struct s3c24xx_irq_state_s
*s
, int irq_num
, int level
, int set_level
)
169 s
->irq_subsrc_level
|= 1<<irq_num
;
170 s
->irq_reg
[S3C_IRQ_SUBSRCPND
] |= 1<<irq_num
;
172 s
->irq_subsrc_level
&= ~(1<<irq_num
);
173 s
->irq_reg
[S3C_IRQ_SUBSRCPND
] &= ~(1<<irq_num
);
175 s3c24xx_percolate_subsrc_interrupt(s
);
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;
187 s3c24xx_irq_set_interrupt_level(s
, irq_num
, level
, is_level
);
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
;
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
;
206 for (i
= 0; i
< 8; i
++)
207 qemu_get_be32s(f
, &s
->irq_reg
[i
]);
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
);
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);
244 /* get the qemu interrupt from an irq number */
246 s3c24xx_get_irq(struct s3c24xx_irq_state_s
*s
, unsigned inum
)
249 return s
->irqs
[inum
];