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