17702e47cSPaolo Bonzini /* 27702e47cSPaolo Bonzini * Samsung exynos4210 Interrupt Combiner 37702e47cSPaolo Bonzini * 47702e47cSPaolo Bonzini * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. 57702e47cSPaolo Bonzini * All rights reserved. 67702e47cSPaolo Bonzini * 77702e47cSPaolo Bonzini * Evgeny Voevodin <e.voevodin@samsung.com> 87702e47cSPaolo Bonzini * 97702e47cSPaolo Bonzini * This program is free software; you can redistribute it and/or modify it 107702e47cSPaolo Bonzini * under the terms of the GNU General Public License as published by the 117702e47cSPaolo Bonzini * Free Software Foundation; either version 2 of the License, or (at your 127702e47cSPaolo Bonzini * option) any later version. 137702e47cSPaolo Bonzini * 147702e47cSPaolo Bonzini * This program is distributed in the hope that it will be useful, 157702e47cSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 167702e47cSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 177702e47cSPaolo Bonzini * See the GNU General Public License for more details. 187702e47cSPaolo Bonzini * 197702e47cSPaolo Bonzini * You should have received a copy of the GNU General Public License along 207702e47cSPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>. 217702e47cSPaolo Bonzini */ 227702e47cSPaolo Bonzini 237702e47cSPaolo Bonzini /* 247702e47cSPaolo Bonzini * Exynos4210 Combiner represents an OR gate for SOC's IRQ lines. It combines 257702e47cSPaolo Bonzini * IRQ sources into groups and provides signal output to GIC from each group. It 267702e47cSPaolo Bonzini * is driven by common mask and enable/disable logic. Take a note that not all 277702e47cSPaolo Bonzini * IRQs are passed to GIC through Combiner. 287702e47cSPaolo Bonzini */ 297702e47cSPaolo Bonzini 307702e47cSPaolo Bonzini #include "hw/sysbus.h" 317702e47cSPaolo Bonzini 327702e47cSPaolo Bonzini #include "hw/arm/exynos4210.h" 337702e47cSPaolo Bonzini 347702e47cSPaolo Bonzini //#define DEBUG_COMBINER 357702e47cSPaolo Bonzini 367702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 377702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) \ 387702e47cSPaolo Bonzini do { fprintf(stdout, "COMBINER: [%s:%d] " fmt, __func__ , __LINE__, \ 397702e47cSPaolo Bonzini ## __VA_ARGS__); } while (0) 407702e47cSPaolo Bonzini #else 417702e47cSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while (0) 427702e47cSPaolo Bonzini #endif 437702e47cSPaolo Bonzini 447702e47cSPaolo Bonzini #define IIC_NGRP 64 /* Internal Interrupt Combiner 457702e47cSPaolo Bonzini Groups number */ 467702e47cSPaolo Bonzini #define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner 477702e47cSPaolo Bonzini Interrupts number */ 487702e47cSPaolo Bonzini #define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ 497702e47cSPaolo Bonzini #define IIC_REGSET_SIZE 0x41 507702e47cSPaolo Bonzini 517702e47cSPaolo Bonzini /* 527702e47cSPaolo Bonzini * State for each output signal of internal combiner 537702e47cSPaolo Bonzini */ 547702e47cSPaolo Bonzini typedef struct CombinerGroupState { 557702e47cSPaolo Bonzini uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ 567702e47cSPaolo Bonzini uint8_t src_pending; /* Pending source interrupts before masking */ 577702e47cSPaolo Bonzini } CombinerGroupState; 587702e47cSPaolo Bonzini 59*c03c6b9cSAndreas Färber #define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" 60*c03c6b9cSAndreas Färber #define EXYNOS4210_COMBINER(obj) \ 61*c03c6b9cSAndreas Färber OBJECT_CHECK(Exynos4210CombinerState, (obj), TYPE_EXYNOS4210_COMBINER) 62*c03c6b9cSAndreas Färber 637702e47cSPaolo Bonzini typedef struct Exynos4210CombinerState { 64*c03c6b9cSAndreas Färber SysBusDevice parent_obj; 65*c03c6b9cSAndreas Färber 667702e47cSPaolo Bonzini MemoryRegion iomem; 677702e47cSPaolo Bonzini 687702e47cSPaolo Bonzini struct CombinerGroupState group[IIC_NGRP]; 697702e47cSPaolo Bonzini uint32_t reg_set[IIC_REGSET_SIZE]; 707702e47cSPaolo Bonzini uint32_t icipsr[2]; 717702e47cSPaolo Bonzini uint32_t external; /* 1 means that this combiner is external */ 727702e47cSPaolo Bonzini 737702e47cSPaolo Bonzini qemu_irq output_irq[IIC_NGRP]; 747702e47cSPaolo Bonzini } Exynos4210CombinerState; 757702e47cSPaolo Bonzini 767702e47cSPaolo Bonzini static const VMStateDescription vmstate_exynos4210_combiner_group_state = { 777702e47cSPaolo Bonzini .name = "exynos4210.combiner.groupstate", 787702e47cSPaolo Bonzini .version_id = 1, 797702e47cSPaolo Bonzini .minimum_version_id = 1, 807702e47cSPaolo Bonzini .minimum_version_id_old = 1, 817702e47cSPaolo Bonzini .fields = (VMStateField[]) { 827702e47cSPaolo Bonzini VMSTATE_UINT8(src_mask, CombinerGroupState), 837702e47cSPaolo Bonzini VMSTATE_UINT8(src_pending, CombinerGroupState), 847702e47cSPaolo Bonzini VMSTATE_END_OF_LIST() 857702e47cSPaolo Bonzini } 867702e47cSPaolo Bonzini }; 877702e47cSPaolo Bonzini 887702e47cSPaolo Bonzini static const VMStateDescription vmstate_exynos4210_combiner = { 897702e47cSPaolo Bonzini .name = "exynos4210.combiner", 907702e47cSPaolo Bonzini .version_id = 1, 917702e47cSPaolo Bonzini .minimum_version_id = 1, 927702e47cSPaolo Bonzini .minimum_version_id_old = 1, 937702e47cSPaolo Bonzini .fields = (VMStateField[]) { 947702e47cSPaolo Bonzini VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, 957702e47cSPaolo Bonzini vmstate_exynos4210_combiner_group_state, CombinerGroupState), 967702e47cSPaolo Bonzini VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, 977702e47cSPaolo Bonzini IIC_REGSET_SIZE), 987702e47cSPaolo Bonzini VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), 997702e47cSPaolo Bonzini VMSTATE_UINT32(external, Exynos4210CombinerState), 1007702e47cSPaolo Bonzini VMSTATE_END_OF_LIST() 1017702e47cSPaolo Bonzini } 1027702e47cSPaolo Bonzini }; 1037702e47cSPaolo Bonzini 1047702e47cSPaolo Bonzini /* 1057702e47cSPaolo Bonzini * Get Combiner input GPIO into irqs structure 1067702e47cSPaolo Bonzini */ 1077702e47cSPaolo Bonzini void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, 1087702e47cSPaolo Bonzini int ext) 1097702e47cSPaolo Bonzini { 1107702e47cSPaolo Bonzini int n; 1117702e47cSPaolo Bonzini int bit; 1127702e47cSPaolo Bonzini int max; 1137702e47cSPaolo Bonzini qemu_irq *irq; 1147702e47cSPaolo Bonzini 1157702e47cSPaolo Bonzini max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : 1167702e47cSPaolo Bonzini EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; 1177702e47cSPaolo Bonzini irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; 1187702e47cSPaolo Bonzini 1197702e47cSPaolo Bonzini /* 1207702e47cSPaolo Bonzini * Some IRQs of Int/External Combiner are going to two Combiners groups, 1217702e47cSPaolo Bonzini * so let split them. 1227702e47cSPaolo Bonzini */ 1237702e47cSPaolo Bonzini for (n = 0; n < max; n++) { 1247702e47cSPaolo Bonzini 1257702e47cSPaolo Bonzini bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); 1267702e47cSPaolo Bonzini 1277702e47cSPaolo Bonzini switch (n) { 1287702e47cSPaolo Bonzini /* MDNIE_LCD1 INTG1 */ 1297702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... 1307702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): 1317702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1327702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); 1337702e47cSPaolo Bonzini continue; 1347702e47cSPaolo Bonzini 1357702e47cSPaolo Bonzini /* TMU INTG3 */ 1367702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): 1377702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1387702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); 1397702e47cSPaolo Bonzini continue; 1407702e47cSPaolo Bonzini 1417702e47cSPaolo Bonzini /* LCD1 INTG12 */ 1427702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... 1437702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): 1447702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1457702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); 1467702e47cSPaolo Bonzini continue; 1477702e47cSPaolo Bonzini 1487702e47cSPaolo Bonzini /* Multi-Core Timer INTG12 */ 1497702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... 1507702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): 1517702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1527702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1537702e47cSPaolo Bonzini continue; 1547702e47cSPaolo Bonzini 1557702e47cSPaolo Bonzini /* Multi-Core Timer INTG35 */ 1567702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... 1577702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): 1587702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1597702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1607702e47cSPaolo Bonzini continue; 1617702e47cSPaolo Bonzini 1627702e47cSPaolo Bonzini /* Multi-Core Timer INTG51 */ 1637702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... 1647702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): 1657702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1667702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1677702e47cSPaolo Bonzini continue; 1687702e47cSPaolo Bonzini 1697702e47cSPaolo Bonzini /* Multi-Core Timer INTG53 */ 1707702e47cSPaolo Bonzini case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... 1717702e47cSPaolo Bonzini EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): 1727702e47cSPaolo Bonzini irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 1737702e47cSPaolo Bonzini irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 1747702e47cSPaolo Bonzini continue; 1757702e47cSPaolo Bonzini } 1767702e47cSPaolo Bonzini 1777702e47cSPaolo Bonzini irq[n] = qdev_get_gpio_in(dev, n); 1787702e47cSPaolo Bonzini } 1797702e47cSPaolo Bonzini } 1807702e47cSPaolo Bonzini 1817702e47cSPaolo Bonzini static uint64_t 1827702e47cSPaolo Bonzini exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) 1837702e47cSPaolo Bonzini { 1847702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 1857702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 1867702e47cSPaolo Bonzini uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 1877702e47cSPaolo Bonzini get a start of corresponding group quad */ 1887702e47cSPaolo Bonzini uint32_t grp_quad_base_n; /* Base of group quad */ 1897702e47cSPaolo Bonzini uint32_t reg_n; /* Register number inside the quad */ 1907702e47cSPaolo Bonzini uint32_t val; 1917702e47cSPaolo Bonzini 1927702e47cSPaolo Bonzini req_quad_base_n = offset >> 4; 1937702e47cSPaolo Bonzini grp_quad_base_n = req_quad_base_n << 2; 1947702e47cSPaolo Bonzini reg_n = (offset - (req_quad_base_n << 4)) >> 2; 1957702e47cSPaolo Bonzini 1967702e47cSPaolo Bonzini if (req_quad_base_n >= IIC_NGRP) { 1977702e47cSPaolo Bonzini /* Read of ICIPSR register */ 1987702e47cSPaolo Bonzini return s->icipsr[reg_n]; 1997702e47cSPaolo Bonzini } 2007702e47cSPaolo Bonzini 2017702e47cSPaolo Bonzini val = 0; 2027702e47cSPaolo Bonzini 2037702e47cSPaolo Bonzini switch (reg_n) { 2047702e47cSPaolo Bonzini /* IISTR */ 2057702e47cSPaolo Bonzini case 2: 2067702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n].src_pending; 2077702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 1].src_pending << 8; 2087702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 2].src_pending << 16; 2097702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n + 3].src_pending << 24; 2107702e47cSPaolo Bonzini break; 2117702e47cSPaolo Bonzini /* IIMSR */ 2127702e47cSPaolo Bonzini case 3: 2137702e47cSPaolo Bonzini val |= s->group[grp_quad_base_n].src_mask & 2147702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_pending; 2157702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 1].src_mask & 2167702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_pending) << 8; 2177702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 2].src_mask & 2187702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_pending) << 16; 2197702e47cSPaolo Bonzini val |= (s->group[grp_quad_base_n + 3].src_mask & 2207702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_pending) << 24; 2217702e47cSPaolo Bonzini break; 2227702e47cSPaolo Bonzini default: 2237702e47cSPaolo Bonzini if (offset >> 2 >= IIC_REGSET_SIZE) { 2247702e47cSPaolo Bonzini hw_error("exynos4210.combiner: overflow of reg_set by 0x" 2257702e47cSPaolo Bonzini TARGET_FMT_plx "offset\n", offset); 2267702e47cSPaolo Bonzini } 2277702e47cSPaolo Bonzini val = s->reg_set[offset >> 2]; 2287702e47cSPaolo Bonzini return 0; 2297702e47cSPaolo Bonzini } 2307702e47cSPaolo Bonzini return val; 2317702e47cSPaolo Bonzini } 2327702e47cSPaolo Bonzini 2337702e47cSPaolo Bonzini static void exynos4210_combiner_update(void *opaque, uint8_t group_n) 2347702e47cSPaolo Bonzini { 2357702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 2367702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 2377702e47cSPaolo Bonzini 2387702e47cSPaolo Bonzini /* Send interrupt if needed */ 2397702e47cSPaolo Bonzini if (s->group[group_n].src_mask & s->group[group_n].src_pending) { 2407702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 2417702e47cSPaolo Bonzini if (group_n != 26) { 2427702e47cSPaolo Bonzini /* skip uart */ 2437702e47cSPaolo Bonzini DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 2447702e47cSPaolo Bonzini } 2457702e47cSPaolo Bonzini #endif 2467702e47cSPaolo Bonzini 2477702e47cSPaolo Bonzini /* Set Combiner interrupt pending status after masking */ 2487702e47cSPaolo Bonzini if (group_n >= 32) { 2497702e47cSPaolo Bonzini s->icipsr[1] |= 1 << (group_n - 32); 2507702e47cSPaolo Bonzini } else { 2517702e47cSPaolo Bonzini s->icipsr[0] |= 1 << group_n; 2527702e47cSPaolo Bonzini } 2537702e47cSPaolo Bonzini 2547702e47cSPaolo Bonzini qemu_irq_raise(s->output_irq[group_n]); 2557702e47cSPaolo Bonzini } else { 2567702e47cSPaolo Bonzini #ifdef DEBUG_COMBINER 2577702e47cSPaolo Bonzini if (group_n != 26) { 2587702e47cSPaolo Bonzini /* skip uart */ 2597702e47cSPaolo Bonzini DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 2607702e47cSPaolo Bonzini } 2617702e47cSPaolo Bonzini #endif 2627702e47cSPaolo Bonzini 2637702e47cSPaolo Bonzini /* Set Combiner interrupt pending status after masking */ 2647702e47cSPaolo Bonzini if (group_n >= 32) { 2657702e47cSPaolo Bonzini s->icipsr[1] &= ~(1 << (group_n - 32)); 2667702e47cSPaolo Bonzini } else { 2677702e47cSPaolo Bonzini s->icipsr[0] &= ~(1 << group_n); 2687702e47cSPaolo Bonzini } 2697702e47cSPaolo Bonzini 2707702e47cSPaolo Bonzini qemu_irq_lower(s->output_irq[group_n]); 2717702e47cSPaolo Bonzini } 2727702e47cSPaolo Bonzini } 2737702e47cSPaolo Bonzini 2747702e47cSPaolo Bonzini static void exynos4210_combiner_write(void *opaque, hwaddr offset, 2757702e47cSPaolo Bonzini uint64_t val, unsigned size) 2767702e47cSPaolo Bonzini { 2777702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 2787702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 2797702e47cSPaolo Bonzini uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 2807702e47cSPaolo Bonzini get a start of corresponding group quad */ 2817702e47cSPaolo Bonzini uint32_t grp_quad_base_n; /* Base of group quad */ 2827702e47cSPaolo Bonzini uint32_t reg_n; /* Register number inside the quad */ 2837702e47cSPaolo Bonzini 2847702e47cSPaolo Bonzini req_quad_base_n = offset >> 4; 2857702e47cSPaolo Bonzini grp_quad_base_n = req_quad_base_n << 2; 2867702e47cSPaolo Bonzini reg_n = (offset - (req_quad_base_n << 4)) >> 2; 2877702e47cSPaolo Bonzini 2887702e47cSPaolo Bonzini if (req_quad_base_n >= IIC_NGRP) { 2897702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 2907702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 2917702e47cSPaolo Bonzini return; 2927702e47cSPaolo Bonzini } 2937702e47cSPaolo Bonzini 2947702e47cSPaolo Bonzini if (reg_n > 1) { 2957702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 2967702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 2977702e47cSPaolo Bonzini return; 2987702e47cSPaolo Bonzini } 2997702e47cSPaolo Bonzini 3007702e47cSPaolo Bonzini if (offset >> 2 >= IIC_REGSET_SIZE) { 3017702e47cSPaolo Bonzini hw_error("exynos4210.combiner: overflow of reg_set by 0x" 3027702e47cSPaolo Bonzini TARGET_FMT_plx "offset\n", offset); 3037702e47cSPaolo Bonzini } 3047702e47cSPaolo Bonzini s->reg_set[offset >> 2] = val; 3057702e47cSPaolo Bonzini 3067702e47cSPaolo Bonzini switch (reg_n) { 3077702e47cSPaolo Bonzini /* IIESR */ 3087702e47cSPaolo Bonzini case 0: 3097702e47cSPaolo Bonzini /* FIXME: what if irq is pending, allowed by mask, and we allow it 3107702e47cSPaolo Bonzini * again. Interrupt will rise again! */ 3117702e47cSPaolo Bonzini 3127702e47cSPaolo Bonzini DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", 3137702e47cSPaolo Bonzini s->external ? "EXT" : "INT", 3147702e47cSPaolo Bonzini grp_quad_base_n, 3157702e47cSPaolo Bonzini grp_quad_base_n + 1, 3167702e47cSPaolo Bonzini grp_quad_base_n + 2, 3177702e47cSPaolo Bonzini grp_quad_base_n + 3); 3187702e47cSPaolo Bonzini 3197702e47cSPaolo Bonzini /* Enable interrupt sources */ 3207702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_mask |= val & 0xFF; 3217702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; 3227702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; 3237702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; 3247702e47cSPaolo Bonzini 3257702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n); 3267702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 1); 3277702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 2); 3287702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 3); 3297702e47cSPaolo Bonzini break; 3307702e47cSPaolo Bonzini /* IIECR */ 3317702e47cSPaolo Bonzini case 1: 3327702e47cSPaolo Bonzini DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", 3337702e47cSPaolo Bonzini s->external ? "EXT" : "INT", 3347702e47cSPaolo Bonzini grp_quad_base_n, 3357702e47cSPaolo Bonzini grp_quad_base_n + 1, 3367702e47cSPaolo Bonzini grp_quad_base_n + 2, 3377702e47cSPaolo Bonzini grp_quad_base_n + 3); 3387702e47cSPaolo Bonzini 3397702e47cSPaolo Bonzini /* Disable interrupt sources */ 3407702e47cSPaolo Bonzini s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); 3417702e47cSPaolo Bonzini s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); 3427702e47cSPaolo Bonzini s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); 3437702e47cSPaolo Bonzini s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); 3447702e47cSPaolo Bonzini 3457702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n); 3467702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 1); 3477702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 2); 3487702e47cSPaolo Bonzini exynos4210_combiner_update(s, grp_quad_base_n + 3); 3497702e47cSPaolo Bonzini break; 3507702e47cSPaolo Bonzini default: 3517702e47cSPaolo Bonzini hw_error("exynos4210.combiner: unallowed write access at offset 0x" 3527702e47cSPaolo Bonzini TARGET_FMT_plx "\n", offset); 3537702e47cSPaolo Bonzini break; 3547702e47cSPaolo Bonzini } 3557702e47cSPaolo Bonzini } 3567702e47cSPaolo Bonzini 3577702e47cSPaolo Bonzini /* Get combiner group and bit from irq number */ 3587702e47cSPaolo Bonzini static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) 3597702e47cSPaolo Bonzini { 3607702e47cSPaolo Bonzini *bit = irq - ((irq >> 3) << 3); 3617702e47cSPaolo Bonzini return irq >> 3; 3627702e47cSPaolo Bonzini } 3637702e47cSPaolo Bonzini 3647702e47cSPaolo Bonzini /* Process a change in an external IRQ input. */ 3657702e47cSPaolo Bonzini static void exynos4210_combiner_handler(void *opaque, int irq, int level) 3667702e47cSPaolo Bonzini { 3677702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = 3687702e47cSPaolo Bonzini (struct Exynos4210CombinerState *)opaque; 3697702e47cSPaolo Bonzini uint8_t bit_n, group_n; 3707702e47cSPaolo Bonzini 3717702e47cSPaolo Bonzini group_n = get_combiner_group_and_bit(irq, &bit_n); 3727702e47cSPaolo Bonzini 3737702e47cSPaolo Bonzini if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { 3747702e47cSPaolo Bonzini DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" 3757702e47cSPaolo Bonzini , group_n); 3767702e47cSPaolo Bonzini return; 3777702e47cSPaolo Bonzini } 3787702e47cSPaolo Bonzini 3797702e47cSPaolo Bonzini if (level) { 3807702e47cSPaolo Bonzini s->group[group_n].src_pending |= 1 << bit_n; 3817702e47cSPaolo Bonzini } else { 3827702e47cSPaolo Bonzini s->group[group_n].src_pending &= ~(1 << bit_n); 3837702e47cSPaolo Bonzini } 3847702e47cSPaolo Bonzini 3857702e47cSPaolo Bonzini exynos4210_combiner_update(s, group_n); 3867702e47cSPaolo Bonzini } 3877702e47cSPaolo Bonzini 3887702e47cSPaolo Bonzini static void exynos4210_combiner_reset(DeviceState *d) 3897702e47cSPaolo Bonzini { 3907702e47cSPaolo Bonzini struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; 3917702e47cSPaolo Bonzini 3927702e47cSPaolo Bonzini memset(&s->group, 0, sizeof(s->group)); 3937702e47cSPaolo Bonzini memset(&s->reg_set, 0, sizeof(s->reg_set)); 3947702e47cSPaolo Bonzini 3957702e47cSPaolo Bonzini s->reg_set[0xC0 >> 2] = 0x01010101; 3967702e47cSPaolo Bonzini s->reg_set[0xC4 >> 2] = 0x01010101; 3977702e47cSPaolo Bonzini s->reg_set[0xD0 >> 2] = 0x01010101; 3987702e47cSPaolo Bonzini s->reg_set[0xD4 >> 2] = 0x01010101; 3997702e47cSPaolo Bonzini } 4007702e47cSPaolo Bonzini 4017702e47cSPaolo Bonzini static const MemoryRegionOps exynos4210_combiner_ops = { 4027702e47cSPaolo Bonzini .read = exynos4210_combiner_read, 4037702e47cSPaolo Bonzini .write = exynos4210_combiner_write, 4047702e47cSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 4057702e47cSPaolo Bonzini }; 4067702e47cSPaolo Bonzini 4077702e47cSPaolo Bonzini /* 4087702e47cSPaolo Bonzini * Internal Combiner initialization. 4097702e47cSPaolo Bonzini */ 410*c03c6b9cSAndreas Färber static int exynos4210_combiner_init(SysBusDevice *sbd) 4117702e47cSPaolo Bonzini { 412*c03c6b9cSAndreas Färber DeviceState *dev = DEVICE(sbd); 413*c03c6b9cSAndreas Färber Exynos4210CombinerState *s = EXYNOS4210_COMBINER(dev); 4147702e47cSPaolo Bonzini unsigned int i; 4157702e47cSPaolo Bonzini 4167702e47cSPaolo Bonzini /* Allocate general purpose input signals and connect a handler to each of 4177702e47cSPaolo Bonzini * them */ 418*c03c6b9cSAndreas Färber qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); 4197702e47cSPaolo Bonzini 4207702e47cSPaolo Bonzini /* Connect SysBusDev irqs to device specific irqs */ 4217702e47cSPaolo Bonzini for (i = 0; i < IIC_NIRQ; i++) { 422*c03c6b9cSAndreas Färber sysbus_init_irq(sbd, &s->output_irq[i]); 4237702e47cSPaolo Bonzini } 4247702e47cSPaolo Bonzini 4251437c94bSPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_combiner_ops, s, 4267702e47cSPaolo Bonzini "exynos4210-combiner", IIC_REGION_SIZE); 427*c03c6b9cSAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 4287702e47cSPaolo Bonzini 4297702e47cSPaolo Bonzini return 0; 4307702e47cSPaolo Bonzini } 4317702e47cSPaolo Bonzini 4327702e47cSPaolo Bonzini static Property exynos4210_combiner_properties[] = { 4337702e47cSPaolo Bonzini DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), 4347702e47cSPaolo Bonzini DEFINE_PROP_END_OF_LIST(), 4357702e47cSPaolo Bonzini }; 4367702e47cSPaolo Bonzini 4377702e47cSPaolo Bonzini static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) 4387702e47cSPaolo Bonzini { 4397702e47cSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass); 4407702e47cSPaolo Bonzini SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 4417702e47cSPaolo Bonzini 4427702e47cSPaolo Bonzini k->init = exynos4210_combiner_init; 4437702e47cSPaolo Bonzini dc->reset = exynos4210_combiner_reset; 4447702e47cSPaolo Bonzini dc->props = exynos4210_combiner_properties; 4457702e47cSPaolo Bonzini dc->vmsd = &vmstate_exynos4210_combiner; 4467702e47cSPaolo Bonzini } 4477702e47cSPaolo Bonzini 4487702e47cSPaolo Bonzini static const TypeInfo exynos4210_combiner_info = { 449*c03c6b9cSAndreas Färber .name = TYPE_EXYNOS4210_COMBINER, 4507702e47cSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE, 4517702e47cSPaolo Bonzini .instance_size = sizeof(Exynos4210CombinerState), 4527702e47cSPaolo Bonzini .class_init = exynos4210_combiner_class_init, 4537702e47cSPaolo Bonzini }; 4547702e47cSPaolo Bonzini 4557702e47cSPaolo Bonzini static void exynos4210_combiner_register_types(void) 4567702e47cSPaolo Bonzini { 4577702e47cSPaolo Bonzini type_register_static(&exynos4210_combiner_info); 4587702e47cSPaolo Bonzini } 4597702e47cSPaolo Bonzini 4607702e47cSPaolo Bonzini type_init(exynos4210_combiner_register_types) 461