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