Commit 80f515e63688f43b7800027c233ec7139cf3375b
1 parent
30d6eaca
sh775x interrupt controller by Magnus Damm.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3327 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
631 additions
and
91 deletions
Makefile.target
... | ... | @@ -484,7 +484,7 @@ CPPFLAGS += -DHAS_AUDIO |
484 | 484 | endif |
485 | 485 | ifeq ($(TARGET_BASE_ARCH), sh4) |
486 | 486 | VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o |
487 | -VL_OBJS+= sh_timer.o ptimer.o sh_serial.o | |
487 | +VL_OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o | |
488 | 488 | endif |
489 | 489 | ifeq ($(TARGET_BASE_ARCH), m68k) |
490 | 490 | VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | ... | ... |
hw/sh7750.c
1 | 1 | /* |
2 | 2 | * SH7750 device |
3 | 3 | * |
4 | + * Copyright (c) 2007 Magnus Damm | |
4 | 5 | * Copyright (c) 2005 Samuel Tardieu |
5 | 6 | * |
6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
... | ... | @@ -26,6 +27,7 @@ |
26 | 27 | #include "vl.h" |
27 | 28 | #include "sh7750_regs.h" |
28 | 29 | #include "sh7750_regnames.h" |
30 | +#include "sh_intc.h" | |
29 | 31 | |
30 | 32 | #define NB_DEVICES 4 |
31 | 33 | |
... | ... | @@ -53,15 +55,10 @@ typedef struct SH7750State { |
53 | 55 | sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ |
54 | 56 | |
55 | 57 | uint16_t icr; |
56 | - uint16_t ipra; | |
57 | - uint16_t iprb; | |
58 | - uint16_t iprc; | |
59 | - uint16_t iprd; | |
60 | - uint32_t intpri00; | |
61 | - uint32_t intmsk00; | |
62 | 58 | /* Cache */ |
63 | 59 | uint32_t ccr; |
64 | 60 | |
61 | + struct intc_desc intc; | |
65 | 62 | } SH7750State; |
66 | 63 | |
67 | 64 | |
... | ... | @@ -219,14 +216,6 @@ static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr) |
219 | 216 | return portb_lines(s); |
220 | 217 | case 0x1fd00000: |
221 | 218 | return s->icr; |
222 | - case 0x1fd00004: | |
223 | - return s->ipra; | |
224 | - case 0x1fd00008: | |
225 | - return s->iprb; | |
226 | - case 0x1fd0000c: | |
227 | - return s->iprc; | |
228 | - case 0x1fd00010: | |
229 | - return s->iprd; | |
230 | 219 | default: |
231 | 220 | error_access("word read", addr); |
232 | 221 | assert(0); |
... | ... | @@ -262,14 +251,6 @@ static uint32_t sh7750_mem_readl(void *opaque, target_phys_addr_t addr) |
262 | 251 | return 0x00110000; /* Minimum caches */ |
263 | 252 | case 0x1f000044: /* Processor version PRR */ |
264 | 253 | return 0x00000100; /* SH7750R */ |
265 | - case 0x1e080000: | |
266 | - return s->intpri00; | |
267 | - case 0x1e080020: | |
268 | - return 0; | |
269 | - case 0x1e080040: | |
270 | - return s->intmsk00; | |
271 | - case 0x1e080060: | |
272 | - return 0; | |
273 | 254 | default: |
274 | 255 | error_access("long read", addr); |
275 | 256 | assert(0); |
... | ... | @@ -331,18 +312,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, |
331 | 312 | case 0x1fd00000: |
332 | 313 | s->icr = mem_value; |
333 | 314 | return; |
334 | - case 0x1fd00004: | |
335 | - s->ipra = mem_value; | |
336 | - return; | |
337 | - case 0x1fd00008: | |
338 | - s->iprb = mem_value; | |
339 | - return; | |
340 | - case 0x1fd0000c: | |
341 | - s->iprc = mem_value; | |
342 | - return; | |
343 | - case 0x1fd00010: | |
344 | - s->iprd = mem_value; | |
345 | - return; | |
346 | 315 | default: |
347 | 316 | error_access("word write", addr); |
348 | 317 | assert(0); |
... | ... | @@ -407,16 +376,6 @@ static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr, |
407 | 376 | case SH7750_CCR_A7: |
408 | 377 | s->ccr = mem_value; |
409 | 378 | return; |
410 | - case 0x1e080000: | |
411 | - s->intpri00 = mem_value; | |
412 | - return; | |
413 | - case 0x1e080020: | |
414 | - return; | |
415 | - case 0x1e080040: | |
416 | - s->intmsk00 = mem_value; | |
417 | - return; | |
418 | - case 0x1e080060: | |
419 | - return; | |
420 | 379 | default: |
421 | 380 | error_access("long write", addr); |
422 | 381 | assert(0); |
... | ... | @@ -435,10 +394,144 @@ static CPUWriteMemoryFunc *sh7750_mem_write[] = { |
435 | 394 | sh7750_mem_writel |
436 | 395 | }; |
437 | 396 | |
397 | +/* sh775x interrupt controller tables for sh_intc.c | |
398 | + * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c | |
399 | + */ | |
400 | + | |
401 | +enum { | |
402 | + UNUSED = 0, | |
403 | + | |
404 | + /* interrupt sources */ | |
405 | + IRL0, IRL1, IRL2, IRL3, /* only IRLM mode supported */ | |
406 | + HUDI, GPIOI, | |
407 | + DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3, | |
408 | + DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7, | |
409 | + DMAC_DMAE, | |
410 | + PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, | |
411 | + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, | |
412 | + TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI, | |
413 | + RTC_ATI, RTC_PRI, RTC_CUI, | |
414 | + SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI, | |
415 | + SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI, | |
416 | + WDT, | |
417 | + REF_RCMI, REF_ROVI, | |
418 | + | |
419 | + /* interrupt groups */ | |
420 | + DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF, | |
421 | + | |
422 | + NR_SOURCES, | |
423 | +}; | |
424 | + | |
425 | +static struct intc_vect vectors[] = { | |
426 | + INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), | |
427 | + INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), | |
428 | + INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460), | |
429 | + INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0), | |
430 | + INTC_VECT(RTC_CUI, 0x4c0), | |
431 | + INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500), | |
432 | + INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540), | |
433 | + INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720), | |
434 | + INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760), | |
435 | + INTC_VECT(WDT, 0x560), | |
436 | + INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0), | |
437 | +}; | |
438 | + | |
439 | +static struct intc_group groups[] = { | |
440 | + INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI), | |
441 | + INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), | |
442 | + INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI), | |
443 | + INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI), | |
444 | + INTC_GROUP(REF, REF_RCMI, REF_ROVI), | |
445 | +}; | |
446 | + | |
447 | +static struct intc_prio_reg prio_registers[] = { | |
448 | + { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, | |
449 | + { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, | |
450 | + { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, | |
451 | + { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, | |
452 | + { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, | |
453 | + TMU4, TMU3, | |
454 | + PCIC1, PCIC0_PCISERR } }, | |
455 | +}; | |
456 | + | |
457 | +/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */ | |
458 | + | |
459 | +static struct intc_vect vectors_dma4[] = { | |
460 | + INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), | |
461 | + INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), | |
462 | + INTC_VECT(DMAC_DMAE, 0x6c0), | |
463 | +}; | |
464 | + | |
465 | +static struct intc_group groups_dma4[] = { | |
466 | + INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, | |
467 | + DMAC_DMTE3, DMAC_DMAE), | |
468 | +}; | |
469 | + | |
470 | +/* SH7750R and SH7751R both have 8-channel DMA controllers */ | |
471 | + | |
472 | +static struct intc_vect vectors_dma8[] = { | |
473 | + INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), | |
474 | + INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), | |
475 | + INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0), | |
476 | + INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0), | |
477 | + INTC_VECT(DMAC_DMAE, 0x6c0), | |
478 | +}; | |
479 | + | |
480 | +static struct intc_group groups_dma8[] = { | |
481 | + INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, | |
482 | + DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5, | |
483 | + DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE), | |
484 | +}; | |
485 | + | |
486 | +/* SH7750R, SH7751 and SH7751R all have two extra timer channels */ | |
487 | + | |
488 | +static struct intc_vect vectors_tmu34[] = { | |
489 | + INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), | |
490 | +}; | |
491 | + | |
492 | +static struct intc_mask_reg mask_registers[] = { | |
493 | + { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ | |
494 | + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
495 | + 0, 0, 0, 0, 0, 0, TMU4, TMU3, | |
496 | + PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, | |
497 | + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, | |
498 | + PCIC1_PCIDMA3, PCIC0_PCISERR } }, | |
499 | +}; | |
500 | + | |
501 | +/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */ | |
502 | + | |
503 | +static struct intc_vect vectors_irlm[] = { | |
504 | + INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), | |
505 | + INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), | |
506 | +}; | |
507 | + | |
508 | +/* SH7751 and SH7751R both have PCI */ | |
509 | + | |
510 | +static struct intc_vect vectors_pci[] = { | |
511 | + INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), | |
512 | + INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), | |
513 | + INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), | |
514 | + INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), | |
515 | +}; | |
516 | + | |
517 | +static struct intc_group groups_pci[] = { | |
518 | + INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, | |
519 | + PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), | |
520 | +}; | |
521 | + | |
522 | +#define SH_CPU_SH7750 (1 << 0) | |
523 | +#define SH_CPU_SH7750S (1 << 1) | |
524 | +#define SH_CPU_SH7750R (1 << 2) | |
525 | +#define SH_CPU_SH7751 (1 << 3) | |
526 | +#define SH_CPU_SH7751R (1 << 4) | |
527 | +#define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R) | |
528 | +#define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R) | |
529 | + | |
438 | 530 | SH7750State *sh7750_init(CPUSH4State * cpu) |
439 | 531 | { |
440 | 532 | SH7750State *s; |
441 | 533 | int sh7750_io_memory; |
534 | + int cpu_model = SH_CPU_SH7751R; /* for now */ | |
442 | 535 | |
443 | 536 | s = qemu_mallocz(sizeof(SH7750State)); |
444 | 537 | s->cpu = cpu; |
... | ... | @@ -448,6 +541,14 @@ SH7750State *sh7750_init(CPUSH4State * cpu) |
448 | 541 | sh7750_mem_write, s); |
449 | 542 | cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); |
450 | 543 | |
544 | + sh_intc_init(&s->intc, NR_SOURCES, | |
545 | + _INTC_ARRAY(mask_registers), | |
546 | + _INTC_ARRAY(prio_registers)); | |
547 | + | |
548 | + sh_intc_register_sources(&s->intc, | |
549 | + _INTC_ARRAY(vectors), | |
550 | + _INTC_ARRAY(groups)); | |
551 | + | |
451 | 552 | sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]); |
452 | 553 | sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF, |
453 | 554 | s->periph_freq, serial_hds[1]); |
... | ... | @@ -455,6 +556,38 @@ SH7750State *sh7750_init(CPUSH4State * cpu) |
455 | 556 | tmu012_init(0x1fd80000, |
456 | 557 | TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, |
457 | 558 | s->periph_freq); |
458 | - tmu012_init(0x1e100000, 0, s->periph_freq); | |
559 | + | |
560 | + | |
561 | + if (cpu_model & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { | |
562 | + sh_intc_register_sources(&s->intc, | |
563 | + _INTC_ARRAY(vectors_dma4), | |
564 | + _INTC_ARRAY(groups_dma4)); | |
565 | + } | |
566 | + | |
567 | + if (cpu_model & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { | |
568 | + sh_intc_register_sources(&s->intc, | |
569 | + _INTC_ARRAY(vectors_dma8), | |
570 | + _INTC_ARRAY(groups_dma8)); | |
571 | + } | |
572 | + | |
573 | + if (cpu_model & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) { | |
574 | + sh_intc_register_sources(&s->intc, | |
575 | + _INTC_ARRAY(vectors_tmu34), | |
576 | + _INTC_ARRAY(NULL)); | |
577 | + tmu012_init(0x1e100000, 0, s->periph_freq); | |
578 | + } | |
579 | + | |
580 | + if (cpu_model & (SH_CPU_SH7751_ALL)) { | |
581 | + sh_intc_register_sources(&s->intc, | |
582 | + _INTC_ARRAY(vectors_pci), | |
583 | + _INTC_ARRAY(groups_pci)); | |
584 | + } | |
585 | + | |
586 | + if (cpu_model & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) { | |
587 | + sh_intc_register_sources(&s->intc, | |
588 | + _INTC_ARRAY(vectors_irlm), | |
589 | + _INTC_ARRAY(NULL)); | |
590 | + } | |
591 | + | |
459 | 592 | return s; |
460 | 593 | } | ... | ... |
hw/sh7750_regnames.c
... | ... | @@ -76,9 +76,6 @@ static regname_t regnames[] = { |
76 | 76 | REGNAME(SH7750_PDTRB_A7) |
77 | 77 | REGNAME(SH7750_GPIOIC_A7) |
78 | 78 | REGNAME(SH7750_ICR_A7) |
79 | - REGNAME(SH7750_IPRA_A7) | |
80 | - REGNAME(SH7750_IPRB_A7) | |
81 | - REGNAME(SH7750_IPRC_A7) | |
82 | 79 | REGNAME(SH7750_BCR3_A7) |
83 | 80 | REGNAME(SH7750_BCR4_A7) |
84 | 81 | REGNAME(SH7750_PRECHARGE0_A7) | ... | ... |
hw/sh7750_regs.h
... | ... | @@ -1241,48 +1241,6 @@ |
1241 | 1241 | #define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent |
1242 | 1242 | interrupt requests */ |
1243 | 1243 | |
1244 | -/* Interrupt Priority Register A - IPRA (half) */ | |
1245 | -#define SH7750_IPRA_REGOFS 0xD00004 /* offset */ | |
1246 | -#define SH7750_IPRA SH7750_P4_REG32(SH7750_IPRA_REGOFS) | |
1247 | -#define SH7750_IPRA_A7 SH7750_A7_REG32(SH7750_IPRA_REGOFS) | |
1248 | - | |
1249 | -#define SH7750_IPRA_TMU0 0xF000 /* TMU0 interrupt priority */ | |
1250 | -#define SH7750_IPRA_TMU0_S 12 | |
1251 | -#define SH7750_IPRA_TMU1 0x0F00 /* TMU1 interrupt priority */ | |
1252 | -#define SH7750_IPRA_TMU1_S 8 | |
1253 | -#define SH7750_IPRA_TMU2 0x00F0 /* TMU2 interrupt priority */ | |
1254 | -#define SH7750_IPRA_TMU2_S 4 | |
1255 | -#define SH7750_IPRA_RTC 0x000F /* RTC interrupt priority */ | |
1256 | -#define SH7750_IPRA_RTC_S 0 | |
1257 | - | |
1258 | -/* Interrupt Priority Register B - IPRB (half) */ | |
1259 | -#define SH7750_IPRB_REGOFS 0xD00008 /* offset */ | |
1260 | -#define SH7750_IPRB SH7750_P4_REG32(SH7750_IPRB_REGOFS) | |
1261 | -#define SH7750_IPRB_A7 SH7750_A7_REG32(SH7750_IPRB_REGOFS) | |
1262 | - | |
1263 | -#define SH7750_IPRB_WDT 0xF000 /* WDT interrupt priority */ | |
1264 | -#define SH7750_IPRB_WDT_S 12 | |
1265 | -#define SH7750_IPRB_REF 0x0F00 /* Memory Refresh unit interrupt | |
1266 | - priority */ | |
1267 | -#define SH7750_IPRB_REF_S 8 | |
1268 | -#define SH7750_IPRB_SCI1 0x00F0 /* SCI1 interrupt priority */ | |
1269 | -#define SH7750_IPRB_SCI1_S 4 | |
1270 | - | |
1271 | -/* Interrupt Priority Register ó - IPRó (half) */ | |
1272 | -#define SH7750_IPRC_REGOFS 0xD00004 /* offset */ | |
1273 | -#define SH7750_IPRC SH7750_P4_REG32(SH7750_IPRC_REGOFS) | |
1274 | -#define SH7750_IPRC_A7 SH7750_A7_REG32(SH7750_IPRC_REGOFS) | |
1275 | - | |
1276 | -#define SH7750_IPRC_GPIO 0xF000 /* GPIO interrupt priority */ | |
1277 | -#define SH7750_IPRC_GPIO_S 12 | |
1278 | -#define SH7750_IPRC_DMAC 0x0F00 /* DMAC interrupt priority */ | |
1279 | -#define SH7750_IPRC_DMAC_S 8 | |
1280 | -#define SH7750_IPRC_SCIF 0x00F0 /* SCIF interrupt priority */ | |
1281 | -#define SH7750_IPRC_SCIF_S 4 | |
1282 | -#define SH7750_IPRC_HUDI 0x000F /* H-UDI interrupt priority */ | |
1283 | -#define SH7750_IPRC_HUDI_S 0 | |
1284 | - | |
1285 | - | |
1286 | 1244 | /* |
1287 | 1245 | * User Break Controller registers |
1288 | 1246 | */ | ... | ... |
hw/sh_intc.c
0 → 100644
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 | + * | |
8 | + * This code is licenced under the GPL. | |
9 | + */ | |
10 | + | |
11 | +#include <assert.h> | |
12 | +#include "sh_intc.h" | |
13 | +#include "vl.h" | |
14 | + | |
15 | +//#define DEBUG_INTC | |
16 | + | |
17 | +#define INTC_A7(x) ((x) & 0x1fffffff) | |
18 | +#define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0])) | |
19 | + | |
20 | +#define INTC_MODE_NONE 0 | |
21 | +#define INTC_MODE_DUAL_SET 1 | |
22 | +#define INTC_MODE_DUAL_CLR 2 | |
23 | +#define INTC_MODE_ENABLE_REG 3 | |
24 | +#define INTC_MODE_MASK_REG 4 | |
25 | +#define INTC_MODE_IS_PRIO 8 | |
26 | + | |
27 | +static unsigned int sh_intc_mode(unsigned long address, | |
28 | + unsigned long set_reg, unsigned long clr_reg) | |
29 | +{ | |
30 | + if ((address != INTC_A7(set_reg)) && | |
31 | + (address != INTC_A7(clr_reg))) | |
32 | + return INTC_MODE_NONE; | |
33 | + | |
34 | + if (set_reg && clr_reg) { | |
35 | + if (address == INTC_A7(set_reg)) | |
36 | + return INTC_MODE_DUAL_SET; | |
37 | + else | |
38 | + return INTC_MODE_DUAL_CLR; | |
39 | + } | |
40 | + | |
41 | + if (set_reg) | |
42 | + return INTC_MODE_ENABLE_REG; | |
43 | + else | |
44 | + return INTC_MODE_MASK_REG; | |
45 | +} | |
46 | + | |
47 | +static void sh_intc_locate(struct intc_desc *desc, | |
48 | + unsigned long address, | |
49 | + unsigned long **datap, | |
50 | + intc_enum **enums, | |
51 | + unsigned int *first, | |
52 | + unsigned int *width, | |
53 | + unsigned int *modep) | |
54 | +{ | |
55 | + unsigned int i, mode; | |
56 | + | |
57 | + /* this is slow but works for now */ | |
58 | + | |
59 | + if (desc->mask_regs) { | |
60 | + for (i = 0; i < desc->nr_mask_regs; i++) { | |
61 | + struct intc_mask_reg *mr = desc->mask_regs + i; | |
62 | + | |
63 | + mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); | |
64 | + if (mode == INTC_MODE_NONE) | |
65 | + continue; | |
66 | + | |
67 | + *modep = mode; | |
68 | + *datap = &mr->value; | |
69 | + *enums = mr->enum_ids; | |
70 | + *first = mr->reg_width - 1; | |
71 | + *width = 1; | |
72 | + return; | |
73 | + } | |
74 | + } | |
75 | + | |
76 | + if (desc->prio_regs) { | |
77 | + for (i = 0; i < desc->nr_prio_regs; i++) { | |
78 | + struct intc_prio_reg *pr = desc->prio_regs + i; | |
79 | + | |
80 | + mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); | |
81 | + if (mode == INTC_MODE_NONE) | |
82 | + continue; | |
83 | + | |
84 | + *modep = mode | INTC_MODE_IS_PRIO; | |
85 | + *datap = &pr->value; | |
86 | + *enums = pr->enum_ids; | |
87 | + *first = (pr->reg_width / pr->field_width) - 1; | |
88 | + *width = pr->field_width; | |
89 | + return; | |
90 | + } | |
91 | + } | |
92 | + | |
93 | + assert(0); | |
94 | +} | |
95 | + | |
96 | +static void sh_intc_toggle(struct intc_desc *desc, intc_enum id, | |
97 | + int enable, int is_group) | |
98 | +{ | |
99 | + struct intc_source *source = desc->sources + id; | |
100 | + int old = source->enable_count; | |
101 | + | |
102 | + if (!id) | |
103 | + return; | |
104 | + | |
105 | + if (!source->next_enum_id && (!source->enable_max || !source->vect)) { | |
106 | +#ifdef DEBUG_INTC | |
107 | + printf("sh_intc: reserved interrupt source %d modified\n", id); | |
108 | +#endif | |
109 | + return; | |
110 | + } | |
111 | + | |
112 | + if (source->vect) { | |
113 | + if (enable) | |
114 | + source->enable_count++; | |
115 | + else | |
116 | + source->enable_count--; | |
117 | + | |
118 | + if (source->enable_count == source->enable_max) { | |
119 | +#ifdef DEBUG_INTC | |
120 | + printf("sh_intc: enabling interrupt source %d -> 0x%04x\n", | |
121 | + id, source->vect); | |
122 | +#endif | |
123 | + } | |
124 | + | |
125 | + if (old == source->enable_max) { | |
126 | +#ifdef DEBUG_INTC | |
127 | + printf("sh_intc: disabling interrupt source %d -> 0x%04x\n", | |
128 | + id, source->vect); | |
129 | +#endif | |
130 | + } | |
131 | + } | |
132 | +#ifdef DEBUG_INTC | |
133 | + else { | |
134 | + printf("setting interrupt group %d to %d\n", id, !!enable); | |
135 | + } | |
136 | +#endif | |
137 | + | |
138 | + if ((is_group || !source->vect) && source->next_enum_id) { | |
139 | + sh_intc_toggle(desc, source->next_enum_id, enable, 1); | |
140 | + } | |
141 | + | |
142 | +#ifdef DEBUG_INTC | |
143 | + if (!source->vect) { | |
144 | + printf("setting interrupt group %d to %d - done\n", id, !!enable); | |
145 | + } | |
146 | +#endif | |
147 | +} | |
148 | + | |
149 | +static uint32_t sh_intc_read(void *opaque, target_phys_addr_t offset) | |
150 | +{ | |
151 | + struct intc_desc *desc = opaque; | |
152 | + intc_enum *enum_ids = NULL; | |
153 | + unsigned int first = 0; | |
154 | + unsigned int width = 0; | |
155 | + unsigned int mode = 0; | |
156 | + unsigned long *valuep; | |
157 | + | |
158 | +#ifdef DEBUG_INTC | |
159 | + printf("sh_intc_read 0x%lx\n", (unsigned long) offset); | |
160 | +#endif | |
161 | + | |
162 | + sh_intc_locate(desc, (unsigned long)offset, &valuep, | |
163 | + &enum_ids, &first, &width, &mode); | |
164 | + return *valuep; | |
165 | +} | |
166 | + | |
167 | +static void sh_intc_write(void *opaque, target_phys_addr_t offset, | |
168 | + uint32_t value) | |
169 | +{ | |
170 | + struct intc_desc *desc = opaque; | |
171 | + intc_enum *enum_ids = NULL; | |
172 | + unsigned int first = 0; | |
173 | + unsigned int width = 0; | |
174 | + unsigned int mode = 0; | |
175 | + unsigned int k; | |
176 | + unsigned long *valuep; | |
177 | + unsigned long mask; | |
178 | + | |
179 | +#ifdef DEBUG_INTC | |
180 | + printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); | |
181 | +#endif | |
182 | + | |
183 | + sh_intc_locate(desc, (unsigned long)offset, &valuep, | |
184 | + &enum_ids, &first, &width, &mode); | |
185 | + | |
186 | + switch (mode) { | |
187 | + case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; | |
188 | + case INTC_MODE_DUAL_SET: value |= *valuep; break; | |
189 | + case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; | |
190 | + default: assert(0); | |
191 | + } | |
192 | + | |
193 | + for (k = 0; k <= first; k++) { | |
194 | + mask = ((1 << width) - 1) << ((first - k) * width); | |
195 | + | |
196 | + if ((*valuep & mask) == (value & mask)) | |
197 | + continue; | |
198 | +#if 0 | |
199 | + printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", | |
200 | + k, first, enum_ids[k], (unsigned int)mask); | |
201 | +#endif | |
202 | + sh_intc_toggle(desc, enum_ids[k], value & mask, 0); | |
203 | + } | |
204 | + | |
205 | + *valuep = value; | |
206 | + | |
207 | +#ifdef DEBUG_INTC | |
208 | + printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value); | |
209 | +#endif | |
210 | +} | |
211 | + | |
212 | +static CPUReadMemoryFunc *sh_intc_readfn[] = { | |
213 | + sh_intc_read, | |
214 | + sh_intc_read, | |
215 | + sh_intc_read | |
216 | +}; | |
217 | + | |
218 | +static CPUWriteMemoryFunc *sh_intc_writefn[] = { | |
219 | + sh_intc_write, | |
220 | + sh_intc_write, | |
221 | + sh_intc_write | |
222 | +}; | |
223 | + | |
224 | +struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) | |
225 | +{ | |
226 | + if (id) | |
227 | + return desc->sources + id; | |
228 | + | |
229 | + return NULL; | |
230 | +} | |
231 | + | |
232 | +static void sh_intc_register(struct intc_desc *desc, | |
233 | + unsigned long address) | |
234 | +{ | |
235 | + if (address) | |
236 | + cpu_register_physical_memory(INTC_A7(address), 4, desc->iomemtype); | |
237 | +} | |
238 | + | |
239 | +static void sh_intc_register_source(struct intc_desc *desc, | |
240 | + intc_enum source, | |
241 | + struct intc_group *groups, | |
242 | + int nr_groups) | |
243 | +{ | |
244 | + unsigned int i, k; | |
245 | + struct intc_source *s; | |
246 | + | |
247 | + if (desc->mask_regs) { | |
248 | + for (i = 0; i < desc->nr_mask_regs; i++) { | |
249 | + struct intc_mask_reg *mr = desc->mask_regs + i; | |
250 | + | |
251 | + for (k = 0; k < INTC_ARRAY(mr->enum_ids); k++) { | |
252 | + if (mr->enum_ids[k] != source) | |
253 | + continue; | |
254 | + | |
255 | + s = sh_intc_source(desc, mr->enum_ids[k]); | |
256 | + if (s) | |
257 | + s->enable_max++; | |
258 | + } | |
259 | + } | |
260 | + } | |
261 | + | |
262 | + if (desc->prio_regs) { | |
263 | + for (i = 0; i < desc->nr_prio_regs; i++) { | |
264 | + struct intc_prio_reg *pr = desc->prio_regs + i; | |
265 | + | |
266 | + for (k = 0; k < INTC_ARRAY(pr->enum_ids); k++) { | |
267 | + if (pr->enum_ids[k] != source) | |
268 | + continue; | |
269 | + | |
270 | + s = sh_intc_source(desc, pr->enum_ids[k]); | |
271 | + if (s) | |
272 | + s->enable_max++; | |
273 | + } | |
274 | + } | |
275 | + } | |
276 | + | |
277 | + if (groups) { | |
278 | + for (i = 0; i < nr_groups; i++) { | |
279 | + struct intc_group *gr = groups + i; | |
280 | + | |
281 | + for (k = 0; k < INTC_ARRAY(gr->enum_ids); k++) { | |
282 | + if (gr->enum_ids[k] != source) | |
283 | + continue; | |
284 | + | |
285 | + s = sh_intc_source(desc, gr->enum_ids[k]); | |
286 | + if (s) | |
287 | + s->enable_max++; | |
288 | + } | |
289 | + } | |
290 | + } | |
291 | + | |
292 | +} | |
293 | + | |
294 | +void sh_intc_register_sources(struct intc_desc *desc, | |
295 | + struct intc_vect *vectors, | |
296 | + int nr_vectors, | |
297 | + struct intc_group *groups, | |
298 | + int nr_groups) | |
299 | +{ | |
300 | + unsigned int i, k; | |
301 | + struct intc_source *s; | |
302 | + | |
303 | + for (i = 0; i < nr_vectors; i++) { | |
304 | + struct intc_vect *vect = vectors + i; | |
305 | + | |
306 | + sh_intc_register_source(desc, vect->enum_id, groups, nr_groups); | |
307 | + s = sh_intc_source(desc, vect->enum_id); | |
308 | + if (s) | |
309 | + s->vect = vect->vect; | |
310 | + | |
311 | +#ifdef DEBUG_INTC | |
312 | + printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n", | |
313 | + vect->enum_id, s->vect, s->enable_count, s->enable_max); | |
314 | +#endif | |
315 | + } | |
316 | + | |
317 | + if (groups) { | |
318 | + for (i = 0; i < nr_groups; i++) { | |
319 | + struct intc_group *gr = groups + i; | |
320 | + | |
321 | + s = sh_intc_source(desc, gr->enum_id); | |
322 | + s->next_enum_id = gr->enum_ids[0]; | |
323 | + | |
324 | + for (k = 1; k < INTC_ARRAY(gr->enum_ids); k++) { | |
325 | + if (!gr->enum_ids[k]) | |
326 | + continue; | |
327 | + | |
328 | + s = sh_intc_source(desc, gr->enum_ids[k - 1]); | |
329 | + s->next_enum_id = gr->enum_ids[k]; | |
330 | + } | |
331 | + | |
332 | +#ifdef DEBUG_INTC | |
333 | + printf("sh_intc: registered group %d (%d/%d)\n", | |
334 | + gr->enum_id, s->enable_count, s->enable_max); | |
335 | +#endif | |
336 | + } | |
337 | + } | |
338 | +} | |
339 | + | |
340 | +int sh_intc_init(struct intc_desc *desc, | |
341 | + int nr_sources, | |
342 | + struct intc_mask_reg *mask_regs, | |
343 | + int nr_mask_regs, | |
344 | + struct intc_prio_reg *prio_regs, | |
345 | + int nr_prio_regs) | |
346 | +{ | |
347 | + unsigned int i; | |
348 | + | |
349 | + desc->nr_sources = nr_sources; | |
350 | + desc->mask_regs = mask_regs; | |
351 | + desc->nr_mask_regs = nr_mask_regs; | |
352 | + desc->prio_regs = prio_regs; | |
353 | + desc->nr_prio_regs = nr_prio_regs; | |
354 | + | |
355 | + i = sizeof(struct intc_source) * nr_sources; | |
356 | + desc->sources = malloc(i); | |
357 | + if (!desc->sources) | |
358 | + return -1; | |
359 | + | |
360 | + memset(desc->sources, 0, i); | |
361 | + | |
362 | + desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn, | |
363 | + sh_intc_writefn, desc); | |
364 | + if (desc->mask_regs) { | |
365 | + for (i = 0; i < desc->nr_mask_regs; i++) { | |
366 | + struct intc_mask_reg *mr = desc->mask_regs + i; | |
367 | + | |
368 | + sh_intc_register(desc, mr->set_reg); | |
369 | + sh_intc_register(desc, mr->clr_reg); | |
370 | + } | |
371 | + } | |
372 | + | |
373 | + if (desc->prio_regs) { | |
374 | + for (i = 0; i < desc->nr_prio_regs; i++) { | |
375 | + struct intc_prio_reg *pr = desc->prio_regs + i; | |
376 | + | |
377 | + sh_intc_register(desc, pr->set_reg); | |
378 | + sh_intc_register(desc, pr->clr_reg); | |
379 | + } | |
380 | + } | |
381 | + | |
382 | + return 0; | |
383 | +} | ... | ... |
hw/sh_intc.h
0 → 100644
1 | +#ifndef __SH_INTC_H__ | |
2 | +#define __SH_INTC_H__ | |
3 | + | |
4 | +typedef unsigned char intc_enum; | |
5 | + | |
6 | +struct intc_vect { | |
7 | + intc_enum enum_id; | |
8 | + unsigned short vect; | |
9 | +}; | |
10 | + | |
11 | +#define INTC_VECT(enum_id, vect) { enum_id, vect } | |
12 | + | |
13 | +struct intc_group { | |
14 | + intc_enum enum_id; | |
15 | + intc_enum enum_ids[32]; | |
16 | +}; | |
17 | + | |
18 | +#define INTC_GROUP(enum_id, ids...) { enum_id, { ids } } | |
19 | + | |
20 | +struct intc_mask_reg { | |
21 | + unsigned long set_reg, clr_reg, reg_width; | |
22 | + intc_enum enum_ids[32]; | |
23 | + unsigned long value; | |
24 | +}; | |
25 | + | |
26 | +struct intc_prio_reg { | |
27 | + unsigned long set_reg, clr_reg, reg_width, field_width; | |
28 | + intc_enum enum_ids[16]; | |
29 | + unsigned long value; | |
30 | +}; | |
31 | + | |
32 | +#define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a) | |
33 | + | |
34 | +struct intc_source { | |
35 | + unsigned short vect; | |
36 | + intc_enum next_enum_id; | |
37 | + | |
38 | + int asserted; | |
39 | + int enable_count; | |
40 | + int enable_max; | |
41 | +}; | |
42 | + | |
43 | +struct intc_desc { | |
44 | + struct intc_source *sources; | |
45 | + int nr_sources; | |
46 | + struct intc_mask_reg *mask_regs; | |
47 | + int nr_mask_regs; | |
48 | + struct intc_prio_reg *prio_regs; | |
49 | + int nr_prio_regs; | |
50 | + | |
51 | + int iomemtype; | |
52 | +}; | |
53 | + | |
54 | +struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); | |
55 | + | |
56 | +void sh_intc_register_sources(struct intc_desc *desc, | |
57 | + struct intc_vect *vectors, | |
58 | + int nr_vectors, | |
59 | + struct intc_group *groups, | |
60 | + int nr_groups); | |
61 | + | |
62 | +int sh_intc_init(struct intc_desc *desc, | |
63 | + int nr_sources, | |
64 | + struct intc_mask_reg *mask_regs, | |
65 | + int nr_mask_regs, | |
66 | + struct intc_prio_reg *prio_regs, | |
67 | + int nr_prio_regs); | |
68 | + | |
69 | +#endif /* __SH_INTC_H__ */ | ... | ... |