1*7702e47cSPaolo Bonzini /* 2*7702e47cSPaolo Bonzini * Samsung exynos4210 Interrupt Combiner 3*7702e47cSPaolo Bonzini * 4*7702e47cSPaolo Bonzini * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. 5*7702e47cSPaolo Bonzini * All rights reserved. 6*7702e47cSPaolo Bonzini * 7*7702e47cSPaolo Bonzini * Evgeny Voevodin <e.voevodin@samsung.com> 8*7702e47cSPaolo Bonzini * 9*7702e47cSPaolo Bonzini * This program is free software; you can redistribute it and/or modify it 10*7702e47cSPaolo Bonzini * under the terms of the GNU General Public License as published by the 11*7702e47cSPaolo Bonzini * Free Software Foundation; either version 2 of the License, or (at your 12*7702e47cSPaolo Bonzini * option) any later version. 13*7702e47cSPaolo Bonzini * 14*7702e47cSPaolo Bonzini * This program is distributed in the hope that it will be useful, 15*7702e47cSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 16*7702e47cSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 17*7702e47cSPaolo Bonzini * See the GNU General Public License for more details. 18*7702e47cSPaolo Bonzini * 19*7702e47cSPaolo Bonzini * You should have received a copy of the GNU General Public License along 20*7702e47cSPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>. 21*7702e47cSPaolo Bonzini */ 22*7702e47cSPaolo Bonzini 23*7702e47cSPaolo Bonzini /* 24*7702e47cSPaolo Bonzini * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines 25*7702e47cSPaolo Bonzini * IRQ sources into groups and provides signal output to GIC from each group. It 26*7702e47cSPaolo Bonzini * is driven by common mask and enable/disable logic. Take a note that not all 27*7702e47cSPaolo Bonzini * IRQs are passed to GIC through Combiner. 28*7702e47cSPaolo Bonzini */ 29*7702e47cSPaolo Bonzini 30*7702e47cSPaolo Bonzini #include "hw/sysbus.h" 31*7702e47cSPaolo Bonzini 32*7702e47cSPaolo Bonzini #include "hw/arm/exynos4210.h" 33*7702e47cSPaolo Bonzini 34*7702e47cSPaolo Bonzini //#define DEBUG_COMBINER 35*7702e47cSPaolo Bonzini 36*7702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 37*7702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) \ 38*7702e47cSPaolo Bonzini do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \ 39*7702e47cSPaolo Bonzini ## __VA_ARGS__); } while (0) 40*7702e47cSPaolo Bonzini #else 41*7702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while (0) 42*7702e47cSPaolo Bonzini #endif 43*7702e47cSPaolo Bonzini 44*7702e47cSPaolo Bonzini #define IIC_NGRP 64 /* Internal Interrupt Combiner 45*7702e47cSPaolo Bonzini Groups number */ 46*7702e47cSPaolo Bonzini #define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner 47*7702e47cSPaolo Bonzini Interrupts number */ 48*7702e47cSPaolo Bonzini #define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ 49*7702e47cSPaolo Bonzini #define IIC_REGSET_SIZE 0x41 50*7702e47cSPaolo Bonzini 51*7702e47cSPaolo Bonzini /* 52*7702e47cSPaolo Bonzini * State for each output signal of internal combiner 53*7702e47cSPaolo Bonzini */ 54*7702e47cSPaolo Bonzini typedef struct CombinerGroupState { 55*7702e47cSPaolo Bonzini uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ 56*7702e47cSPaolo Bonzini uint8_t src_pending; /* Pending source interrupts before masking */ 57*7702e47cSPaolo Bonzini } CombinerGroupState; 58*7702e47cSPaolo Bonzini 59*7702e47cSPaolo Bonzini typedef struct Exynos4210CombinerState { 60*7702e47cSPaolo Bonzini SysBusDevice busdev; 61*7702e47cSPaolo Bonzini MemoryRegion iomem; 62*7702e47cSPaolo Bonzini 63*7702e47cSPaolo Bonzini struct CombinerGroupState group[IIC_NGRP]; 64*7702e47cSPaolo Bonzini uint32_t reg_set[IIC_REGSET_SIZE]; 65*7702e47cSPaolo Bonzini uint32_t icipsr[2]; 66*7702e47cSPaolo Bonzini uint32_t external; /* 1 means that this combiner is external */ 67*7702e47cSPaolo Bonzini 68*7702e47cSPaolo Bonzini qemu_irq output_irq[IIC_NGRP]; 69*7702e47cSPaolo Bonzini } Exynos4210CombinerState; 70*7702e47cSPaolo Bonzini 71*7702e47cSPaolo Bonzini static const VMStateDescription vmstate_exynos4210_combiner_group_state = { 72*7702e47cSPaolo Bonzini .name = "exynos4210.combiner.groupstate", 73*7702e47cSPaolo Bonzini .version_id = 1, 74*7702e47cSPaolo Bonzini .minimum_version_id = 1, 75*7702e47cSPaolo Bonzini .minimum_version_id_old = 1, 76*7702e47cSPaolo Bonzini .fields = (VMStateField[]) { 77*7702e47cSPaolo Bonzini VMSTATE_UINT8(src_mask, CombinerGroupState), 78*7702e47cSPaolo Bonzini VMSTATE_UINT8(src_pending, CombinerGroupState), 79*7702e47cSPaolo Bonzini VMSTATE_END_OF_LIST() 80*7702e47cSPaolo Bonzini } 81*7702e47cSPaolo Bonzini }; 82*7702e47cSPaolo Bonzini 83*7702e47cSPaolo Bonzini static const VMStateDescription vmstate_exynos4210_combiner = { 84*7702e47cSPaolo Bonzini .name = "exynos4210.combiner", 85*7702e47cSPaolo Bonzini .version_id = 1, 86*7702e47cSPaolo Bonzini .minimum_version_id = 1, 87*7702e47cSPaolo Bonzini .minimum_version_id_old = 1, 88*7702e47cSPaolo Bonzini .fields = (VMStateField[]) { 89*7702e47cSPaolo Bonzini VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, 90*7702e47cSPaolo Bonzini vmstate_exynos4210_combiner_group_state, CombinerGroupState), 91*7702e47cSPaolo Bonzini VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, 92*7702e47cSPaolo Bonzini IIC_REGSET_SIZE), 93*7702e47cSPaolo Bonzini VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), 94*7702e47cSPaolo Bonzini VMSTATE_UINT32(external, Exynos4210CombinerState), 95*7702e47cSPaolo Bonzini VMSTATE_END_OF_LIST() 96*7702e47cSPaolo Bonzini } 97*7702e47cSPaolo Bonzini }; 98*7702e47cSPaolo Bonzini 99*7702e47cSPaolo Bonzini /* 100*7702e47cSPaolo Bonzini * Get Combiner input GPIO into irqs structure 101*7702e47cSPaolo Bonzini */ 102*7702e47cSPaolo Bonzini void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, 103*7702e47cSPaolo Bonzini int ext) 104*7702e47cSPaolo Bonzini { 105*7702e47cSPaolo Bonzini int n; 106*7702e47cSPaolo Bonzini int bit; 107*7702e47cSPaolo Bonzini int max; 108*7702e47cSPaolo Bonzini qemu_irq *irq; 109*7702e47cSPaolo Bonzini 110*7702e47cSPaolo Bonzini max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : 111*7702e47cSPaolo Bonzini EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; 112*7702e47cSPaolo Bonzini irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; 113*7702e47cSPaolo Bonzini 114*7702e47cSPaolo Bonzini /* 115*7702e47cSPaolo Bonzini * Some IRQs of Int/External Combiner are going to two Combiners groups, 116*7702e47cSPaolo Bonzini * so let split them. 117*7702e47cSPaolo Bonzini */ 118*7702e47cSPaolo Bonzini for (n = 0; n < max; n++) { 119*7702e47cSPaolo Bonzini 120*7702e47cSPaolo Bonzini bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); 121*7702e47cSPaolo Bonzini 122*7702e47cSPaolo Bonzini switch (n) { 123*7702e47cSPaolo Bonzini /* MDNIE_LCD1 INTG1 */ 124*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... 125*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): 126*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 127*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); 128*7702e47cSPaolo Bonzini continue; 129*7702e47cSPaolo Bonzini 130*7702e47cSPaolo Bonzini /* TMU INTG3 */ 131*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): 132*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 133*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); 134*7702e47cSPaolo Bonzini continue; 135*7702e47cSPaolo Bonzini 136*7702e47cSPaolo Bonzini /* LCD1 INTG12 */ 137*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... 138*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): 139*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 140*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); 141*7702e47cSPaolo Bonzini continue; 142*7702e47cSPaolo Bonzini 143*7702e47cSPaolo Bonzini /* Multi-Core Timer INTG12 */ 144*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... 145*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): 146*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 147*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 148*7702e47cSPaolo Bonzini continue; 149*7702e47cSPaolo Bonzini 150*7702e47cSPaolo Bonzini /* Multi-Core Timer INTG35 */ 151*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... 152*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): 153*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 154*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 155*7702e47cSPaolo Bonzini continue; 156*7702e47cSPaolo Bonzini 157*7702e47cSPaolo Bonzini /* Multi-Core Timer INTG51 */ 158*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... 159*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): 160*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 161*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 162*7702e47cSPaolo Bonzini continue; 163*7702e47cSPaolo Bonzini 164*7702e47cSPaolo Bonzini /* Multi-Core Timer INTG53 */ 165*7702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... 166*7702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): 167*7702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 168*7702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 169*7702e47cSPaolo Bonzini continue; 170*7702e47cSPaolo Bonzini } 171*7702e47cSPaolo Bonzini 172*7702e47cSPaolo Bonzini irq[n] = qdev_get_gpio_in(dev, n); 173*7702e47cSPaolo Bonzini } 174*7702e47cSPaolo Bonzini } 175*7702e47cSPaolo Bonzini 176*7702e47cSPaolo Bonzini static uint64_t 177*7702e47cSPaolo Bonzini exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) 178*7702e47cSPaolo Bonzini { 179*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 180*7702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 181*7702e47cSPaolo Bonzini uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 182*7702e47cSPaolo Bonzini get a start of corresponding group quad */ 183*7702e47cSPaolo Bonzini uint32_t grp_quad_base_n; /* Base of group quad */ 184*7702e47cSPaolo Bonzini uint32_t reg_n; /* Register number inside the quad */ 185*7702e47cSPaolo Bonzini uint32_t val; 186*7702e47cSPaolo Bonzini 187*7702e47cSPaolo Bonzini req_quad_base_n = offset >> 4; 188*7702e47cSPaolo Bonzini grp_quad_base_n = req_quad_base_n << 2; 189*7702e47cSPaolo Bonzini reg_n = (offset - (req_quad_base_n << 4)) >> 2; 190*7702e47cSPaolo Bonzini 191*7702e47cSPaolo Bonzini if (req_quad_base_n >= IIC_NGRP) { 192*7702e47cSPaolo Bonzini /* Read of ICIPSR register */ 193*7702e47cSPaolo Bonzini return s->icipsr[reg_n]; 194*7702e47cSPaolo Bonzini } 195*7702e47cSPaolo Bonzini 196*7702e47cSPaolo Bonzini val = 0; 197*7702e47cSPaolo Bonzini 198*7702e47cSPaolo Bonzini switch (reg_n) { 199*7702e47cSPaolo Bonzini /* IISTR */ 200*7702e47cSPaolo Bonzini case 2: 201*7702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n].src_pending; 202*7702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 1].src_pending << 8; 203*7702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 2].src_pending << 16; 204*7702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 3].src_pending << 24; 205*7702e47cSPaolo Bonzini break; 206*7702e47cSPaolo Bonzini /* IIMSR */ 207*7702e47cSPaolo Bonzini case 3: 208*7702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n].src_mask & 209*7702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_pending; 210*7702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 1].src_mask & 211*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_pending) << 8; 212*7702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 2].src_mask & 213*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_pending) << 16; 214*7702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 3].src_mask & 215*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_pending) << 24; 216*7702e47cSPaolo Bonzini break; 217*7702e47cSPaolo Bonzini default: 218*7702e47cSPaolo Bonzini if (offset >> 2 >= IIC_REGSET_SIZE) { 219*7702e47cSPaolo Bonzini hw_error("exynos4210.combiner: overflow of reg_set by 0x" 220*7702e47cSPaolo Bonzini TARGET_FMT_plx "offset\n", offset); 221*7702e47cSPaolo Bonzini } 222*7702e47cSPaolo Bonzini val = s->reg_set[offset >> 2]; 223*7702e47cSPaolo Bonzini return 0; 224*7702e47cSPaolo Bonzini } 225*7702e47cSPaolo Bonzini return val; 226*7702e47cSPaolo Bonzini } 227*7702e47cSPaolo Bonzini 228*7702e47cSPaolo Bonzini static void exynos4210_combiner_update(void *opaque, uint8_t group_n) 229*7702e47cSPaolo Bonzini { 230*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 231*7702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 232*7702e47cSPaolo Bonzini 233*7702e47cSPaolo Bonzini /* Send interrupt if needed */ 234*7702e47cSPaolo Bonzini if (s->group[group_n].src_mask & s->group[group_n].src_pending) { 235*7702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 236*7702e47cSPaolo Bonzini if (group_n != 26) { 237*7702e47cSPaolo Bonzini /* skip uart */ 238*7702e47cSPaolo Bonzini DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 239*7702e47cSPaolo Bonzini } 240*7702e47cSPaolo Bonzini #endif 241*7702e47cSPaolo Bonzini 242*7702e47cSPaolo Bonzini /* Set Combiner interrupt pending status after masking */ 243*7702e47cSPaolo Bonzini if (group_n >= 32) { 244*7702e47cSPaolo Bonzini s->icipsr[1] |= 1 << (group_n - 32); 245*7702e47cSPaolo Bonzini } else { 246*7702e47cSPaolo Bonzini s->icipsr[0] |= 1 << group_n; 247*7702e47cSPaolo Bonzini } 248*7702e47cSPaolo Bonzini 249*7702e47cSPaolo Bonzini qemu_irq_raise(s->output_irq[group_n]); 250*7702e47cSPaolo Bonzini } else { 251*7702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 252*7702e47cSPaolo Bonzini if (group_n != 26) { 253*7702e47cSPaolo Bonzini /* skip uart */ 254*7702e47cSPaolo Bonzini DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 255*7702e47cSPaolo Bonzini } 256*7702e47cSPaolo Bonzini #endif 257*7702e47cSPaolo Bonzini 258*7702e47cSPaolo Bonzini /* Set Combiner interrupt pending status after masking */ 259*7702e47cSPaolo Bonzini if (group_n >= 32) { 260*7702e47cSPaolo Bonzini s->icipsr[1] &= ~(1 << (group_n - 32)); 261*7702e47cSPaolo Bonzini } else { 262*7702e47cSPaolo Bonzini s->icipsr[0] &= ~(1 << group_n); 263*7702e47cSPaolo Bonzini } 264*7702e47cSPaolo Bonzini 265*7702e47cSPaolo Bonzini qemu_irq_lower(s->output_irq[group_n]); 266*7702e47cSPaolo Bonzini } 267*7702e47cSPaolo Bonzini } 268*7702e47cSPaolo Bonzini 269*7702e47cSPaolo Bonzini static void exynos4210_combiner_write(void *opaque, hwaddr offset, 270*7702e47cSPaolo Bonzini uint64_t val, unsigned size) 271*7702e47cSPaolo Bonzini { 272*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 273*7702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 274*7702e47cSPaolo Bonzini uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 275*7702e47cSPaolo Bonzini get a start of corresponding group quad */ 276*7702e47cSPaolo Bonzini uint32_t grp_quad_base_n; /* Base of group quad */ 277*7702e47cSPaolo Bonzini uint32_t reg_n; /* Register number inside the quad */ 278*7702e47cSPaolo Bonzini 279*7702e47cSPaolo Bonzini req_quad_base_n = offset >> 4; 280*7702e47cSPaolo Bonzini grp_quad_base_n = req_quad_base_n << 2; 281*7702e47cSPaolo Bonzini reg_n = (offset - (req_quad_base_n << 4)) >> 2; 282*7702e47cSPaolo Bonzini 283*7702e47cSPaolo Bonzini if (req_quad_base_n >= IIC_NGRP) { 284*7702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 285*7702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 286*7702e47cSPaolo Bonzini return; 287*7702e47cSPaolo Bonzini } 288*7702e47cSPaolo Bonzini 289*7702e47cSPaolo Bonzini if (reg_n > 1) { 290*7702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 291*7702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 292*7702e47cSPaolo Bonzini return; 293*7702e47cSPaolo Bonzini } 294*7702e47cSPaolo Bonzini 295*7702e47cSPaolo Bonzini if (offset >> 2 >= IIC_REGSET_SIZE) { 296*7702e47cSPaolo Bonzini hw_error("exynos4210.combiner: overflow of reg_set by 0x" 297*7702e47cSPaolo Bonzini TARGET_FMT_plx "offset\n", offset); 298*7702e47cSPaolo Bonzini } 299*7702e47cSPaolo Bonzini s->reg_set[offset >> 2] = val; 300*7702e47cSPaolo Bonzini 301*7702e47cSPaolo Bonzini switch (reg_n) { 302*7702e47cSPaolo Bonzini /* IIESR */ 303*7702e47cSPaolo Bonzini case 0: 304*7702e47cSPaolo Bonzini /* FIXME: what if irq is pending, allowed by mask, and we allow it 305*7702e47cSPaolo Bonzini * again. Interrupt will rise again! */ 306*7702e47cSPaolo Bonzini 307*7702e47cSPaolo Bonzini DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", 308*7702e47cSPaolo Bonzini s->external ? "EXT" : "INT", 309*7702e47cSPaolo Bonzini grp_quad_base_n, 310*7702e47cSPaolo Bonzini grp_quad_base_n + 1, 311*7702e47cSPaolo Bonzini grp_quad_base_n + 2, 312*7702e47cSPaolo Bonzini grp_quad_base_n + 3); 313*7702e47cSPaolo Bonzini 314*7702e47cSPaolo Bonzini /* Enable interrupt sources */ 315*7702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_mask |= val & 0xFF; 316*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; 317*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; 318*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; 319*7702e47cSPaolo Bonzini 320*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n); 321*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 1); 322*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 2); 323*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 3); 324*7702e47cSPaolo Bonzini break; 325*7702e47cSPaolo Bonzini /* IIECR */ 326*7702e47cSPaolo Bonzini case 1: 327*7702e47cSPaolo Bonzini DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", 328*7702e47cSPaolo Bonzini s->external ? "EXT" : "INT", 329*7702e47cSPaolo Bonzini grp_quad_base_n, 330*7702e47cSPaolo Bonzini grp_quad_base_n + 1, 331*7702e47cSPaolo Bonzini grp_quad_base_n + 2, 332*7702e47cSPaolo Bonzini grp_quad_base_n + 3); 333*7702e47cSPaolo Bonzini 334*7702e47cSPaolo Bonzini /* Disable interrupt sources */ 335*7702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); 336*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); 337*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); 338*7702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); 339*7702e47cSPaolo Bonzini 340*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n); 341*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 1); 342*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 2); 343*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 3); 344*7702e47cSPaolo Bonzini break; 345*7702e47cSPaolo Bonzini default: 346*7702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 347*7702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 348*7702e47cSPaolo Bonzini break; 349*7702e47cSPaolo Bonzini } 350*7702e47cSPaolo Bonzini } 351*7702e47cSPaolo Bonzini 352*7702e47cSPaolo Bonzini /* Get combiner group and bit from irq number */ 353*7702e47cSPaolo Bonzini static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) 354*7702e47cSPaolo Bonzini { 355*7702e47cSPaolo Bonzini *bit = irq - ((irq >> 3) << 3); 356*7702e47cSPaolo Bonzini return irq >> 3; 357*7702e47cSPaolo Bonzini } 358*7702e47cSPaolo Bonzini 359*7702e47cSPaolo Bonzini /* Process a change in an external IRQ input. */ 360*7702e47cSPaolo Bonzini static void exynos4210_combiner_handler(void *opaque, int irq, int level) 361*7702e47cSPaolo Bonzini { 362*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 363*7702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 364*7702e47cSPaolo Bonzini uint8_t bit_n, group_n; 365*7702e47cSPaolo Bonzini 366*7702e47cSPaolo Bonzini group_n = get_combiner_group_and_bit(irq, &bit_n); 367*7702e47cSPaolo Bonzini 368*7702e47cSPaolo Bonzini if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { 369*7702e47cSPaolo Bonzini DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" 370*7702e47cSPaolo Bonzini , group_n); 371*7702e47cSPaolo Bonzini return; 372*7702e47cSPaolo Bonzini } 373*7702e47cSPaolo Bonzini 374*7702e47cSPaolo Bonzini if (level) { 375*7702e47cSPaolo Bonzini s->group[group_n].src_pending |= 1 << bit_n; 376*7702e47cSPaolo Bonzini } else { 377*7702e47cSPaolo Bonzini s->group[group_n].src_pending &= ~(1 << bit_n); 378*7702e47cSPaolo Bonzini } 379*7702e47cSPaolo Bonzini 380*7702e47cSPaolo Bonzini exynos4210_combiner_update(s, group_n); 381*7702e47cSPaolo Bonzini } 382*7702e47cSPaolo Bonzini 383*7702e47cSPaolo Bonzini static void exynos4210_combiner_reset(DeviceState *d) 384*7702e47cSPaolo Bonzini { 385*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; 386*7702e47cSPaolo Bonzini 387*7702e47cSPaolo Bonzini memset(&s->group, 0, sizeof(s->group)); 388*7702e47cSPaolo Bonzini memset(&s->reg_set, 0, sizeof(s->reg_set)); 389*7702e47cSPaolo Bonzini 390*7702e47cSPaolo Bonzini s->reg_set[0xC0 >> 2] = 0x01010101; 391*7702e47cSPaolo Bonzini s->reg_set[0xC4 >> 2] = 0x01010101; 392*7702e47cSPaolo Bonzini s->reg_set[0xD0 >> 2] = 0x01010101; 393*7702e47cSPaolo Bonzini s->reg_set[0xD4 >> 2] = 0x01010101; 394*7702e47cSPaolo Bonzini } 395*7702e47cSPaolo Bonzini 396*7702e47cSPaolo Bonzini static const MemoryRegionOps exynos4210_combiner_ops = { 397*7702e47cSPaolo Bonzini .read = exynos4210_combiner_read, 398*7702e47cSPaolo Bonzini .write = exynos4210_combiner_write, 399*7702e47cSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 400*7702e47cSPaolo Bonzini }; 401*7702e47cSPaolo Bonzini 402*7702e47cSPaolo Bonzini /* 403*7702e47cSPaolo Bonzini * Internal Combiner initialization. 404*7702e47cSPaolo Bonzini */ 405*7702e47cSPaolo Bonzini static int exynos4210_combiner_init(SysBusDevice *dev) 406*7702e47cSPaolo Bonzini { 407*7702e47cSPaolo Bonzini unsigned int i; 408*7702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 409*7702e47cSPaolo Bonzini FROM_SYSBUS(struct Exynos4210CombinerState, dev); 410*7702e47cSPaolo Bonzini 411*7702e47cSPaolo Bonzini /* Allocate general purpose input signals and connect a handler to each of 412*7702e47cSPaolo Bonzini * them */ 413*7702e47cSPaolo Bonzini qdev_init_gpio_in(&s->busdev.qdev, exynos4210_combiner_handler, IIC_NIRQ); 414*7702e47cSPaolo Bonzini 415*7702e47cSPaolo Bonzini /* Connect SysBusDev irqs to device specific irqs */ 416*7702e47cSPaolo Bonzini for (i = 0; i < IIC_NIRQ; i++) { 417*7702e47cSPaolo Bonzini sysbus_init_irq(dev, &s->output_irq[i]); 418*7702e47cSPaolo Bonzini } 419*7702e47cSPaolo Bonzini 420*7702e47cSPaolo Bonzini memory_region_init_io(&s->iomem, &exynos4210_combiner_ops, s, 421*7702e47cSPaolo Bonzini "exynos4210-combiner", IIC_REGION_SIZE); 422*7702e47cSPaolo Bonzini sysbus_init_mmio(dev, &s->iomem); 423*7702e47cSPaolo Bonzini 424*7702e47cSPaolo Bonzini return 0; 425*7702e47cSPaolo Bonzini } 426*7702e47cSPaolo Bonzini 427*7702e47cSPaolo Bonzini static Property exynos4210_combiner_properties[] = { 428*7702e47cSPaolo Bonzini DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), 429*7702e47cSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 430*7702e47cSPaolo Bonzini }; 431*7702e47cSPaolo Bonzini 432*7702e47cSPaolo Bonzini static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) 433*7702e47cSPaolo Bonzini { 434*7702e47cSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 435*7702e47cSPaolo Bonzini SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 436*7702e47cSPaolo Bonzini 437*7702e47cSPaolo Bonzini k->init = exynos4210_combiner_init; 438*7702e47cSPaolo Bonzini dc->reset = exynos4210_combiner_reset; 439*7702e47cSPaolo Bonzini dc->props = exynos4210_combiner_properties; 440*7702e47cSPaolo Bonzini dc->vmsd = &vmstate_exynos4210_combiner; 441*7702e47cSPaolo Bonzini } 442*7702e47cSPaolo Bonzini 443*7702e47cSPaolo Bonzini static const TypeInfo exynos4210_combiner_info = { 444*7702e47cSPaolo Bonzini .name = "exynos4210.combiner", 445*7702e47cSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 446*7702e47cSPaolo Bonzini .instance_size = sizeof(Exynos4210CombinerState), 447*7702e47cSPaolo Bonzini .class_init = exynos4210_combiner_class_init, 448*7702e47cSPaolo Bonzini }; 449*7702e47cSPaolo Bonzini 450*7702e47cSPaolo Bonzini static void exynos4210_combiner_register_types(void) 451*7702e47cSPaolo Bonzini { 452*7702e47cSPaolo Bonzini type_register_static(&exynos4210_combiner_info); 453*7702e47cSPaolo Bonzini } 454*7702e47cSPaolo Bonzini 455*7702e47cSPaolo Bonzini type_init(exynos4210_combiner_register_types) 456