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