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
99 switch (offset) {
100 case INTERRUPT_STATUS: /* IRQ_STATUS */
101 return s->pending_count;
102 case INTERRUPT_NUMBER: {
103 int i;
104 uint32_t pending = s->level & s->irq_enabled;
105 for(i = 0; i < 32; i++) {
106 if(pending & (1U << i))
107 return i;
108 }
109 return 0;
110 }
111 default:
112 cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset);
113 return 0;
114 }
115 }
116
goldfish_int_write(void * opaque,target_phys_addr_t offset,uint32_t value)117 static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
118 {
119 struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
120 uint32_t mask = (1U << value);
121
122 switch (offset) {
123 case INTERRUPT_DISABLE_ALL:
124 s->pending_count = 0;
125 s->level = 0;
126 break;
127
128 case INTERRUPT_DISABLE:
129 if(s->irq_enabled & mask) {
130 if(s->level & mask)
131 s->pending_count--;
132 s->irq_enabled &= ~mask;
133 }
134 break;
135 case INTERRUPT_ENABLE:
136 if(!(s->irq_enabled & mask)) {
137 s->irq_enabled |= mask;
138 if(s->level & mask)
139 s->pending_count++;
140 }
141 break;
142
143 default:
144 cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset);
145 return;
146 }
147 goldfish_int_update(s);
148 }
149
150 static CPUReadMemoryFunc *goldfish_int_readfn[] = {
151 goldfish_int_read,
152 goldfish_int_read,
153 goldfish_int_read
154 };
155
156 static CPUWriteMemoryFunc *goldfish_int_writefn[] = {
157 goldfish_int_write,
158 goldfish_int_write,
159 goldfish_int_write
160 };
161
goldfish_interrupt_init(uint32_t base,qemu_irq parent_irq,qemu_irq parent_fiq)162 qemu_irq* goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
163 {
164 int ret;
165 struct goldfish_int_state *s;
166 qemu_irq* qi;
167
168 s = qemu_mallocz(sizeof(*s));
169 qi = qemu_allocate_irqs(goldfish_int_set_irq, s, GFD_MAX_IRQ);
170 s->dev.name = "goldfish_interrupt_controller";
171 s->dev.id = -1;
172 s->dev.base = base;
173 s->dev.size = 0x1000;
174 s->parent_irq = parent_irq;
175 s->parent_fiq = parent_fiq;
176
177 ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
178 if(ret) {
179 qemu_free(s);
180 return NULL;
181 }
182
183 register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION,
184 goldfish_int_save, goldfish_int_load, s);
185
186 return qi;
187 }
188
189