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