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 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_NGRP 64 /* Internal Interrupt Combiner 52 Groups number */ 53 #define IIC_NIRQ (IIC_NGRP * 8)/* Internal Interrupt Combiner 54 Interrupts number */ 55 #define IIC_REGION_SIZE 0x108 /* Size of memory mapped region */ 56 #define IIC_REGSET_SIZE 0x41 57 58 /* 59 * State for each output signal of internal combiner 60 */ 61 typedef struct CombinerGroupState { 62 uint8_t src_mask; /* 1 - source enabled, 0 - disabled */ 63 uint8_t src_pending; /* Pending source interrupts before masking */ 64 } CombinerGroupState; 65 66 #define TYPE_EXYNOS4210_COMBINER "exynos4210.combiner" 67 typedef struct Exynos4210CombinerState Exynos4210CombinerState; 68 DECLARE_INSTANCE_CHECKER(Exynos4210CombinerState, EXYNOS4210_COMBINER, 69 TYPE_EXYNOS4210_COMBINER) 70 71 struct Exynos4210CombinerState { 72 SysBusDevice parent_obj; 73 74 MemoryRegion iomem; 75 76 struct CombinerGroupState group[IIC_NGRP]; 77 uint32_t reg_set[IIC_REGSET_SIZE]; 78 uint32_t icipsr[2]; 79 uint32_t external; /* 1 means that this combiner is external */ 80 81 qemu_irq output_irq[IIC_NGRP]; 82 }; 83 84 static const VMStateDescription vmstate_exynos4210_combiner_group_state = { 85 .name = "exynos4210.combiner.groupstate", 86 .version_id = 1, 87 .minimum_version_id = 1, 88 .fields = (VMStateField[]) { 89 VMSTATE_UINT8(src_mask, CombinerGroupState), 90 VMSTATE_UINT8(src_pending, CombinerGroupState), 91 VMSTATE_END_OF_LIST() 92 } 93 }; 94 95 static const VMStateDescription vmstate_exynos4210_combiner = { 96 .name = "exynos4210.combiner", 97 .version_id = 1, 98 .minimum_version_id = 1, 99 .fields = (VMStateField[]) { 100 VMSTATE_STRUCT_ARRAY(group, Exynos4210CombinerState, IIC_NGRP, 0, 101 vmstate_exynos4210_combiner_group_state, CombinerGroupState), 102 VMSTATE_UINT32_ARRAY(reg_set, Exynos4210CombinerState, 103 IIC_REGSET_SIZE), 104 VMSTATE_UINT32_ARRAY(icipsr, Exynos4210CombinerState, 2), 105 VMSTATE_UINT32(external, Exynos4210CombinerState), 106 VMSTATE_END_OF_LIST() 107 } 108 }; 109 110 /* 111 * Get Combiner input GPIO into irqs structure 112 */ 113 void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, 114 int ext) 115 { 116 int n; 117 int bit; 118 int max; 119 qemu_irq *irq; 120 121 max = ext ? EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ : 122 EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; 123 irq = ext ? irqs->ext_combiner_irq : irqs->int_combiner_irq; 124 125 /* 126 * Some IRQs of Int/External Combiner are going to two Combiners groups, 127 * so let split them. 128 */ 129 for (n = 0; n < max; n++) { 130 131 bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n); 132 133 switch (n) { 134 /* MDNIE_LCD1 INTG1 */ 135 case EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 0) ... 136 EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 3): 137 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 138 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(0, bit + 4)]); 139 continue; 140 141 /* TMU INTG3 */ 142 case EXYNOS4210_COMBINER_GET_IRQ_NUM(3, 4): 143 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 144 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(2, bit)]); 145 continue; 146 147 /* LCD1 INTG12 */ 148 case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 0) ... 149 EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 3): 150 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 151 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(11, bit + 4)]); 152 continue; 153 154 /* Multi-Core Timer INTG12 */ 155 case EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4) ... 156 EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 8): 157 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 158 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 159 continue; 160 161 /* Multi-Core Timer INTG35 */ 162 case EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 4) ... 163 EXYNOS4210_COMBINER_GET_IRQ_NUM(35, 8): 164 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 165 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 166 continue; 167 168 /* Multi-Core Timer INTG51 */ 169 case EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 4) ... 170 EXYNOS4210_COMBINER_GET_IRQ_NUM(51, 8): 171 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 172 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 173 continue; 174 175 /* Multi-Core Timer INTG53 */ 176 case EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 4) ... 177 EXYNOS4210_COMBINER_GET_IRQ_NUM(53, 8): 178 irq[n] = qemu_irq_split(qdev_get_gpio_in(dev, n), 179 irq[EXYNOS4210_COMBINER_GET_IRQ_NUM(1, bit + 4)]); 180 continue; 181 } 182 183 irq[n] = qdev_get_gpio_in(dev, n); 184 } 185 } 186 187 static uint64_t 188 exynos4210_combiner_read(void *opaque, hwaddr offset, unsigned size) 189 { 190 struct Exynos4210CombinerState *s = 191 (struct Exynos4210CombinerState *)opaque; 192 uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 193 get a start of corresponding group quad */ 194 uint32_t grp_quad_base_n; /* Base of group quad */ 195 uint32_t reg_n; /* Register number inside the quad */ 196 uint32_t val; 197 198 req_quad_base_n = offset >> 4; 199 grp_quad_base_n = req_quad_base_n << 2; 200 reg_n = (offset - (req_quad_base_n << 4)) >> 2; 201 202 if (req_quad_base_n >= IIC_NGRP) { 203 /* Read of ICIPSR register */ 204 return s->icipsr[reg_n]; 205 } 206 207 val = 0; 208 209 switch (reg_n) { 210 /* IISTR */ 211 case 2: 212 val |= s->group[grp_quad_base_n].src_pending; 213 val |= s->group[grp_quad_base_n + 1].src_pending << 8; 214 val |= s->group[grp_quad_base_n + 2].src_pending << 16; 215 val |= s->group[grp_quad_base_n + 3].src_pending << 24; 216 break; 217 /* IIMSR */ 218 case 3: 219 val |= s->group[grp_quad_base_n].src_mask & 220 s->group[grp_quad_base_n].src_pending; 221 val |= (s->group[grp_quad_base_n + 1].src_mask & 222 s->group[grp_quad_base_n + 1].src_pending) << 8; 223 val |= (s->group[grp_quad_base_n + 2].src_mask & 224 s->group[grp_quad_base_n + 2].src_pending) << 16; 225 val |= (s->group[grp_quad_base_n + 3].src_mask & 226 s->group[grp_quad_base_n + 3].src_pending) << 24; 227 break; 228 default: 229 if (offset >> 2 >= IIC_REGSET_SIZE) { 230 hw_error("exynos4210.combiner: overflow of reg_set by 0x" 231 TARGET_FMT_plx "offset\n", offset); 232 } 233 val = s->reg_set[offset >> 2]; 234 } 235 return val; 236 } 237 238 static void exynos4210_combiner_update(void *opaque, uint8_t group_n) 239 { 240 struct Exynos4210CombinerState *s = 241 (struct Exynos4210CombinerState *)opaque; 242 243 /* Send interrupt if needed */ 244 if (s->group[group_n].src_mask & s->group[group_n].src_pending) { 245 #ifdef DEBUG_COMBINER 246 if (group_n != 26) { 247 /* skip uart */ 248 DPRINTF("%s raise IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 249 } 250 #endif 251 252 /* Set Combiner interrupt pending status after masking */ 253 if (group_n >= 32) { 254 s->icipsr[1] |= 1 << (group_n - 32); 255 } else { 256 s->icipsr[0] |= 1 << group_n; 257 } 258 259 qemu_irq_raise(s->output_irq[group_n]); 260 } else { 261 #ifdef DEBUG_COMBINER 262 if (group_n != 26) { 263 /* skip uart */ 264 DPRINTF("%s lower IRQ[%d]\n", s->external ? "EXT" : "INT", group_n); 265 } 266 #endif 267 268 /* Set Combiner interrupt pending status after masking */ 269 if (group_n >= 32) { 270 s->icipsr[1] &= ~(1 << (group_n - 32)); 271 } else { 272 s->icipsr[0] &= ~(1 << group_n); 273 } 274 275 qemu_irq_lower(s->output_irq[group_n]); 276 } 277 } 278 279 static void exynos4210_combiner_write(void *opaque, hwaddr offset, 280 uint64_t val, unsigned size) 281 { 282 struct Exynos4210CombinerState *s = 283 (struct Exynos4210CombinerState *)opaque; 284 uint32_t req_quad_base_n; /* Base of registers quad. Multiply it by 4 and 285 get a start of corresponding group quad */ 286 uint32_t grp_quad_base_n; /* Base of group quad */ 287 uint32_t reg_n; /* Register number inside the quad */ 288 289 req_quad_base_n = offset >> 4; 290 grp_quad_base_n = req_quad_base_n << 2; 291 reg_n = (offset - (req_quad_base_n << 4)) >> 2; 292 293 if (req_quad_base_n >= IIC_NGRP) { 294 hw_error("exynos4210.combiner: unallowed write access at offset 0x" 295 TARGET_FMT_plx "\n", offset); 296 return; 297 } 298 299 if (reg_n > 1) { 300 hw_error("exynos4210.combiner: unallowed write access at offset 0x" 301 TARGET_FMT_plx "\n", offset); 302 return; 303 } 304 305 if (offset >> 2 >= IIC_REGSET_SIZE) { 306 hw_error("exynos4210.combiner: overflow of reg_set by 0x" 307 TARGET_FMT_plx "offset\n", offset); 308 } 309 s->reg_set[offset >> 2] = val; 310 311 switch (reg_n) { 312 /* IIESR */ 313 case 0: 314 /* FIXME: what if irq is pending, allowed by mask, and we allow it 315 * again. Interrupt will rise again! */ 316 317 DPRINTF("%s enable IRQ for groups %d, %d, %d, %d\n", 318 s->external ? "EXT" : "INT", 319 grp_quad_base_n, 320 grp_quad_base_n + 1, 321 grp_quad_base_n + 2, 322 grp_quad_base_n + 3); 323 324 /* Enable interrupt sources */ 325 s->group[grp_quad_base_n].src_mask |= val & 0xFF; 326 s->group[grp_quad_base_n + 1].src_mask |= (val & 0xFF00) >> 8; 327 s->group[grp_quad_base_n + 2].src_mask |= (val & 0xFF0000) >> 16; 328 s->group[grp_quad_base_n + 3].src_mask |= (val & 0xFF000000) >> 24; 329 330 exynos4210_combiner_update(s, grp_quad_base_n); 331 exynos4210_combiner_update(s, grp_quad_base_n + 1); 332 exynos4210_combiner_update(s, grp_quad_base_n + 2); 333 exynos4210_combiner_update(s, grp_quad_base_n + 3); 334 break; 335 /* IIECR */ 336 case 1: 337 DPRINTF("%s disable IRQ for groups %d, %d, %d, %d\n", 338 s->external ? "EXT" : "INT", 339 grp_quad_base_n, 340 grp_quad_base_n + 1, 341 grp_quad_base_n + 2, 342 grp_quad_base_n + 3); 343 344 /* Disable interrupt sources */ 345 s->group[grp_quad_base_n].src_mask &= ~(val & 0xFF); 346 s->group[grp_quad_base_n + 1].src_mask &= ~((val & 0xFF00) >> 8); 347 s->group[grp_quad_base_n + 2].src_mask &= ~((val & 0xFF0000) >> 16); 348 s->group[grp_quad_base_n + 3].src_mask &= ~((val & 0xFF000000) >> 24); 349 350 exynos4210_combiner_update(s, grp_quad_base_n); 351 exynos4210_combiner_update(s, grp_quad_base_n + 1); 352 exynos4210_combiner_update(s, grp_quad_base_n + 2); 353 exynos4210_combiner_update(s, grp_quad_base_n + 3); 354 break; 355 default: 356 hw_error("exynos4210.combiner: unallowed write access at offset 0x" 357 TARGET_FMT_plx "\n", offset); 358 break; 359 } 360 } 361 362 /* Get combiner group and bit from irq number */ 363 static uint8_t get_combiner_group_and_bit(int irq, uint8_t *bit) 364 { 365 *bit = irq - ((irq >> 3) << 3); 366 return irq >> 3; 367 } 368 369 /* Process a change in an external IRQ input. */ 370 static void exynos4210_combiner_handler(void *opaque, int irq, int level) 371 { 372 struct Exynos4210CombinerState *s = 373 (struct Exynos4210CombinerState *)opaque; 374 uint8_t bit_n, group_n; 375 376 group_n = get_combiner_group_and_bit(irq, &bit_n); 377 378 if (s->external && group_n >= EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ) { 379 DPRINTF("%s unallowed IRQ group 0x%x\n", s->external ? "EXT" : "INT" 380 , group_n); 381 return; 382 } 383 384 if (level) { 385 s->group[group_n].src_pending |= 1 << bit_n; 386 } else { 387 s->group[group_n].src_pending &= ~(1 << bit_n); 388 } 389 390 exynos4210_combiner_update(s, group_n); 391 } 392 393 static void exynos4210_combiner_reset(DeviceState *d) 394 { 395 struct Exynos4210CombinerState *s = (struct Exynos4210CombinerState *)d; 396 397 memset(&s->group, 0, sizeof(s->group)); 398 memset(&s->reg_set, 0, sizeof(s->reg_set)); 399 400 s->reg_set[0xC0 >> 2] = 0x01010101; 401 s->reg_set[0xC4 >> 2] = 0x01010101; 402 s->reg_set[0xD0 >> 2] = 0x01010101; 403 s->reg_set[0xD4 >> 2] = 0x01010101; 404 } 405 406 static const MemoryRegionOps exynos4210_combiner_ops = { 407 .read = exynos4210_combiner_read, 408 .write = exynos4210_combiner_write, 409 .endianness = DEVICE_NATIVE_ENDIAN, 410 }; 411 412 /* 413 * Internal Combiner initialization. 414 */ 415 static void exynos4210_combiner_init(Object *obj) 416 { 417 DeviceState *dev = DEVICE(obj); 418 Exynos4210CombinerState *s = EXYNOS4210_COMBINER(obj); 419 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 420 unsigned int i; 421 422 /* Allocate general purpose input signals and connect a handler to each of 423 * them */ 424 qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); 425 426 /* Connect SysBusDev irqs to device specific irqs */ 427 for (i = 0; i < IIC_NGRP; i++) { 428 sysbus_init_irq(sbd, &s->output_irq[i]); 429 } 430 431 memory_region_init_io(&s->iomem, obj, &exynos4210_combiner_ops, s, 432 "exynos4210-combiner", IIC_REGION_SIZE); 433 sysbus_init_mmio(sbd, &s->iomem); 434 } 435 436 static Property exynos4210_combiner_properties[] = { 437 DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), 438 DEFINE_PROP_END_OF_LIST(), 439 }; 440 441 static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) 442 { 443 DeviceClass *dc = DEVICE_CLASS(klass); 444 445 dc->reset = exynos4210_combiner_reset; 446 device_class_set_props(dc, exynos4210_combiner_properties); 447 dc->vmsd = &vmstate_exynos4210_combiner; 448 } 449 450 static const TypeInfo exynos4210_combiner_info = { 451 .name = TYPE_EXYNOS4210_COMBINER, 452 .parent = TYPE_SYS_BUS_DEVICE, 453 .instance_size = sizeof(Exynos4210CombinerState), 454 .instance_init = exynos4210_combiner_init, 455 .class_init = exynos4210_combiner_class_init, 456 }; 457 458 static void exynos4210_combiner_register_types(void) 459 { 460 type_register_static(&exynos4210_combiner_info); 461 } 462 463 type_init(exynos4210_combiner_register_types) 464