1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12 #include "qemu_file.h"
13 #include "arm_pic.h"
14 #include "goldfish_device.h"
15 #include "irq.h"
16
17 enum {
18 INTERRUPT_STATUS = 0x00, // number of pending interrupts
19 INTERRUPT_NUMBER = 0x04,
20 INTERRUPT_DISABLE_ALL = 0x08,
21 INTERRUPT_DISABLE = 0x0c,
22 INTERRUPT_ENABLE = 0x10
23 };
24
25 struct goldfish_int_state {
26 struct goldfish_device dev;
27 uint32_t level;
28 uint32_t pending_count;
29 uint32_t irq_enabled;
30 uint32_t fiq_enabled;
31 qemu_irq parent_irq;
32 qemu_irq parent_fiq;
33 };
34
35 #define GOLDFISH_INT_SAVE_VERSION 1
36
37 #define QFIELD_STRUCT struct goldfish_int_state
38 QFIELD_BEGIN(goldfish_int_fields)
QFIELD_INT32(level)39 QFIELD_INT32(level),
40 QFIELD_INT32(pending_count),
41 QFIELD_INT32(irq_enabled),
42 QFIELD_INT32(fiq_enabled),
43 QFIELD_END
44
45 static void goldfish_int_save(QEMUFile* f, void* opaque)
46 {
47 struct goldfish_int_state* s = opaque;
48
49 qemu_put_struct(f, goldfish_int_fields, s);
50 }
51
goldfish_int_load(QEMUFile * f,void * opaque,int version_id)52 static int goldfish_int_load(QEMUFile* f, void* opaque, int version_id)
53 {
54 struct goldfish_int_state* s = opaque;
55
56 if (version_id != GOLDFISH_INT_SAVE_VERSION)
57 return -1;
58
59 return qemu_get_struct(f, goldfish_int_fields, s);
60 }
61
goldfish_int_update(struct goldfish_int_state * s)62 static void goldfish_int_update(struct goldfish_int_state *s)
63 {
64 uint32_t flags;
65
66 flags = (s->level & s->irq_enabled);
67 qemu_set_irq(s->parent_irq, flags != 0);
68
69 flags = (s->level & s->fiq_enabled);
70 qemu_set_irq(s->parent_fiq, flags != 0);
71 }
72
goldfish_int_set_irq(void * opaque,int irq,int level)73 static void goldfish_int_set_irq(void *opaque, int irq, int level)
74 {
75 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
76 uint32_t mask = (1U << irq);
77
78 if(level) {
79 if(!(s->level & mask)) {
80 if(s->irq_enabled & mask)
81 s->pending_count++;
82 s->level |= mask;
83 }
84 }
85 else {
86 if(s->level & mask) {
87 if(s->irq_enabled & mask)
88 s->pending_count--;
89 s->level &= ~mask;
90 }
91 }
92 goldfish_int_update(s);
93 }
94
goldfish_int_read(void * opaque,target_phys_addr_t offset)95 static uint32_t goldfish_int_read(void *opaque, target_phys_addr_t offset)
96 {
97 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
98 offset -= s->dev.base;
99
100 switch (offset) {
101 case INTERRUPT_STATUS: /* IRQ_STATUS */
102 return s->pending_count;
103 case INTERRUPT_NUMBER: {
104 int i;
105 uint32_t pending = s->level & s->irq_enabled;
106 for(i = 0; i < 32; i++) {
107 if(pending & (1U << i))
108 return i;
109 }
110 return 0;
111 }
112 default:
113 cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset);
114 return 0;
115 }
116 }
117
goldfish_int_write(void * opaque,target_phys_addr_t offset,uint32_t value)118 static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
119 {
120 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
121 uint32_t mask = (1U << value);
122 offset -= s->dev.base;
123
124 switch (offset) {
125 case INTERRUPT_DISABLE_ALL:
126 s->pending_count = 0;
127 s->level = 0;
128 break;
129
130 case INTERRUPT_DISABLE:
131 if(s->irq_enabled & mask) {
132 if(s->level & mask)
133 s->pending_count--;
134 s->irq_enabled &= ~mask;
135 }
136 break;
137 case INTERRUPT_ENABLE:
138 if(!(s->irq_enabled & mask)) {
139 s->irq_enabled |= mask;
140 if(s->level & mask)
141 s->pending_count++;
142 }
143 break;
144
145 default:
146 cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset);
147 return;
148 }
149 goldfish_int_update(s);
150 }
151
152 static CPUReadMemoryFunc *goldfish_int_readfn[] = {
153 goldfish_int_read,
154 goldfish_int_read,
155 goldfish_int_read
156 };
157
158 static CPUWriteMemoryFunc *goldfish_int_writefn[] = {
159 goldfish_int_write,
160 goldfish_int_write,
161 goldfish_int_write
162 };
163
goldfish_interrupt_init(uint32_t base,qemu_irq parent_irq,qemu_irq parent_fiq)164 qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
165 {
166 int ret;
167 struct goldfish_int_state *s;
168 qemu_irq* qi;
169
170 s = qemu_mallocz(sizeof(*s));
171 qi = qemu_allocate_irqs(goldfish_int_set_irq, s, 32);
172 s->dev.name = "goldfish_interrupt_controller";
173 s->dev.id = -1;
174 s->dev.base = base;
175 s->dev.size = 0x1000;
176 s->parent_irq = parent_irq;
177 s->parent_fiq = parent_fiq;
178
179 ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
180 if(ret) {
181 qemu_free(s);
182 return NULL;
183 }
184
185 register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION,
186 goldfish_int_save, goldfish_int_load, s);
187
188 return qi;
189 }
190
191