Home | History | Annotate | Download | only in goldfish
      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)
     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 
     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 
     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 
     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 
     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 
    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 
    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