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