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