balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 1 | /* |
| 2 | * SuperH interrupt controller module |
| 3 | * |
| 4 | * Copyright (c) 2007 Magnus Damm |
| 5 | * Based on sh_timer.c and arm_timer.c by Paul Brook |
| 6 | * Copyright (c) 2005-2006 CodeSourcery. |
| 7 | * |
Matthew Fernandez | 8e31bf3 | 2011-06-26 12:21:35 +1000 | [diff] [blame] | 8 | * This code is licensed under the GPL. |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 9 | */ |
| 10 | |
Paolo Bonzini | 83c9f4c | 2013-02-04 15:40:22 +0100 | [diff] [blame] | 11 | #include "hw/sh_intc.h" |
| 12 | #include "hw/hw.h" |
| 13 | #include "hw/sh.h" |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 14 | |
| 15 | //#define DEBUG_INTC |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 16 | //#define DEBUG_INTC_SOURCES |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 17 | |
| 18 | #define INTC_A7(x) ((x) & 0x1fffffff) |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 19 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 20 | void sh_intc_toggle_source(struct intc_source *source, |
| 21 | int enable_adj, int assert_adj) |
| 22 | { |
| 23 | int enable_changed = 0; |
| 24 | int pending_changed = 0; |
| 25 | int old_pending; |
| 26 | |
| 27 | if ((source->enable_count == source->enable_max) && (enable_adj == -1)) |
| 28 | enable_changed = -1; |
| 29 | |
| 30 | source->enable_count += enable_adj; |
| 31 | |
| 32 | if (source->enable_count == source->enable_max) |
| 33 | enable_changed = 1; |
| 34 | |
| 35 | source->asserted += assert_adj; |
| 36 | |
| 37 | old_pending = source->pending; |
| 38 | source->pending = source->asserted && |
| 39 | (source->enable_count == source->enable_max); |
| 40 | |
| 41 | if (old_pending != source->pending) |
| 42 | pending_changed = 1; |
| 43 | |
| 44 | if (pending_changed) { |
Andreas Färber | d8ed887 | 2013-01-17 22:30:20 +0100 | [diff] [blame] | 45 | CPUState *cpu = CPU(sh_env_get_cpu(first_cpu)); |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 46 | if (source->pending) { |
| 47 | source->parent->pending++; |
Andreas Färber | c3affe5 | 2013-01-18 15:03:43 +0100 | [diff] [blame] | 48 | if (source->parent->pending == 1) { |
| 49 | cpu_interrupt(cpu, CPU_INTERRUPT_HARD); |
| 50 | } |
Andreas Färber | d8ed887 | 2013-01-17 22:30:20 +0100 | [diff] [blame] | 51 | } else { |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 52 | source->parent->pending--; |
Andreas Färber | d8ed887 | 2013-01-17 22:30:20 +0100 | [diff] [blame] | 53 | if (source->parent->pending == 0) { |
| 54 | cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); |
| 55 | } |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 56 | } |
| 57 | } |
| 58 | |
| 59 | if (enable_changed || assert_adj || pending_changed) { |
| 60 | #ifdef DEBUG_INTC_SOURCES |
| 61 | printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", |
| 62 | source->parent->pending, |
| 63 | source->asserted, |
| 64 | source->enable_count, |
| 65 | source->enable_max, |
| 66 | source->vect, |
| 67 | source->asserted ? "asserted " : |
| 68 | assert_adj ? "deasserted" : "", |
| 69 | enable_changed == 1 ? "enabled " : |
| 70 | enable_changed == -1 ? "disabled " : "", |
| 71 | source->pending ? "pending" : ""); |
| 72 | #endif |
| 73 | } |
| 74 | } |
| 75 | |
aurel32 | b79e175 | 2008-12-07 22:46:42 +0000 | [diff] [blame] | 76 | static void sh_intc_set_irq (void *opaque, int n, int level) |
aurel32 | 96e2fc4 | 2008-11-21 21:06:42 +0000 | [diff] [blame] | 77 | { |
| 78 | struct intc_desc *desc = opaque; |
| 79 | struct intc_source *source = &(desc->sources[n]); |
| 80 | |
aurel32 | 4e7ed2d | 2008-11-21 21:06:51 +0000 | [diff] [blame] | 81 | if (level && !source->asserted) |
| 82 | sh_intc_toggle_source(source, 0, 1); |
| 83 | else if (!level && source->asserted) |
| 84 | sh_intc_toggle_source(source, 0, -1); |
aurel32 | 96e2fc4 | 2008-11-21 21:06:42 +0000 | [diff] [blame] | 85 | } |
| 86 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 87 | int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) |
| 88 | { |
| 89 | unsigned int i; |
| 90 | |
| 91 | /* slow: use a linked lists of pending sources instead */ |
| 92 | /* wrong: take interrupt priority into account (one list per priority) */ |
| 93 | |
| 94 | if (imask == 0x0f) { |
| 95 | return -1; /* FIXME, update code to include priority per source */ |
| 96 | } |
| 97 | |
| 98 | for (i = 0; i < desc->nr_sources; i++) { |
| 99 | struct intc_source *source = desc->sources + i; |
| 100 | |
| 101 | if (source->pending) { |
| 102 | #ifdef DEBUG_INTC_SOURCES |
| 103 | printf("sh_intc: (%d) returning interrupt source 0x%x\n", |
| 104 | desc->pending, source->vect); |
| 105 | #endif |
| 106 | return source->vect; |
| 107 | } |
| 108 | } |
| 109 | |
Blue Swirl | 43dc2a6 | 2010-03-18 18:41:57 +0000 | [diff] [blame] | 110 | abort(); |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 111 | } |
| 112 | |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 113 | #define INTC_MODE_NONE 0 |
| 114 | #define INTC_MODE_DUAL_SET 1 |
| 115 | #define INTC_MODE_DUAL_CLR 2 |
| 116 | #define INTC_MODE_ENABLE_REG 3 |
| 117 | #define INTC_MODE_MASK_REG 4 |
| 118 | #define INTC_MODE_IS_PRIO 8 |
| 119 | |
| 120 | static unsigned int sh_intc_mode(unsigned long address, |
| 121 | unsigned long set_reg, unsigned long clr_reg) |
| 122 | { |
| 123 | if ((address != INTC_A7(set_reg)) && |
| 124 | (address != INTC_A7(clr_reg))) |
| 125 | return INTC_MODE_NONE; |
| 126 | |
| 127 | if (set_reg && clr_reg) { |
| 128 | if (address == INTC_A7(set_reg)) |
| 129 | return INTC_MODE_DUAL_SET; |
| 130 | else |
| 131 | return INTC_MODE_DUAL_CLR; |
| 132 | } |
| 133 | |
| 134 | if (set_reg) |
| 135 | return INTC_MODE_ENABLE_REG; |
| 136 | else |
| 137 | return INTC_MODE_MASK_REG; |
| 138 | } |
| 139 | |
| 140 | static void sh_intc_locate(struct intc_desc *desc, |
| 141 | unsigned long address, |
| 142 | unsigned long **datap, |
| 143 | intc_enum **enums, |
| 144 | unsigned int *first, |
| 145 | unsigned int *width, |
| 146 | unsigned int *modep) |
| 147 | { |
| 148 | unsigned int i, mode; |
| 149 | |
| 150 | /* this is slow but works for now */ |
| 151 | |
| 152 | if (desc->mask_regs) { |
| 153 | for (i = 0; i < desc->nr_mask_regs; i++) { |
| 154 | struct intc_mask_reg *mr = desc->mask_regs + i; |
| 155 | |
| 156 | mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); |
| 157 | if (mode == INTC_MODE_NONE) |
| 158 | continue; |
| 159 | |
| 160 | *modep = mode; |
| 161 | *datap = &mr->value; |
| 162 | *enums = mr->enum_ids; |
| 163 | *first = mr->reg_width - 1; |
| 164 | *width = 1; |
| 165 | return; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | if (desc->prio_regs) { |
| 170 | for (i = 0; i < desc->nr_prio_regs; i++) { |
| 171 | struct intc_prio_reg *pr = desc->prio_regs + i; |
| 172 | |
| 173 | mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); |
| 174 | if (mode == INTC_MODE_NONE) |
| 175 | continue; |
| 176 | |
| 177 | *modep = mode | INTC_MODE_IS_PRIO; |
| 178 | *datap = &pr->value; |
| 179 | *enums = pr->enum_ids; |
| 180 | *first = (pr->reg_width / pr->field_width) - 1; |
| 181 | *width = pr->field_width; |
| 182 | return; |
| 183 | } |
| 184 | } |
| 185 | |
Blue Swirl | 43dc2a6 | 2010-03-18 18:41:57 +0000 | [diff] [blame] | 186 | abort(); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 187 | } |
| 188 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 189 | static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, |
| 190 | int enable, int is_group) |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 191 | { |
| 192 | struct intc_source *source = desc->sources + id; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 193 | |
| 194 | if (!id) |
| 195 | return; |
| 196 | |
| 197 | if (!source->next_enum_id && (!source->enable_max || !source->vect)) { |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 198 | #ifdef DEBUG_INTC_SOURCES |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 199 | printf("sh_intc: reserved interrupt source %d modified\n", id); |
| 200 | #endif |
| 201 | return; |
| 202 | } |
| 203 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 204 | if (source->vect) |
| 205 | sh_intc_toggle_source(source, enable ? 1 : -1, 0); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 206 | |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 207 | #ifdef DEBUG_INTC |
| 208 | else { |
| 209 | printf("setting interrupt group %d to %d\n", id, !!enable); |
| 210 | } |
| 211 | #endif |
| 212 | |
| 213 | if ((is_group || !source->vect) && source->next_enum_id) { |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 214 | sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 215 | } |
| 216 | |
| 217 | #ifdef DEBUG_INTC |
| 218 | if (!source->vect) { |
| 219 | printf("setting interrupt group %d to %d - done\n", id, !!enable); |
| 220 | } |
| 221 | #endif |
| 222 | } |
| 223 | |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 224 | static uint64_t sh_intc_read(void *opaque, hwaddr offset, |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 225 | unsigned size) |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 226 | { |
| 227 | struct intc_desc *desc = opaque; |
| 228 | intc_enum *enum_ids = NULL; |
| 229 | unsigned int first = 0; |
| 230 | unsigned int width = 0; |
| 231 | unsigned int mode = 0; |
| 232 | unsigned long *valuep; |
| 233 | |
| 234 | #ifdef DEBUG_INTC |
| 235 | printf("sh_intc_read 0x%lx\n", (unsigned long) offset); |
| 236 | #endif |
| 237 | |
| 238 | sh_intc_locate(desc, (unsigned long)offset, &valuep, |
| 239 | &enum_ids, &first, &width, &mode); |
| 240 | return *valuep; |
| 241 | } |
| 242 | |
Avi Kivity | a8170e5 | 2012-10-23 12:30:10 +0200 | [diff] [blame] | 243 | static void sh_intc_write(void *opaque, hwaddr offset, |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 244 | uint64_t value, unsigned size) |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 245 | { |
| 246 | struct intc_desc *desc = opaque; |
| 247 | intc_enum *enum_ids = NULL; |
| 248 | unsigned int first = 0; |
| 249 | unsigned int width = 0; |
| 250 | unsigned int mode = 0; |
| 251 | unsigned int k; |
| 252 | unsigned long *valuep; |
| 253 | unsigned long mask; |
| 254 | |
| 255 | #ifdef DEBUG_INTC |
| 256 | printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); |
| 257 | #endif |
| 258 | |
| 259 | sh_intc_locate(desc, (unsigned long)offset, &valuep, |
| 260 | &enum_ids, &first, &width, &mode); |
| 261 | |
| 262 | switch (mode) { |
| 263 | case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; |
| 264 | case INTC_MODE_DUAL_SET: value |= *valuep; break; |
| 265 | case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; |
Blue Swirl | 43dc2a6 | 2010-03-18 18:41:57 +0000 | [diff] [blame] | 266 | default: abort(); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 267 | } |
| 268 | |
| 269 | for (k = 0; k <= first; k++) { |
| 270 | mask = ((1 << width) - 1) << ((first - k) * width); |
| 271 | |
| 272 | if ((*valuep & mask) == (value & mask)) |
| 273 | continue; |
| 274 | #if 0 |
| 275 | printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", |
| 276 | k, first, enum_ids[k], (unsigned int)mask); |
| 277 | #endif |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 278 | sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 279 | } |
| 280 | |
| 281 | *valuep = value; |
| 282 | |
| 283 | #ifdef DEBUG_INTC |
| 284 | printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value); |
| 285 | #endif |
| 286 | } |
| 287 | |
Stefan Weil | 12f3083 | 2012-02-25 13:37:13 +0100 | [diff] [blame] | 288 | static const MemoryRegionOps sh_intc_ops = { |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 289 | .read = sh_intc_read, |
| 290 | .write = sh_intc_write, |
| 291 | .endianness = DEVICE_NATIVE_ENDIAN, |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 292 | }; |
| 293 | |
| 294 | struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) |
| 295 | { |
| 296 | if (id) |
| 297 | return desc->sources + id; |
| 298 | |
| 299 | return NULL; |
| 300 | } |
| 301 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 302 | static unsigned int sh_intc_register(MemoryRegion *sysmem, |
| 303 | struct intc_desc *desc, |
| 304 | const unsigned long address, |
| 305 | const char *type, |
| 306 | const char *action, |
| 307 | const unsigned int index) |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 308 | { |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 309 | char name[60]; |
| 310 | MemoryRegion *iomem, *iomem_p4, *iomem_a7; |
| 311 | |
| 312 | if (!address) { |
| 313 | return 0; |
balrog | 5c16736 | 2008-12-07 19:39:58 +0000 | [diff] [blame] | 314 | } |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 315 | |
| 316 | iomem = &desc->iomem; |
| 317 | iomem_p4 = desc->iomem_aliases + index; |
| 318 | iomem_a7 = iomem_p4 + 1; |
| 319 | |
| 320 | #define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s" |
| 321 | snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4"); |
| 322 | memory_region_init_alias(iomem_p4, name, iomem, INTC_A7(address), 4); |
| 323 | memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); |
| 324 | |
| 325 | snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7"); |
| 326 | memory_region_init_alias(iomem_a7, name, iomem, INTC_A7(address), 4); |
| 327 | memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); |
| 328 | #undef SH_INTC_IOMEM_FORMAT |
| 329 | |
| 330 | /* used to increment aliases index */ |
| 331 | return 2; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | static void sh_intc_register_source(struct intc_desc *desc, |
| 335 | intc_enum source, |
| 336 | struct intc_group *groups, |
| 337 | int nr_groups) |
| 338 | { |
| 339 | unsigned int i, k; |
| 340 | struct intc_source *s; |
| 341 | |
| 342 | if (desc->mask_regs) { |
| 343 | for (i = 0; i < desc->nr_mask_regs; i++) { |
| 344 | struct intc_mask_reg *mr = desc->mask_regs + i; |
| 345 | |
malc | b1503cd | 2008-12-22 20:33:55 +0000 | [diff] [blame] | 346 | for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) { |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 347 | if (mr->enum_ids[k] != source) |
| 348 | continue; |
| 349 | |
| 350 | s = sh_intc_source(desc, mr->enum_ids[k]); |
| 351 | if (s) |
| 352 | s->enable_max++; |
| 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | if (desc->prio_regs) { |
| 358 | for (i = 0; i < desc->nr_prio_regs; i++) { |
| 359 | struct intc_prio_reg *pr = desc->prio_regs + i; |
| 360 | |
malc | b1503cd | 2008-12-22 20:33:55 +0000 | [diff] [blame] | 361 | for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) { |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 362 | if (pr->enum_ids[k] != source) |
| 363 | continue; |
| 364 | |
| 365 | s = sh_intc_source(desc, pr->enum_ids[k]); |
| 366 | if (s) |
| 367 | s->enable_max++; |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | if (groups) { |
| 373 | for (i = 0; i < nr_groups; i++) { |
| 374 | struct intc_group *gr = groups + i; |
| 375 | |
malc | b1503cd | 2008-12-22 20:33:55 +0000 | [diff] [blame] | 376 | for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) { |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 377 | if (gr->enum_ids[k] != source) |
| 378 | continue; |
| 379 | |
| 380 | s = sh_intc_source(desc, gr->enum_ids[k]); |
| 381 | if (s) |
| 382 | s->enable_max++; |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | } |
| 388 | |
| 389 | void sh_intc_register_sources(struct intc_desc *desc, |
| 390 | struct intc_vect *vectors, |
| 391 | int nr_vectors, |
| 392 | struct intc_group *groups, |
| 393 | int nr_groups) |
| 394 | { |
| 395 | unsigned int i, k; |
| 396 | struct intc_source *s; |
| 397 | |
| 398 | for (i = 0; i < nr_vectors; i++) { |
| 399 | struct intc_vect *vect = vectors + i; |
| 400 | |
| 401 | sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); |
| 402 | s = sh_intc_source(desc, vect->enum_id); |
Stefan Weil | 6f9faa9 | 2011-07-20 20:56:35 +0200 | [diff] [blame] | 403 | if (s) { |
| 404 | s->vect = vect->vect; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 405 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 406 | #ifdef DEBUG_INTC_SOURCES |
Stefan Weil | 6f9faa9 | 2011-07-20 20:56:35 +0200 | [diff] [blame] | 407 | printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", |
| 408 | vect->enum_id, s->vect, s->enable_count, s->enable_max); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 409 | #endif |
Stefan Weil | 6f9faa9 | 2011-07-20 20:56:35 +0200 | [diff] [blame] | 410 | } |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 411 | } |
| 412 | |
| 413 | if (groups) { |
| 414 | for (i = 0; i < nr_groups; i++) { |
| 415 | struct intc_group *gr = groups + i; |
| 416 | |
| 417 | s = sh_intc_source(desc, gr->enum_id); |
| 418 | s->next_enum_id = gr->enum_ids[0]; |
| 419 | |
malc | b1503cd | 2008-12-22 20:33:55 +0000 | [diff] [blame] | 420 | for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) { |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 421 | if (!gr->enum_ids[k]) |
| 422 | continue; |
| 423 | |
| 424 | s = sh_intc_source(desc, gr->enum_ids[k - 1]); |
| 425 | s->next_enum_id = gr->enum_ids[k]; |
| 426 | } |
| 427 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 428 | #ifdef DEBUG_INTC_SOURCES |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 429 | printf("sh_intc: registered group %d (%d/%d)\n", |
| 430 | gr->enum_id, s->enable_count, s->enable_max); |
| 431 | #endif |
| 432 | } |
| 433 | } |
| 434 | } |
| 435 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 436 | int sh_intc_init(MemoryRegion *sysmem, |
| 437 | struct intc_desc *desc, |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 438 | int nr_sources, |
| 439 | struct intc_mask_reg *mask_regs, |
| 440 | int nr_mask_regs, |
| 441 | struct intc_prio_reg *prio_regs, |
| 442 | int nr_prio_regs) |
| 443 | { |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 444 | unsigned int i, j; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 445 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 446 | desc->pending = 0; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 447 | desc->nr_sources = nr_sources; |
| 448 | desc->mask_regs = mask_regs; |
| 449 | desc->nr_mask_regs = nr_mask_regs; |
| 450 | desc->prio_regs = prio_regs; |
| 451 | desc->nr_prio_regs = nr_prio_regs; |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 452 | /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases). |
| 453 | **/ |
| 454 | desc->iomem_aliases = g_new0(MemoryRegion, |
| 455 | (nr_mask_regs + nr_prio_regs) * 4); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 456 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 457 | j = 0; |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 458 | i = sizeof(struct intc_source) * nr_sources; |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 459 | desc->sources = g_malloc0(i); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 460 | |
ths | e96e204 | 2007-12-02 06:18:24 +0000 | [diff] [blame] | 461 | for (i = 0; i < desc->nr_sources; i++) { |
| 462 | struct intc_source *source = desc->sources + i; |
| 463 | |
| 464 | source->parent = desc; |
| 465 | } |
aurel32 | 96e2fc4 | 2008-11-21 21:06:42 +0000 | [diff] [blame] | 466 | |
| 467 | desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 468 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 469 | memory_region_init_io(&desc->iomem, &sh_intc_ops, desc, |
| 470 | "interrupt-controller", 0x100000000ULL); |
| 471 | |
| 472 | #define INT_REG_PARAMS(reg_struct, type, action, j) \ |
| 473 | reg_struct->action##_reg, #type, #action, j |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 474 | if (desc->mask_regs) { |
| 475 | for (i = 0; i < desc->nr_mask_regs; i++) { |
| 476 | struct intc_mask_reg *mr = desc->mask_regs + i; |
| 477 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 478 | j += sh_intc_register(sysmem, desc, |
| 479 | INT_REG_PARAMS(mr, mask, set, j)); |
| 480 | j += sh_intc_register(sysmem, desc, |
| 481 | INT_REG_PARAMS(mr, mask, clr, j)); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 482 | } |
| 483 | } |
| 484 | |
| 485 | if (desc->prio_regs) { |
| 486 | for (i = 0; i < desc->nr_prio_regs; i++) { |
| 487 | struct intc_prio_reg *pr = desc->prio_regs + i; |
| 488 | |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 489 | j += sh_intc_register(sysmem, desc, |
| 490 | INT_REG_PARAMS(pr, prio, set, j)); |
| 491 | j += sh_intc_register(sysmem, desc, |
| 492 | INT_REG_PARAMS(pr, prio, clr, j)); |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 493 | } |
| 494 | } |
Benoît Canet | b279e5e | 2011-11-17 14:23:01 +0100 | [diff] [blame] | 495 | #undef INT_REG_PARAMS |
balrog | 80f515e | 2007-10-04 21:53:55 +0000 | [diff] [blame] | 496 | |
| 497 | return 0; |
| 498 | } |
balrog | c6d86a3 | 2008-12-07 18:49:57 +0000 | [diff] [blame] | 499 | |
| 500 | /* Assert level <n> IRL interrupt. |
| 501 | 0:deassert. 1:lowest priority,... 15:highest priority. */ |
| 502 | void sh_intc_set_irl(void *opaque, int n, int level) |
| 503 | { |
| 504 | struct intc_source *s = opaque; |
| 505 | int i, irl = level ^ 15; |
| 506 | for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { |
| 507 | if (i == irl) |
| 508 | sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); |
| 509 | else |
| 510 | if (s->asserted) |
| 511 | sh_intc_toggle_source(s, 0, -1); |
| 512 | } |
| 513 | } |