• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "migration/qemu-file.h"
13 #include "hw/arm/pic.h"
14 #include "hw/android/goldfish/device.h"
15 #include "hw/irq.h"
16 #include "hw/hw.h"
17 
18 enum {
19     INTERRUPT_STATUS        = 0x00, // number of pending interrupts
20     INTERRUPT_NUMBER        = 0x04,
21     INTERRUPT_DISABLE_ALL   = 0x08,
22     INTERRUPT_DISABLE       = 0x0c,
23     INTERRUPT_ENABLE        = 0x10
24 };
25 
26 struct goldfish_int_state {
27     struct goldfish_device dev;
28     uint32_t level;
29     uint32_t pending_count;
30     uint32_t irq_enabled;
31     uint32_t fiq_enabled;
32     qemu_irq parent_irq;
33     qemu_irq parent_fiq;
34 };
35 
36 #define  GOLDFISH_INT_SAVE_VERSION  1
37 
38 #define  QFIELD_STRUCT  struct goldfish_int_state
39 QFIELD_BEGIN(goldfish_int_fields)
QFIELD_INT32(level)40     QFIELD_INT32(level),
41     QFIELD_INT32(pending_count),
42     QFIELD_INT32(irq_enabled),
43     QFIELD_INT32(fiq_enabled),
44 QFIELD_END
45 
46 static void goldfish_int_save(QEMUFile*  f, void*  opaque)
47 {
48     struct goldfish_int_state*  s = opaque;
49 
50     qemu_put_struct(f, goldfish_int_fields, s);
51 }
52 
goldfish_int_load(QEMUFile * f,void * opaque,int version_id)53 static int  goldfish_int_load(QEMUFile*  f, void*  opaque, int  version_id)
54 {
55     struct goldfish_int_state*  s = opaque;
56 
57     if (version_id != GOLDFISH_INT_SAVE_VERSION)
58         return -1;
59 
60     return qemu_get_struct(f, goldfish_int_fields, s);
61 }
62 
goldfish_int_update(struct goldfish_int_state * s)63 static void goldfish_int_update(struct goldfish_int_state *s)
64 {
65     uint32_t flags;
66 
67     flags = (s->level & s->irq_enabled);
68     qemu_set_irq(s->parent_irq, flags != 0);
69 
70     flags = (s->level & s->fiq_enabled);
71     qemu_set_irq(s->parent_fiq, flags != 0);
72 }
73 
goldfish_int_set_irq(void * opaque,int irq,int level)74 static void goldfish_int_set_irq(void *opaque, int irq, int level)
75 {
76     struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
77     uint32_t mask = (1U << irq);
78 
79     if(level) {
80         if(!(s->level & mask)) {
81             if(s->irq_enabled & mask)
82                 s->pending_count++;
83             s->level |= mask;
84         }
85     }
86     else {
87         if(s->level & mask) {
88             if(s->irq_enabled & mask)
89                 s->pending_count--;
90             s->level &= ~mask;
91         }
92     }
93     goldfish_int_update(s);
94 }
95 
goldfish_int_read(void * opaque,hwaddr offset)96 static uint32_t goldfish_int_read(void *opaque, hwaddr offset)
97 {
98     struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
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,hwaddr offset,uint32_t value)118 static void goldfish_int_write(void *opaque, hwaddr offset, uint32_t value)
119 {
120     struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
121     uint32_t mask = (1U << value);
122 
123     switch (offset) {
124         case INTERRUPT_DISABLE_ALL:
125             s->pending_count = 0;
126             s->level = 0;
127             break;
128 
129         case INTERRUPT_DISABLE:
130             if(s->irq_enabled & mask) {
131                 if(s->level & mask)
132                     s->pending_count--;
133                 s->irq_enabled &= ~mask;
134             }
135             break;
136         case INTERRUPT_ENABLE:
137             if(!(s->irq_enabled & mask)) {
138                 s->irq_enabled |= mask;
139                 if(s->level & mask)
140                     s->pending_count++;
141             }
142             break;
143 
144     default:
145         cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset);
146         return;
147     }
148     goldfish_int_update(s);
149 }
150 
151 static CPUReadMemoryFunc *goldfish_int_readfn[] = {
152     goldfish_int_read,
153     goldfish_int_read,
154     goldfish_int_read
155 };
156 
157 static CPUWriteMemoryFunc *goldfish_int_writefn[] = {
158     goldfish_int_write,
159     goldfish_int_write,
160     goldfish_int_write
161 };
162 
goldfish_interrupt_init(uint32_t base,qemu_irq parent_irq,qemu_irq parent_fiq)163 qemu_irq*  goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
164 {
165     int ret;
166     struct goldfish_int_state *s;
167     qemu_irq*  qi;
168 
169     s = g_malloc0(sizeof(*s));
170     qi = qemu_allocate_irqs(goldfish_int_set_irq, s, GFD_MAX_IRQ);
171     s->dev.name = "goldfish_interrupt_controller";
172     s->dev.id = -1;
173     s->dev.base = base;
174     s->dev.size = 0x1000;
175     s->parent_irq = parent_irq;
176     s->parent_fiq = parent_fiq;
177 
178     ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
179     if(ret) {
180         g_free(s);
181         return NULL;
182     }
183 
184     register_savevm(NULL,
185                     "goldfish_int",
186                     0,
187                     GOLDFISH_INT_SAVE_VERSION,
188                     goldfish_int_save,
189                     goldfish_int_load,
190                     s);
191 
192     return qi;
193 }
194 
195