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