qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

exynos4210_combiner.c (11961B)


      1 /*
      2  * Samsung exynos4210 Interrupt Combiner
      3  *
      4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
      5  * All rights reserved.
      6  *
      7  * Evgeny Voevodin <e.voevodin@samsung.com>
      8  *
      9  * This program is free software; you can redistribute it and/or modify it
     10  * under the terms of the GNU General Public License as published by the
     11  * Free Software Foundation; either version 2 of the License, or (at your
     12  * option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     17  * See the GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License along
     20  * with this program; if not, see <http://www.gnu.org/licenses/>.
     21  */
     22 
     23 /*
     24  * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines
     25  * IRQ sources into groups and provides signal output to GIC from each group. It
     26  * is driven by common mask and enable/disable logic. Take a note that not all
     27  * IRQs are passed to GIC through Combiner.
     28  */
     29 
     30 #include "qemu/osdep.h"
     31 #include "hw/sysbus.h"
     32 #include "migration/vmstate.h"
     33 #include "qemu/module.h"
     34 #include "hw/intc/exynos4210_combiner.h"
     35 #include "hw/arm/exynos4210.h"
     36 #include "hw/hw.h"
     37 #include "hw/irq.h"
     38 #include "hw/qdev-properties.h"
     39 #include "qom/object.h"
     40 
     41 //#define DEBUG_COMBINER
     42 
     43 #ifdef DEBUG_COMBINER
     44 #define DPRINTF(fmt, ...) \
     45         do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \
     46                 ## __VA_ARGS__); } while (0)
     47 #else
     48 #define DPRINTF(fmt, ...) do {} while (0)
     49 #endif
     50 
     51 #define IIC_REGION_SIZE    0x108         /* Size of memory mapped region */
     52 
     53 static const VMStateDescription vmstate_exynos4210_combiner_group_state = {
     54     .name = "exynos4210.combiner.groupstate",
     55     .version_id = 1,
     56     .minimum_version_id = 1,
     57     .fields = (VMStateField[]) {
     58         VMSTATE_UINT8(src_mask, CombinerGroupState),
     59         VMSTATE_UINT8(src_pending, CombinerGroupState),
     60         VMSTATE_END_OF_LIST()
     61     }
     62 };
     63 
     64 static const VMStateDescription vmstate_exynos4210_combiner = {
     65     .name = "exynos4210.combiner",
     66     .version_id = 1,
     67     .minimum_version_id = 1,
     68     .fields = (VMStateField[]) {
     69         VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0,
     70                 vmstate_exynos4210_combiner_group_state, CombinerGroupState),
     71         VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState,
     72                 IIC_REGSET_SIZE),
     73         VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2),
     74         VMSTATE_UINT32(external, Exynos4210CombinerState),
     75         VMSTATE_END_OF_LIST()
     76     }
     77 };
     78 
     79 static uint64_t
     80 exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size)
     81 {
     82     struct Exynos4210CombinerState *s =
     83             (struct Exynos4210CombinerState *)opaque;
     84     uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
     85                                    get a start of corresponding group quad */
     86     uint32_t grp_quad_base_n;    /* Base of group quad */
     87     uint32_t reg_n;              /* Register number inside the quad */
     88     uint32_t val;
     89 
     90     req_quad_base_n = offset >> 4;
     91     grp_quad_base_n = req_quad_base_n << 2;
     92     reg_n = (offset - (req_quad_base_n << 4)) >> 2;
     93 
     94     if (req_quad_base_n >= IIC_NGRP) {
     95         /* Read of ICIPSR register */
     96         return s->icipsr[reg_n];
     97     }
     98 
     99     val = 0;
    100 
    101     switch (reg_n) {
    102     /* IISTR */
    103     case 2:
    104         val |= s->group[grp_quad_base_n].src_pending;
    105         val |= s->group[grp_quad_base_n + 1].src_pending << 8;
    106         val |= s->group[grp_quad_base_n + 2].src_pending << 16;
    107         val |= s->group[grp_quad_base_n + 3].src_pending << 24;
    108         break;
    109     /* IIMSR */
    110     case 3:
    111         val |= s->group[grp_quad_base_n].src_mask &
    112         s->group[grp_quad_base_n].src_pending;
    113         val |= (s->group[grp_quad_base_n + 1].src_mask &
    114                 s->group[grp_quad_base_n + 1].src_pending) << 8;
    115         val |= (s->group[grp_quad_base_n + 2].src_mask &
    116                 s->group[grp_quad_base_n + 2].src_pending) << 16;
    117         val |= (s->group[grp_quad_base_n + 3].src_mask &
    118                 s->group[grp_quad_base_n + 3].src_pending) << 24;
    119         break;
    120     default:
    121         if (offset >> 2 >= IIC_REGSET_SIZE) {
    122             hw_error("exynos4210.combiner: overflow of reg_set by 0x"
    123                     TARGET_FMT_plx "offset\n", offset);
    124         }
    125         val = s->reg_set[offset >> 2];
    126     }
    127     return val;
    128 }
    129 
    130 static void exynos4210_combiner_update(void *opaque, uint8_t group_n)
    131 {
    132     struct Exynos4210CombinerState *s =
    133             (struct Exynos4210CombinerState *)opaque;
    134 
    135     /* Send interrupt if needed */
    136     if (s->group[group_n].src_mask & s->group[group_n].src_pending) {
    137 #ifdef DEBUG_COMBINER
    138         if (group_n != 26) {
    139             /* skip uart */
    140             DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
    141         }
    142 #endif
    143 
    144         /* Set Combiner interrupt pending status after masking */
    145         if (group_n >= 32) {
    146             s->icipsr[1] |= 1 << (group_n - 32);
    147         } else {
    148             s->icipsr[0] |= 1 << group_n;
    149         }
    150 
    151         qemu_irq_raise(s->output_irq[group_n]);
    152     } else {
    153 #ifdef DEBUG_COMBINER
    154         if (group_n != 26) {
    155             /* skip uart */
    156             DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n);
    157         }
    158 #endif
    159 
    160         /* Set Combiner interrupt pending status after masking */
    161         if (group_n >= 32) {
    162             s->icipsr[1] &= ~(1 << (group_n - 32));
    163         } else {
    164             s->icipsr[0] &= ~(1 << group_n);
    165         }
    166 
    167         qemu_irq_lower(s->output_irq[group_n]);
    168     }
    169 }
    170 
    171 static void exynos4210_combiner_write(void *opaque, hwaddr offset,
    172         uint64_t val, unsigned size)
    173 {
    174     struct Exynos4210CombinerState *s =
    175             (struct Exynos4210CombinerState *)opaque;
    176     uint32_t req_quad_base_n;    /* Base of registers quad. Multiply it by 4 and
    177                                    get a start of corresponding group quad */
    178     uint32_t grp_quad_base_n;    /* Base of group quad */
    179     uint32_t reg_n;              /* Register number inside the quad */
    180 
    181     req_quad_base_n = offset >> 4;
    182     grp_quad_base_n = req_quad_base_n << 2;
    183     reg_n = (offset - (req_quad_base_n << 4)) >> 2;
    184 
    185     if (req_quad_base_n >= IIC_NGRP) {
    186         hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    187                 TARGET_FMT_plx "\n", offset);
    188         return;
    189     }
    190 
    191     if (reg_n > 1) {
    192         hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    193                 TARGET_FMT_plx "\n", offset);
    194         return;
    195     }
    196 
    197     if (offset >> 2 >= IIC_REGSET_SIZE) {
    198         hw_error("exynos4210.combiner: overflow of reg_set by 0x"
    199                 TARGET_FMT_plx "offset\n", offset);
    200     }
    201     s->reg_set[offset >> 2] = val;
    202 
    203     switch (reg_n) {
    204     /* IIESR */
    205     case 0:
    206         /* FIXME: what if irq is pending, allowed by mask, and we allow it
    207          * again. Interrupt will rise again! */
    208 
    209         DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n",
    210                 s->external ? "EXT" : "INT",
    211                 grp_quad_base_n,
    212                 grp_quad_base_n + 1,
    213                 grp_quad_base_n + 2,
    214                 grp_quad_base_n + 3);
    215 
    216         /* Enable interrupt sources */
    217         s->group[grp_quad_base_n].src_mask |= val & 0xFF;
    218         s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8;
    219         s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16;
    220         s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24;
    221 
    222         exynos4210_combiner_update(s, grp_quad_base_n);
    223         exynos4210_combiner_update(s, grp_quad_base_n + 1);
    224         exynos4210_combiner_update(s, grp_quad_base_n + 2);
    225         exynos4210_combiner_update(s, grp_quad_base_n + 3);
    226         break;
    227         /* IIECR */
    228     case 1:
    229         DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n",
    230                 s->external ? "EXT" : "INT",
    231                 grp_quad_base_n,
    232                 grp_quad_base_n + 1,
    233                 grp_quad_base_n + 2,
    234                 grp_quad_base_n + 3);
    235 
    236         /* Disable interrupt sources */
    237         s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF);
    238         s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8);
    239         s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16);
    240         s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24);
    241 
    242         exynos4210_combiner_update(s, grp_quad_base_n);
    243         exynos4210_combiner_update(s, grp_quad_base_n + 1);
    244         exynos4210_combiner_update(s, grp_quad_base_n + 2);
    245         exynos4210_combiner_update(s, grp_quad_base_n + 3);
    246         break;
    247     default:
    248         hw_error("exynos4210.combiner: unallowed write access at offset 0x"
    249                 TARGET_FMT_plx "\n", offset);
    250         break;
    251     }
    252 }
    253 
    254 /* Get combiner group and bit from irq number */
    255 static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit)
    256 {
    257     *bit = irq - ((irq >> 3) << 3);
    258     return irq >> 3;
    259 }
    260 
    261 /* Process a change in an external IRQ input.  */
    262 static void exynos4210_combiner_handler(void *opaque, int irq, int level)
    263 {
    264     struct Exynos4210CombinerState *s =
    265             (struct Exynos4210CombinerState *)opaque;
    266     uint8_t bit_n, group_n;
    267 
    268     group_n = get_combiner_group_and_bit(irq, &bit_n);
    269 
    270     if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) {
    271         DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT"
    272                 , group_n);
    273         return;
    274     }
    275 
    276     if (level) {
    277         s->group[group_n].src_pending |= 1 << bit_n;
    278     } else {
    279         s->group[group_n].src_pending &= ~(1 << bit_n);
    280     }
    281 
    282     exynos4210_combiner_update(s, group_n);
    283 }
    284 
    285 static void exynos4210_combiner_reset(DeviceState *d)
    286 {
    287     struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d;
    288 
    289     memset(&s->group, 0, sizeof(s->group));
    290     memset(&s->reg_set, 0, sizeof(s->reg_set));
    291 
    292     s->reg_set[0xC0 >> 2] = 0x01010101;
    293     s->reg_set[0xC4 >> 2] = 0x01010101;
    294     s->reg_set[0xD0 >> 2] = 0x01010101;
    295     s->reg_set[0xD4 >> 2] = 0x01010101;
    296 }
    297 
    298 static const MemoryRegionOps exynos4210_combiner_ops = {
    299     .read = exynos4210_combiner_read,
    300     .write = exynos4210_combiner_write,
    301     .endianness = DEVICE_NATIVE_ENDIAN,
    302 };
    303 
    304 /*
    305  * Internal Combiner initialization.
    306  */
    307 static void exynos4210_combiner_init(Object *obj)
    308 {
    309     DeviceState *dev = DEVICE(obj);
    310     Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj);
    311     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    312     unsigned int i;
    313 
    314     /* Allocate general purpose input signals and connect a handler to each of
    315      * them */
    316     qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ);
    317 
    318     /* Connect SysBusDev irqs to device specific irqs */
    319     for (i = 0; i < IIC_NGRP; i++) {
    320         sysbus_init_irq(sbd, &s->output_irq[i]);
    321     }
    322 
    323     memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s,
    324                           "exynos4210-combiner", IIC_REGION_SIZE);
    325     sysbus_init_mmio(sbd, &s->iomem);
    326 }
    327 
    328 static Property exynos4210_combiner_properties[] = {
    329     DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0),
    330     DEFINE_PROP_END_OF_LIST(),
    331 };
    332 
    333 static void exynos4210_combiner_class_init(ObjectClass *klass, void *data)
    334 {
    335     DeviceClass *dc = DEVICE_CLASS(klass);
    336 
    337     dc->reset = exynos4210_combiner_reset;
    338     device_class_set_props(dc, exynos4210_combiner_properties);
    339     dc->vmsd = &vmstate_exynos4210_combiner;
    340 }
    341 
    342 static const TypeInfo exynos4210_combiner_info = {
    343     .name          = TYPE_EXYNOS4210_COMBINER,
    344     .parent        = TYPE_SYS_BUS_DEVICE,
    345     .instance_size = sizeof(Exynos4210CombinerState),
    346     .instance_init = exynos4210_combiner_init,
    347     .class_init    = exynos4210_combiner_class_init,
    348 };
    349 
    350 static void exynos4210_combiner_register_types(void)
    351 {
    352     type_register_static(&exynos4210_combiner_info);
    353 }
    354 
    355 type_init(exynos4210_combiner_register_types)