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