18e03cf1eSEvgeny Voevodin /* 28e03cf1eSEvgeny Voevodin * Samsung exynos4210 Interrupt Combiner 38e03cf1eSEvgeny Voevodin * 48e03cf1eSEvgeny Voevodin * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. 58e03cf1eSEvgeny Voevodin * All rights reserved. 68e03cf1eSEvgeny Voevodin * 78e03cf1eSEvgeny Voevodin * Evgeny Voevodin <e.voevodin@samsung.com> 88e03cf1eSEvgeny Voevodin * 98e03cf1eSEvgeny Voevodin * This program is free software; you can redistribute it and/or modify it 108e03cf1eSEvgeny Voevodin * under the terms of the GNU General Public License as published by the 118e03cf1eSEvgeny Voevodin * Free Software Foundation; either version 2 of the License, or (at your 128e03cf1eSEvgeny Voevodin * option) any later version. 138e03cf1eSEvgeny Voevodin * 148e03cf1eSEvgeny Voevodin * This program is distributed in the hope that it will be useful, 158e03cf1eSEvgeny Voevodin * but WITHOUT ANY WARRANTY; without even the implied warranty of 168e03cf1eSEvgeny Voevodin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 178e03cf1eSEvgeny Voevodin * See the GNU General Public License for more details. 188e03cf1eSEvgeny Voevodin * 198e03cf1eSEvgeny Voevodin * You should have received a copy of the GNU General Public License along 208e03cf1eSEvgeny Voevodin * with this program; if not, see <http://www.gnu.org/licenses/>. 218e03cf1eSEvgeny Voevodin */ 228e03cf1eSEvgeny Voevodin 238e03cf1eSEvgeny Voevodin /* 248e03cf1eSEvgeny Voevodin * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines 258e03cf1eSEvgeny Voevodin * IRQ sources into groups and provides signal output to GIC from each group. It 268e03cf1eSEvgeny Voevodin * is driven by common mask and enable/disable logic. Take a note that not all 278e03cf1eSEvgeny Voevodin * IRQs are passed to GIC through Combiner. 288e03cf1eSEvgeny Voevodin */ 298e03cf1eSEvgeny Voevodin 308ef94f0bSPeter Maydell #include "qemu/osdep.h" 3183c9f4caSPaolo Bonzini #include "hw/sysbus.h" 32d6454270SMarkus Armbruster #include "migration/vmstate.h" 330b8fa32fSMarkus Armbruster #include "qemu/module.h" 348e03cf1eSEvgeny Voevodin 350d09e41aSPaolo Bonzini #include "hw/arm/exynos4210.h" 36650d103dSMarkus Armbruster #include "hw/hw.h" 3764552b6bSMarkus Armbruster #include "hw/irq.h" 38a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 398e03cf1eSEvgeny Voevodin 408e03cf1eSEvgeny Voevodin //#define DEBUG_COMBINER 418e03cf1eSEvgeny Voevodin 428e03cf1eSEvgeny Voevodin #ifdef DEBUG_COMBINER 438e03cf1eSEvgeny Voevodin #define DPRINTF(fmt, ...) \ 448e03cf1eSEvgeny Voevodin do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \ 458e03cf1eSEvgeny Voevodin ## __VA_ARGS__); } while (0) 468e03cf1eSEvgeny Voevodin #else 478e03cf1eSEvgeny Voevodin #define DPRINTF(fmt, ...) do {} while (0) 488e03cf1eSEvgeny Voevodin #endif 498e03cf1eSEvgeny Voevodin 508e03cf1eSEvgeny Voevodin #define IIC_NGRP 64 /* Internal Interrupt Combiner 518e03cf1eSEvgeny Voevodin Groups number */ 528e03cf1eSEvgeny Voevodin #define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner 538e03cf1eSEvgeny Voevodin Interrupts number */ 548e03cf1eSEvgeny Voevodin #define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ 558e03cf1eSEvgeny Voevodin #define IIC_REGSET_SIZE 0x41 568e03cf1eSEvgeny Voevodin 578e03cf1eSEvgeny Voevodin /* 588e03cf1eSEvgeny Voevodin * State for each output signal of internal combiner 598e03cf1eSEvgeny Voevodin */ 608e03cf1eSEvgeny Voevodin typedef struct CombinerGroupState { 618e03cf1eSEvgeny Voevodin uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ 628e03cf1eSEvgeny Voevodin uint8_t src_pending; /* Pending source interrupts before masking */ 638e03cf1eSEvgeny Voevodin } CombinerGroupState; 648e03cf1eSEvgeny Voevodin 65c03c6b9cSAndreas Färber #define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" 66c03c6b9cSAndreas Färber #define EXYNOS4210_COMBINER(obj) \ 67c03c6b9cSAndreas Färber OBJECT_CHECK(Exynos4210CombinerState, (obj), TYPE_EXYNOS4210_COMBINER) 68c03c6b9cSAndreas Färber 698e03cf1eSEvgeny Voevodin typedef struct Exynos4210CombinerState { 70c03c6b9cSAndreas Färber SysBusDevice parent_obj; 71c03c6b9cSAndreas Färber 728e03cf1eSEvgeny Voevodin MemoryRegion iomem; 738e03cf1eSEvgeny Voevodin 748e03cf1eSEvgeny Voevodin struct CombinerGroupState group[IIC_NGRP]; 758e03cf1eSEvgeny Voevodin uint32_t reg_set[IIC_REGSET_SIZE]; 768e03cf1eSEvgeny Voevodin uint32_t icipsr[2]; 778e03cf1eSEvgeny Voevodin uint32_t external; /* 1 means that this combiner is external */ 788e03cf1eSEvgeny Voevodin 798e03cf1eSEvgeny Voevodin qemu_irq output_irq[IIC_NGRP]; 808e03cf1eSEvgeny Voevodin } Exynos4210CombinerState; 818e03cf1eSEvgeny Voevodin 828e03cf1eSEvgeny Voevodin static const VMStateDescription vmstate_exynos4210_combiner_group_state = { 838e03cf1eSEvgeny Voevodin .name = "exynos4210.combiner.groupstate", 848e03cf1eSEvgeny Voevodin .version_id = 1, 858e03cf1eSEvgeny Voevodin .minimum_version_id = 1, 868e03cf1eSEvgeny Voevodin .fields = (VMStateField[]) { 878e03cf1eSEvgeny Voevodin VMSTATE_UINT8(src_mask, CombinerGroupState), 888e03cf1eSEvgeny Voevodin VMSTATE_UINT8(src_pending, CombinerGroupState), 898e03cf1eSEvgeny Voevodin VMSTATE_END_OF_LIST() 908e03cf1eSEvgeny Voevodin } 918e03cf1eSEvgeny Voevodin }; 928e03cf1eSEvgeny Voevodin 938e03cf1eSEvgeny Voevodin static const VMStateDescription vmstate_exynos4210_combiner = { 948e03cf1eSEvgeny Voevodin .name = "exynos4210.combiner", 958e03cf1eSEvgeny Voevodin .version_id = 1, 968e03cf1eSEvgeny Voevodin .minimum_version_id = 1, 978e03cf1eSEvgeny Voevodin .fields = (VMStateField[]) { 988e03cf1eSEvgeny Voevodin VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, 998e03cf1eSEvgeny Voevodin vmstate_exynos4210_combiner_group_state, CombinerGroupState), 1008e03cf1eSEvgeny Voevodin VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, 1018e03cf1eSEvgeny Voevodin IIC_REGSET_SIZE), 1028e03cf1eSEvgeny Voevodin VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), 1038e03cf1eSEvgeny Voevodin VMSTATE_UINT32(external, Exynos4210CombinerState), 1048e03cf1eSEvgeny Voevodin VMSTATE_END_OF_LIST() 1058e03cf1eSEvgeny Voevodin } 1068e03cf1eSEvgeny Voevodin }; 1078e03cf1eSEvgeny Voevodin 1088e03cf1eSEvgeny Voevodin /* 1098e03cf1eSEvgeny Voevodin * Get Combiner input GPIO into irqs structure 1108e03cf1eSEvgeny Voevodin */ 1118e03cf1eSEvgeny Voevodin void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, 1128e03cf1eSEvgeny Voevodin int ext) 1138e03cf1eSEvgeny Voevodin { 1148e03cf1eSEvgeny Voevodin int n; 1158e03cf1eSEvgeny Voevodin int bit; 1168e03cf1eSEvgeny Voevodin int max; 1178e03cf1eSEvgeny Voevodin qemu_irq *irq; 1188e03cf1eSEvgeny Voevodin 1198e03cf1eSEvgeny Voevodin max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : 1208e03cf1eSEvgeny Voevodin EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; 1218e03cf1eSEvgeny Voevodin irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; 1228e03cf1eSEvgeny Voevodin 1238e03cf1eSEvgeny Voevodin /* 1248e03cf1eSEvgeny Voevodin * Some IRQs of Int/External Combiner are going to two Combiners groups, 1258e03cf1eSEvgeny Voevodin * so let split them. 1268e03cf1eSEvgeny Voevodin */ 1278e03cf1eSEvgeny Voevodin for (n = 0; n < max; n++) { 1288e03cf1eSEvgeny Voevodin 1298e03cf1eSEvgeny Voevodin bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); 1308e03cf1eSEvgeny Voevodin 1318e03cf1eSEvgeny Voevodin switch (n) { 1328e03cf1eSEvgeny Voevodin /* MDNIE_LCD1 INTG1 */ 1338e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... 1348e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): 1358e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1368e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); 1378e03cf1eSEvgeny Voevodin continue; 1388e03cf1eSEvgeny Voevodin 1398e03cf1eSEvgeny Voevodin /* TMU INTG3 */ 1408e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): 1418e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1428e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); 1438e03cf1eSEvgeny Voevodin continue; 1448e03cf1eSEvgeny Voevodin 1458e03cf1eSEvgeny Voevodin /* LCD1 INTG12 */ 1468e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... 1478e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): 1488e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1498e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); 1508e03cf1eSEvgeny Voevodin continue; 1518e03cf1eSEvgeny Voevodin 1528e03cf1eSEvgeny Voevodin /* Multi-Core Timer INTG12 */ 1538e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... 1548e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): 1558e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1568e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1578e03cf1eSEvgeny Voevodin continue; 1588e03cf1eSEvgeny Voevodin 1598e03cf1eSEvgeny Voevodin /* Multi-Core Timer INTG35 */ 1608e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... 1618e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): 1628e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1638e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1648e03cf1eSEvgeny Voevodin continue; 1658e03cf1eSEvgeny Voevodin 1668e03cf1eSEvgeny Voevodin /* Multi-Core Timer INTG51 */ 1678e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... 1688e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): 1698e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1708e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1718e03cf1eSEvgeny Voevodin continue; 1728e03cf1eSEvgeny Voevodin 1738e03cf1eSEvgeny Voevodin /* Multi-Core Timer INTG53 */ 1748e03cf1eSEvgeny Voevodin case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... 1758e03cf1eSEvgeny Voevodin EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): 1768e03cf1eSEvgeny Voevodin irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1778e03cf1eSEvgeny Voevodin irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1788e03cf1eSEvgeny Voevodin continue; 1798e03cf1eSEvgeny Voevodin } 1808e03cf1eSEvgeny Voevodin 1818e03cf1eSEvgeny Voevodin irq[n] = qdev_get_gpio_in(dev, n); 1828e03cf1eSEvgeny Voevodin } 1838e03cf1eSEvgeny Voevodin } 1848e03cf1eSEvgeny Voevodin 1858e03cf1eSEvgeny Voevodin static uint64_t 186a8170e5eSAvi Kivity exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) 1878e03cf1eSEvgeny Voevodin { 1888e03cf1eSEvgeny Voevodin struct Exynos4210CombinerState *s = 1898e03cf1eSEvgeny Voevodin (struct Exynos4210CombinerState *)opaque; 1908e03cf1eSEvgeny Voevodin uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 1918e03cf1eSEvgeny Voevodin get a start of corresponding group quad */ 1928e03cf1eSEvgeny Voevodin uint32_t grp_quad_base_n; /* Base of group quad */ 1938e03cf1eSEvgeny Voevodin uint32_t reg_n; /* Register number inside the quad */ 1948e03cf1eSEvgeny Voevodin uint32_t val; 1958e03cf1eSEvgeny Voevodin 1968e03cf1eSEvgeny Voevodin req_quad_base_n = offset >> 4; 1978e03cf1eSEvgeny Voevodin grp_quad_base_n = req_quad_base_n << 2; 1988e03cf1eSEvgeny Voevodin reg_n = (offset - (req_quad_base_n << 4)) >> 2; 1998e03cf1eSEvgeny Voevodin 2008e03cf1eSEvgeny Voevodin if (req_quad_base_n >= IIC_NGRP) { 2018e03cf1eSEvgeny Voevodin /* Read of ICIPSR register */ 2028e03cf1eSEvgeny Voevodin return s->icipsr[reg_n]; 2038e03cf1eSEvgeny Voevodin } 2048e03cf1eSEvgeny Voevodin 2058e03cf1eSEvgeny Voevodin val = 0; 2068e03cf1eSEvgeny Voevodin 2078e03cf1eSEvgeny Voevodin switch (reg_n) { 2088e03cf1eSEvgeny Voevodin /* IISTR */ 2098e03cf1eSEvgeny Voevodin case 2: 2108e03cf1eSEvgeny Voevodin val |= s->group[grp_quad_base_n].src_pending; 2118e03cf1eSEvgeny Voevodin val |= s->group[grp_quad_base_n + 1].src_pending << 8; 2128e03cf1eSEvgeny Voevodin val |= s->group[grp_quad_base_n + 2].src_pending << 16; 2138e03cf1eSEvgeny Voevodin val |= s->group[grp_quad_base_n + 3].src_pending << 24; 2148e03cf1eSEvgeny Voevodin break; 2158e03cf1eSEvgeny Voevodin /* IIMSR */ 2168e03cf1eSEvgeny Voevodin case 3: 2178e03cf1eSEvgeny Voevodin val |= s->group[grp_quad_base_n].src_mask & 2188e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n].src_pending; 2198e03cf1eSEvgeny Voevodin val |= (s->group[grp_quad_base_n + 1].src_mask & 2208e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 1].src_pending) << 8; 2218e03cf1eSEvgeny Voevodin val |= (s->group[grp_quad_base_n + 2].src_mask & 2228e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 2].src_pending) << 16; 2238e03cf1eSEvgeny Voevodin val |= (s->group[grp_quad_base_n + 3].src_mask & 2248e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 3].src_pending) << 24; 2258e03cf1eSEvgeny Voevodin break; 2268e03cf1eSEvgeny Voevodin default: 2278e03cf1eSEvgeny Voevodin if (offset >> 2 >= IIC_REGSET_SIZE) { 2288e03cf1eSEvgeny Voevodin hw_error("exynos4210.combiner: overflow of reg_set by 0x" 2298e03cf1eSEvgeny Voevodin TARGET_FMT_plx "offset\n", offset); 2308e03cf1eSEvgeny Voevodin } 2318e03cf1eSEvgeny Voevodin val = s->reg_set[offset >> 2]; 2328e03cf1eSEvgeny Voevodin return 0; 2338e03cf1eSEvgeny Voevodin } 2348e03cf1eSEvgeny Voevodin return val; 2358e03cf1eSEvgeny Voevodin } 2368e03cf1eSEvgeny Voevodin 2378e03cf1eSEvgeny Voevodin static void exynos4210_combiner_update(void *opaque, uint8_t group_n) 2388e03cf1eSEvgeny Voevodin { 2398e03cf1eSEvgeny Voevodin struct Exynos4210CombinerState *s = 2408e03cf1eSEvgeny Voevodin (struct Exynos4210CombinerState *)opaque; 2418e03cf1eSEvgeny Voevodin 2428e03cf1eSEvgeny Voevodin /* Send interrupt if needed */ 2438e03cf1eSEvgeny Voevodin if (s->group[group_n].src_mask & s->group[group_n].src_pending) { 2448e03cf1eSEvgeny Voevodin #ifdef DEBUG_COMBINER 2458e03cf1eSEvgeny Voevodin if (group_n != 26) { 2468e03cf1eSEvgeny Voevodin /* skip uart */ 2478e03cf1eSEvgeny Voevodin DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 2488e03cf1eSEvgeny Voevodin } 2498e03cf1eSEvgeny Voevodin #endif 2508e03cf1eSEvgeny Voevodin 2518e03cf1eSEvgeny Voevodin /* Set Combiner interrupt pending status after masking */ 2528e03cf1eSEvgeny Voevodin if (group_n >= 32) { 2538e03cf1eSEvgeny Voevodin s->icipsr[1] |= 1 << (group_n - 32); 2548e03cf1eSEvgeny Voevodin } else { 2558e03cf1eSEvgeny Voevodin s->icipsr[0] |= 1 << group_n; 2568e03cf1eSEvgeny Voevodin } 2578e03cf1eSEvgeny Voevodin 2588e03cf1eSEvgeny Voevodin qemu_irq_raise(s->output_irq[group_n]); 2598e03cf1eSEvgeny Voevodin } else { 2608e03cf1eSEvgeny Voevodin #ifdef DEBUG_COMBINER 2618e03cf1eSEvgeny Voevodin if (group_n != 26) { 2628e03cf1eSEvgeny Voevodin /* skip uart */ 2638e03cf1eSEvgeny Voevodin DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 2648e03cf1eSEvgeny Voevodin } 2658e03cf1eSEvgeny Voevodin #endif 2668e03cf1eSEvgeny Voevodin 2678e03cf1eSEvgeny Voevodin /* Set Combiner interrupt pending status after masking */ 2688e03cf1eSEvgeny Voevodin if (group_n >= 32) { 2698e03cf1eSEvgeny Voevodin s->icipsr[1] &= ~(1 << (group_n - 32)); 2708e03cf1eSEvgeny Voevodin } else { 2718e03cf1eSEvgeny Voevodin s->icipsr[0] &= ~(1 << group_n); 2728e03cf1eSEvgeny Voevodin } 2738e03cf1eSEvgeny Voevodin 2748e03cf1eSEvgeny Voevodin qemu_irq_lower(s->output_irq[group_n]); 2758e03cf1eSEvgeny Voevodin } 2768e03cf1eSEvgeny Voevodin } 2778e03cf1eSEvgeny Voevodin 278a8170e5eSAvi Kivity static void exynos4210_combiner_write(void *opaque, hwaddr offset, 2798e03cf1eSEvgeny Voevodin uint64_t val, unsigned size) 2808e03cf1eSEvgeny Voevodin { 2818e03cf1eSEvgeny Voevodin struct Exynos4210CombinerState *s = 2828e03cf1eSEvgeny Voevodin (struct Exynos4210CombinerState *)opaque; 2838e03cf1eSEvgeny Voevodin uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 2848e03cf1eSEvgeny Voevodin get a start of corresponding group quad */ 2858e03cf1eSEvgeny Voevodin uint32_t grp_quad_base_n; /* Base of group quad */ 2868e03cf1eSEvgeny Voevodin uint32_t reg_n; /* Register number inside the quad */ 2878e03cf1eSEvgeny Voevodin 2888e03cf1eSEvgeny Voevodin req_quad_base_n = offset >> 4; 2898e03cf1eSEvgeny Voevodin grp_quad_base_n = req_quad_base_n << 2; 2908e03cf1eSEvgeny Voevodin reg_n = (offset - (req_quad_base_n << 4)) >> 2; 2918e03cf1eSEvgeny Voevodin 2928e03cf1eSEvgeny Voevodin if (req_quad_base_n >= IIC_NGRP) { 2938e03cf1eSEvgeny Voevodin hw_error("exynos4210.combiner: unallowed write access at offset 0x" 2948e03cf1eSEvgeny Voevodin TARGET_FMT_plx "\n", offset); 2958e03cf1eSEvgeny Voevodin return; 2968e03cf1eSEvgeny Voevodin } 2978e03cf1eSEvgeny Voevodin 2988e03cf1eSEvgeny Voevodin if (reg_n > 1) { 2998e03cf1eSEvgeny Voevodin hw_error("exynos4210.combiner: unallowed write access at offset 0x" 3008e03cf1eSEvgeny Voevodin TARGET_FMT_plx "\n", offset); 3018e03cf1eSEvgeny Voevodin return; 3028e03cf1eSEvgeny Voevodin } 3038e03cf1eSEvgeny Voevodin 3048e03cf1eSEvgeny Voevodin if (offset >> 2 >= IIC_REGSET_SIZE) { 3058e03cf1eSEvgeny Voevodin hw_error("exynos4210.combiner: overflow of reg_set by 0x" 3068e03cf1eSEvgeny Voevodin TARGET_FMT_plx "offset\n", offset); 3078e03cf1eSEvgeny Voevodin } 3088e03cf1eSEvgeny Voevodin s->reg_set[offset >> 2] = val; 3098e03cf1eSEvgeny Voevodin 3108e03cf1eSEvgeny Voevodin switch (reg_n) { 3118e03cf1eSEvgeny Voevodin /* IIESR */ 3128e03cf1eSEvgeny Voevodin case 0: 3138e03cf1eSEvgeny Voevodin /* FIXME: what if irq is pending, allowed by mask, and we allow it 3148e03cf1eSEvgeny Voevodin * again. Interrupt will rise again! */ 3158e03cf1eSEvgeny Voevodin 3168e03cf1eSEvgeny Voevodin DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", 3178e03cf1eSEvgeny Voevodin s->external ? "EXT" : "INT", 3188e03cf1eSEvgeny Voevodin grp_quad_base_n, 3198e03cf1eSEvgeny Voevodin grp_quad_base_n + 1, 3208e03cf1eSEvgeny Voevodin grp_quad_base_n + 2, 3218e03cf1eSEvgeny Voevodin grp_quad_base_n + 3); 3228e03cf1eSEvgeny Voevodin 3238e03cf1eSEvgeny Voevodin /* Enable interrupt sources */ 3248e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n].src_mask |= val & 0xFF; 3258e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; 3268e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; 3278e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; 3288e03cf1eSEvgeny Voevodin 3298e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n); 3308e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 1); 3318e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 2); 3328e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 3); 3338e03cf1eSEvgeny Voevodin break; 3348e03cf1eSEvgeny Voevodin /* IIECR */ 3358e03cf1eSEvgeny Voevodin case 1: 3368e03cf1eSEvgeny Voevodin DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", 3378e03cf1eSEvgeny Voevodin s->external ? "EXT" : "INT", 3388e03cf1eSEvgeny Voevodin grp_quad_base_n, 3398e03cf1eSEvgeny Voevodin grp_quad_base_n + 1, 3408e03cf1eSEvgeny Voevodin grp_quad_base_n + 2, 3418e03cf1eSEvgeny Voevodin grp_quad_base_n + 3); 3428e03cf1eSEvgeny Voevodin 3438e03cf1eSEvgeny Voevodin /* Disable interrupt sources */ 3448e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); 3458e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); 3468e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); 3478e03cf1eSEvgeny Voevodin s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); 3488e03cf1eSEvgeny Voevodin 3498e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n); 3508e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 1); 3518e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 2); 3528e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, grp_quad_base_n + 3); 3538e03cf1eSEvgeny Voevodin break; 3548e03cf1eSEvgeny Voevodin default: 3558e03cf1eSEvgeny Voevodin hw_error("exynos4210.combiner: unallowed write access at offset 0x" 3568e03cf1eSEvgeny Voevodin TARGET_FMT_plx "\n", offset); 3578e03cf1eSEvgeny Voevodin break; 3588e03cf1eSEvgeny Voevodin } 3598e03cf1eSEvgeny Voevodin } 3608e03cf1eSEvgeny Voevodin 3618e03cf1eSEvgeny Voevodin /* Get combiner group and bit from irq number */ 3628e03cf1eSEvgeny Voevodin static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) 3638e03cf1eSEvgeny Voevodin { 3648e03cf1eSEvgeny Voevodin *bit = irq - ((irq >> 3) << 3); 3658e03cf1eSEvgeny Voevodin return irq >> 3; 3668e03cf1eSEvgeny Voevodin } 3678e03cf1eSEvgeny Voevodin 3688e03cf1eSEvgeny Voevodin /* Process a change in an external IRQ input. */ 3698e03cf1eSEvgeny Voevodin static void exynos4210_combiner_handler(void *opaque, int irq, int level) 3708e03cf1eSEvgeny Voevodin { 3718e03cf1eSEvgeny Voevodin struct Exynos4210CombinerState *s = 3728e03cf1eSEvgeny Voevodin (struct Exynos4210CombinerState *)opaque; 3738e03cf1eSEvgeny Voevodin uint8_t bit_n, group_n; 3748e03cf1eSEvgeny Voevodin 3758e03cf1eSEvgeny Voevodin group_n = get_combiner_group_and_bit(irq, &bit_n); 3768e03cf1eSEvgeny Voevodin 3778e03cf1eSEvgeny Voevodin if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { 3788e03cf1eSEvgeny Voevodin DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" 3798e03cf1eSEvgeny Voevodin , group_n); 3808e03cf1eSEvgeny Voevodin return; 3818e03cf1eSEvgeny Voevodin } 3828e03cf1eSEvgeny Voevodin 3838e03cf1eSEvgeny Voevodin if (level) { 3848e03cf1eSEvgeny Voevodin s->group[group_n].src_pending |= 1 << bit_n; 3858e03cf1eSEvgeny Voevodin } else { 3868e03cf1eSEvgeny Voevodin s->group[group_n].src_pending &= ~(1 << bit_n); 3878e03cf1eSEvgeny Voevodin } 3888e03cf1eSEvgeny Voevodin 3898e03cf1eSEvgeny Voevodin exynos4210_combiner_update(s, group_n); 3908e03cf1eSEvgeny Voevodin } 3918e03cf1eSEvgeny Voevodin 3928e03cf1eSEvgeny Voevodin static void exynos4210_combiner_reset(DeviceState *d) 3938e03cf1eSEvgeny Voevodin { 3948e03cf1eSEvgeny Voevodin struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; 3958e03cf1eSEvgeny Voevodin 3968e03cf1eSEvgeny Voevodin memset(&s->group, 0, sizeof(s->group)); 3978e03cf1eSEvgeny Voevodin memset(&s->reg_set, 0, sizeof(s->reg_set)); 3988e03cf1eSEvgeny Voevodin 3998e03cf1eSEvgeny Voevodin s->reg_set[0xC0 >> 2] = 0x01010101; 4008e03cf1eSEvgeny Voevodin s->reg_set[0xC4 >> 2] = 0x01010101; 4018e03cf1eSEvgeny Voevodin s->reg_set[0xD0 >> 2] = 0x01010101; 4028e03cf1eSEvgeny Voevodin s->reg_set[0xD4 >> 2] = 0x01010101; 4038e03cf1eSEvgeny Voevodin } 4048e03cf1eSEvgeny Voevodin 4058e03cf1eSEvgeny Voevodin static const MemoryRegionOps exynos4210_combiner_ops = { 4068e03cf1eSEvgeny Voevodin .read = exynos4210_combiner_read, 4078e03cf1eSEvgeny Voevodin .write = exynos4210_combiner_write, 4088e03cf1eSEvgeny Voevodin .endianness = DEVICE_NATIVE_ENDIAN, 4098e03cf1eSEvgeny Voevodin }; 4108e03cf1eSEvgeny Voevodin 4118e03cf1eSEvgeny Voevodin /* 4128e03cf1eSEvgeny Voevodin * Internal Combiner initialization. 4138e03cf1eSEvgeny Voevodin */ 414d3d5a6feSxiaoqiang.zhao static void exynos4210_combiner_init(Object *obj) 4158e03cf1eSEvgeny Voevodin { 416d3d5a6feSxiaoqiang.zhao DeviceState *dev = DEVICE(obj); 417d3d5a6feSxiaoqiang.zhao Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj); 418d3d5a6feSxiaoqiang.zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 4198e03cf1eSEvgeny Voevodin unsigned int i; 4208e03cf1eSEvgeny Voevodin 4218e03cf1eSEvgeny Voevodin /* Allocate general purpose input signals and connect a handler to each of 4228e03cf1eSEvgeny Voevodin * them */ 423c03c6b9cSAndreas Färber qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); 4248e03cf1eSEvgeny Voevodin 4258e03cf1eSEvgeny Voevodin /* Connect SysBusDev irqs to device specific irqs */ 426fce0a826SPeter Maydell for (i = 0; i < IIC_NGRP; i++) { 427c03c6b9cSAndreas Färber sysbus_init_irq(sbd, &s->output_irq[i]); 4288e03cf1eSEvgeny Voevodin } 4298e03cf1eSEvgeny Voevodin 430d3d5a6feSxiaoqiang.zhao memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s, 4318e03cf1eSEvgeny Voevodin "exynos4210-combiner", IIC_REGION_SIZE); 432c03c6b9cSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 4338e03cf1eSEvgeny Voevodin } 4348e03cf1eSEvgeny Voevodin 4358e03cf1eSEvgeny Voevodin static Property exynos4210_combiner_properties[] = { 4368e03cf1eSEvgeny Voevodin DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), 4378e03cf1eSEvgeny Voevodin DEFINE_PROP_END_OF_LIST(), 4388e03cf1eSEvgeny Voevodin }; 4398e03cf1eSEvgeny Voevodin 4408e03cf1eSEvgeny Voevodin static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) 4418e03cf1eSEvgeny Voevodin { 4428e03cf1eSEvgeny Voevodin DeviceClass *dc = DEVICE_CLASS(klass); 4438e03cf1eSEvgeny Voevodin 4448e03cf1eSEvgeny Voevodin dc->reset = exynos4210_combiner_reset; 445*4f67d30bSMarc-André Lureau device_class_set_props(dc, exynos4210_combiner_properties); 4468e03cf1eSEvgeny Voevodin dc->vmsd = &vmstate_exynos4210_combiner; 4478e03cf1eSEvgeny Voevodin } 4488e03cf1eSEvgeny Voevodin 4498c43a6f0SAndreas Färber static const TypeInfo exynos4210_combiner_info = { 450c03c6b9cSAndreas Färber .name = TYPE_EXYNOS4210_COMBINER, 4518e03cf1eSEvgeny Voevodin .parent = TYPE_SYS_BUS_DEVICE, 4528e03cf1eSEvgeny Voevodin .instance_size = sizeof(Exynos4210CombinerState), 453d3d5a6feSxiaoqiang.zhao .instance_init = exynos4210_combiner_init, 4548e03cf1eSEvgeny Voevodin .class_init = exynos4210_combiner_class_init, 4558e03cf1eSEvgeny Voevodin }; 4568e03cf1eSEvgeny Voevodin 4578e03cf1eSEvgeny Voevodin static void exynos4210_combiner_register_types(void) 4588e03cf1eSEvgeny Voevodin { 4598e03cf1eSEvgeny Voevodin type_register_static(&exynos4210_combiner_info); 4608e03cf1eSEvgeny Voevodin } 4618e03cf1eSEvgeny Voevodin 4628e03cf1eSEvgeny Voevodin type_init(exynos4210_combiner_register_types) 463