• 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 "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