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