qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Magnus Damm" <magnus.damm@gmail.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] sh4: system emulator - interrupt controller
Date: Wed, 3 Oct 2007 19:38:35 +0900	[thread overview]
Message-ID: <aec7e5c30710030338k78c056dev7f82da47f7349d43@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 881 bytes --]

Hi everyone,

Attached is a patch that implements a table driver interrupt
controller for the sh architecture. The tables that describes the
registers and interrupt sources are very similar to the linux kernel
code that I rewrote recently. Included are tables for sh775x but it
should be fairly easy to add tables for other cpu models in the future
if needed.

Only registers are hooked up to the cpu for now. No interrupts are
delivered to the processor yet, but the association between register
bit fields and interrupt sources works well.

People that want to play with this can uncomment DEBUG_INTC in
sh_intc.c to get printouts when the kernel enables various interrupt
sources during boot. I'm going to continue working on this to hook up
the processor, timers and serial ports as well. This is at least a
good first step to share with the community. Please apply.

/ magnus

[-- Attachment #2: qemu-cvs-20071003-sh-intc.patch --]
[-- Type: application/octet-stream, Size: 23453 bytes --]

--- 0001/Makefile.target
+++ work/Makefile.target	2007-10-02 19:50:24.000000000 +0900
@@ -484,7 +484,7 @@ CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
 VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
-VL_OBJS+= sh_timer.o ptimer.o sh_serial.o
+VL_OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o
 endif
 ifeq ($(TARGET_BASE_ARCH), m68k)
 VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
--- 0001/hw/sh7750.c
+++ work/hw/sh7750.c	2007-10-03 19:13:38.000000000 +0900
@@ -1,6 +1,7 @@
 /*
  * SH7750 device
  *
+ * Copyright (c) 2007 Magnus Damm
  * Copyright (c) 2005 Samuel Tardieu
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +27,7 @@
 #include "vl.h"
 #include "sh7750_regs.h"
 #include "sh7750_regnames.h"
+#include "sh_intc.h"
 
 #define NB_DEVICES 4
 
@@ -53,15 +55,10 @@ typedef struct SH7750State {
     sh7750_io_device *devices[NB_DEVICES];	/* External peripherals */
 
     uint16_t icr;
-    uint16_t ipra;
-    uint16_t iprb;
-    uint16_t iprc;
-    uint16_t iprd;
-    uint32_t intpri00;
-    uint32_t intmsk00;
     /* Cache */
     uint32_t ccr;
 
+    struct intc_desc intc;
 } SH7750State;
 
 
@@ -219,14 +216,6 @@ static uint32_t sh7750_mem_readw(void *o
 	return portb_lines(s);
     case 0x1fd00000:
         return s->icr;
-    case 0x1fd00004:
-        return s->ipra;
-    case 0x1fd00008:
-        return s->iprb;
-    case 0x1fd0000c:
-        return s->iprc;
-    case 0x1fd00010:
-        return s->iprd;
     default:
 	error_access("word read", addr);
 	assert(0);
@@ -262,14 +251,6 @@ static uint32_t sh7750_mem_readl(void *o
 	return 0x00110000;	/* Minimum caches */
     case 0x1f000044:		/* Processor version PRR */
 	return 0x00000100;	/* SH7750R */
-    case 0x1e080000:
-        return s->intpri00;
-    case 0x1e080020:
-        return 0;
-    case 0x1e080040:
-        return s->intmsk00;
-    case 0x1e080060:
-        return 0;
     default:
 	error_access("long read", addr);
 	assert(0);
@@ -331,18 +312,6 @@ static void sh7750_mem_writew(void *opaq
     case 0x1fd00000:
         s->icr = mem_value;
 	return;
-    case 0x1fd00004:
-        s->ipra = mem_value;
-	return;
-    case 0x1fd00008:
-        s->iprb = mem_value;
-	return;
-    case 0x1fd0000c:
-        s->iprc = mem_value;
-	return;
-    case 0x1fd00010:
-        s->iprd = mem_value;
-	return;
     default:
 	error_access("word write", addr);
 	assert(0);
@@ -407,16 +376,6 @@ static void sh7750_mem_writel(void *opaq
     case SH7750_CCR_A7:
 	s->ccr = mem_value;
 	return;
-    case 0x1e080000:
-        s->intpri00 = mem_value;
-	return;
-    case 0x1e080020:
-        return;
-    case 0x1e080040:
-        s->intmsk00 = mem_value;
-	return;
-    case 0x1e080060:
-        return;
     default:
 	error_access("long write", addr);
 	assert(0);
@@ -435,10 +394,144 @@ static CPUWriteMemoryFunc *sh7750_mem_wr
     sh7750_mem_writel
 };
 
+/* sh775x interrupt controller tables for sh_intc.c
+ * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+ */
+
+enum {
+	UNUSED = 0,
+
+	/* interrupt sources */
+	IRL0, IRL1, IRL2, IRL3, /* only IRLM mode supported */
+	HUDI, GPIOI,
+	DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3,
+	DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7,
+	DMAC_DMAE,
+	PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+	PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3,
+	TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
+	RTC_ATI, RTC_PRI, RTC_CUI,
+	SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI,
+	SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI,
+	WDT,
+	REF_RCMI, REF_ROVI,
+
+	/* interrupt groups */
+	DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF,
+
+	NR_SOURCES,
+};
+
+static struct intc_vect vectors[] = {
+	INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620),
+	INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
+	INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
+	INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
+	INTC_VECT(RTC_CUI, 0x4c0),
+	INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500),
+	INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540),
+	INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720),
+	INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760),
+	INTC_VECT(WDT, 0x560),
+	INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0),
+};
+
+static struct intc_group groups[] = {
+	INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
+	INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
+	INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI),
+	INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI),
+	INTC_GROUP(REF, REF_RCMI, REF_ROVI),
+};
+
+static struct intc_prio_reg prio_registers[] = {
+	{ 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
+	{ 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } },
+	{ 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } },
+	{ 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } },
+	{ 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0,
+						 TMU4, TMU3,
+						 PCIC1, PCIC0_PCISERR } },
+};
+
+/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */
+
+static struct intc_vect vectors_dma4[] = {
+	INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
+	INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
+	INTC_VECT(DMAC_DMAE, 0x6c0),
+};
+
+static struct intc_group groups_dma4[] = {
+	INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
+		   DMAC_DMTE3, DMAC_DMAE),
+};
+
+/* SH7750R and SH7751R both have 8-channel DMA controllers */
+
+static struct intc_vect vectors_dma8[] = {
+	INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
+	INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
+	INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0),
+	INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0),
+	INTC_VECT(DMAC_DMAE, 0x6c0),
+};
+
+static struct intc_group groups_dma8[] = {
+	INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
+		   DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5,
+		   DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE),
+};
+
+/* SH7750R, SH7751 and SH7751R all have two extra timer channels */
+
+static struct intc_vect vectors_tmu34[] = {
+	INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80),
+};
+
+static struct intc_mask_reg mask_registers[] = {
+	{ 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */
+	  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	    0, 0, 0, 0, 0, 0, TMU4, TMU3,
+	    PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+	    PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2,
+	    PCIC1_PCIDMA3, PCIC0_PCISERR } },
+};
+
+/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */
+
+static struct intc_vect vectors_irlm[] = {
+	INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0),
+	INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360),
+};
+
+/* SH7751 and SH7751R both have PCI */
+
+static struct intc_vect vectors_pci[] = {
+	INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0),
+	INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0),
+	INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60),
+	INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20),
+};
+
+static struct intc_group groups_pci[] = {
+	INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+		   PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3),
+};
+
+#define SH_CPU_SH7750  (1 << 0)
+#define SH_CPU_SH7750S (1 << 1)
+#define SH_CPU_SH7750R (1 << 2)
+#define SH_CPU_SH7751  (1 << 3)
+#define SH_CPU_SH7751R (1 << 4)
+#define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R)
+#define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R)
+
 SH7750State *sh7750_init(CPUSH4State * cpu)
 {
     SH7750State *s;
     int sh7750_io_memory;
+    int cpu_model = SH_CPU_SH7751R; /* for now */
 
     s = qemu_mallocz(sizeof(SH7750State));
     s->cpu = cpu;
@@ -448,6 +541,14 @@ SH7750State *sh7750_init(CPUSH4State * c
 					      sh7750_mem_write, s);
     cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory);
 
+    sh_intc_init(&s->intc, NR_SOURCES,
+		 _INTC_ARRAY(mask_registers),
+		 _INTC_ARRAY(prio_registers));
+
+    sh_intc_register_sources(&s->intc, 
+			     _INTC_ARRAY(vectors),
+			     _INTC_ARRAY(groups));
+
     sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]);
     sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF,
 		   s->periph_freq, serial_hds[1]);
@@ -455,6 +556,38 @@ SH7750State *sh7750_init(CPUSH4State * c
     tmu012_init(0x1fd80000,
 		TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,
 		s->periph_freq);
-    tmu012_init(0x1e100000, 0, s->periph_freq);
+
+
+    if (cpu_model & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) {
+        sh_intc_register_sources(&s->intc, 
+				 _INTC_ARRAY(vectors_dma4),
+				 _INTC_ARRAY(groups_dma4));
+    }
+
+    if (cpu_model & (SH_CPU_SH7750R | SH_CPU_SH7751R)) {
+        sh_intc_register_sources(&s->intc, 
+				 _INTC_ARRAY(vectors_dma8),
+				 _INTC_ARRAY(groups_dma8));
+    }
+
+    if (cpu_model & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) {
+        sh_intc_register_sources(&s->intc, 
+				 _INTC_ARRAY(vectors_tmu34),
+				 _INTC_ARRAY(NULL));
+        tmu012_init(0x1e100000, 0, s->periph_freq);
+    }
+
+    if (cpu_model & (SH_CPU_SH7751_ALL)) {
+        sh_intc_register_sources(&s->intc, 
+				 _INTC_ARRAY(vectors_pci),
+				 _INTC_ARRAY(groups_pci));
+    }
+
+    if (cpu_model & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) {
+        sh_intc_register_sources(&s->intc, 
+				 _INTC_ARRAY(vectors_irlm),
+				 _INTC_ARRAY(NULL));
+    }
+
     return s;
 }
--- 0001/hw/sh7750_regnames.c
+++ work/hw/sh7750_regnames.c	2007-10-03 19:15:39.000000000 +0900
@@ -76,9 +76,6 @@ static regname_t regnames[] = {
 	REGNAME(SH7750_PDTRB_A7)
 	REGNAME(SH7750_GPIOIC_A7)
 	REGNAME(SH7750_ICR_A7)
-	REGNAME(SH7750_IPRA_A7)
-	REGNAME(SH7750_IPRB_A7)
-	REGNAME(SH7750_IPRC_A7)
 	REGNAME(SH7750_BCR3_A7)
 	REGNAME(SH7750_BCR4_A7)
 	REGNAME(SH7750_PRECHARGE0_A7)
--- 0001/hw/sh7750_regs.h
+++ work/hw/sh7750_regs.h	2007-10-03 19:14:57.000000000 +0900
@@ -1241,48 +1241,6 @@
 #define SH7750_ICR_IRLM_RAW   0x0080	/*   IRL\ pins used as a four independent
 					   interrupt requests */
 
-/* Interrupt Priority Register A - IPRA (half) */
-#define SH7750_IPRA_REGOFS    0xD00004	/* offset */
-#define SH7750_IPRA           SH7750_P4_REG32(SH7750_IPRA_REGOFS)
-#define SH7750_IPRA_A7        SH7750_A7_REG32(SH7750_IPRA_REGOFS)
-
-#define SH7750_IPRA_TMU0      0xF000	/* TMU0 interrupt priority */
-#define SH7750_IPRA_TMU0_S    12
-#define SH7750_IPRA_TMU1      0x0F00	/* TMU1 interrupt priority */
-#define SH7750_IPRA_TMU1_S    8
-#define SH7750_IPRA_TMU2      0x00F0	/* TMU2 interrupt priority */
-#define SH7750_IPRA_TMU2_S    4
-#define SH7750_IPRA_RTC       0x000F	/* RTC interrupt priority */
-#define SH7750_IPRA_RTC_S     0
-
-/* Interrupt Priority Register B - IPRB (half) */
-#define SH7750_IPRB_REGOFS    0xD00008	/* offset */
-#define SH7750_IPRB           SH7750_P4_REG32(SH7750_IPRB_REGOFS)
-#define SH7750_IPRB_A7        SH7750_A7_REG32(SH7750_IPRB_REGOFS)
-
-#define SH7750_IPRB_WDT       0xF000	/* WDT interrupt priority */
-#define SH7750_IPRB_WDT_S     12
-#define SH7750_IPRB_REF       0x0F00	/* Memory Refresh unit interrupt
-					   priority */
-#define SH7750_IPRB_REF_S     8
-#define SH7750_IPRB_SCI1      0x00F0	/* SCI1 interrupt priority */
-#define SH7750_IPRB_SCI1_S    4
-
-/* Interrupt Priority Register ó - IPRó (half) */
-#define SH7750_IPRC_REGOFS    0xD00004	/* offset */
-#define SH7750_IPRC           SH7750_P4_REG32(SH7750_IPRC_REGOFS)
-#define SH7750_IPRC_A7        SH7750_A7_REG32(SH7750_IPRC_REGOFS)
-
-#define SH7750_IPRC_GPIO      0xF000	/* GPIO interrupt priority */
-#define SH7750_IPRC_GPIO_S    12
-#define SH7750_IPRC_DMAC      0x0F00	/* DMAC interrupt priority */
-#define SH7750_IPRC_DMAC_S    8
-#define SH7750_IPRC_SCIF      0x00F0	/* SCIF interrupt priority */
-#define SH7750_IPRC_SCIF_S    4
-#define SH7750_IPRC_HUDI      0x000F	/* H-UDI interrupt priority */
-#define SH7750_IPRC_HUDI_S    0
-
-
 /*
  * User Break Controller registers
  */
--- /dev/null
+++ work/hw/sh_intc.c	2007-10-03 19:02:07.000000000 +0900
@@ -0,0 +1,383 @@
+/*
+ * SuperH interrupt controller module
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Based on sh_timer.c and arm_timer.c by Paul Brook
+ * Copyright (c) 2005-2006 CodeSourcery.
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include <assert.h>
+#include "sh_intc.h"
+#include "vl.h"
+
+//#define DEBUG_INTC
+
+#define INTC_A7(x) ((x) & 0x1fffffff)
+#define INTC_ARRAY(x) (sizeof(x) / sizeof(x[0]))
+
+#define INTC_MODE_NONE       0
+#define INTC_MODE_DUAL_SET   1
+#define INTC_MODE_DUAL_CLR   2
+#define INTC_MODE_ENABLE_REG 3
+#define INTC_MODE_MASK_REG   4
+#define INTC_MODE_IS_PRIO    8
+
+static unsigned int sh_intc_mode(unsigned long address,
+				 unsigned long set_reg, unsigned long clr_reg)
+{
+    if ((address != INTC_A7(set_reg)) &&
+	(address != INTC_A7(clr_reg)))
+        return INTC_MODE_NONE;
+
+    if (set_reg && clr_reg) {
+        if (address == INTC_A7(set_reg))
+            return INTC_MODE_DUAL_SET;
+	else
+            return INTC_MODE_DUAL_CLR;
+    }
+
+    if (set_reg)
+        return INTC_MODE_ENABLE_REG;
+    else
+        return INTC_MODE_MASK_REG;
+}
+
+static void sh_intc_locate(struct intc_desc *desc,
+			   unsigned long address,
+			   unsigned long **datap,
+			   intc_enum **enums,
+			   unsigned int *first,
+			   unsigned int *width,
+			   unsigned int *modep)
+{
+    unsigned int i, mode;
+
+    /* this is slow but works for now */
+
+    if (desc->mask_regs) {
+        for (i = 0; i < desc->nr_mask_regs; i++) {
+	    struct intc_mask_reg *mr = desc->mask_regs + i;
+
+	    mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
+	    if (mode == INTC_MODE_NONE)
+                continue;
+
+	    *modep = mode;
+	    *datap = &mr->value;
+	    *enums = mr->enum_ids;
+	    *first = mr->reg_width - 1;
+	    *width = 1;
+	    return;
+	}
+    }
+
+    if (desc->prio_regs) {
+        for (i = 0; i < desc->nr_prio_regs; i++) {
+	    struct intc_prio_reg *pr = desc->prio_regs + i;
+
+	    mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
+	    if (mode == INTC_MODE_NONE)
+                continue;
+
+	    *modep = mode | INTC_MODE_IS_PRIO;
+	    *datap = &pr->value;
+	    *enums = pr->enum_ids;
+	    *first = (pr->reg_width / pr->field_width) - 1;
+	    *width = pr->field_width;
+	    return;
+	}
+    }
+
+    assert(0);
+}
+
+static void sh_intc_toggle(struct intc_desc *desc, intc_enum id,
+			   int enable, int is_group)
+{
+    struct intc_source *source = desc->sources + id;
+    int old = source->enable_count;
+
+    if (!id)
+	return;
+
+    if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
+#ifdef DEBUG_INTC
+        printf("sh_intc: reserved interrupt source %d modified\n", id);
+#endif
+	return;
+    }
+
+    if (source->vect) {
+        if (enable)
+            source->enable_count++;
+	else 
+            source->enable_count--;
+
+        if (source->enable_count == source->enable_max) {
+#ifdef DEBUG_INTC
+            printf("sh_intc: enabling interrupt source %d -> 0x%04x\n",
+		   id, source->vect);
+#endif
+	}
+
+        if (old == source->enable_max) {
+#ifdef DEBUG_INTC
+            printf("sh_intc: disabling interrupt source %d -> 0x%04x\n",
+		   id, source->vect);
+#endif
+	}
+    }
+#ifdef DEBUG_INTC
+    else {
+        printf("setting interrupt group %d to %d\n", id, !!enable);
+    }
+#endif
+
+    if ((is_group || !source->vect) && source->next_enum_id) {
+        sh_intc_toggle(desc, source->next_enum_id, enable, 1);
+    }
+
+#ifdef DEBUG_INTC
+    if (!source->vect) {
+        printf("setting interrupt group %d to %d - done\n", id, !!enable);
+    }
+#endif
+}
+
+static uint32_t sh_intc_read(void *opaque, target_phys_addr_t offset)
+{
+    struct intc_desc *desc = opaque;
+    intc_enum *enum_ids = NULL;
+    unsigned int first = 0;
+    unsigned int width = 0;
+    unsigned int mode = 0;
+    unsigned long *valuep;
+
+#ifdef DEBUG_INTC
+    printf("sh_intc_read 0x%lx\n", (unsigned long) offset);
+#endif
+
+    sh_intc_locate(desc, (unsigned long)offset, &valuep, 
+		   &enum_ids, &first, &width, &mode);
+    return *valuep;
+}
+
+static void sh_intc_write(void *opaque, target_phys_addr_t offset,
+			  uint32_t value)
+{
+    struct intc_desc *desc = opaque;
+    intc_enum *enum_ids = NULL;
+    unsigned int first = 0;
+    unsigned int width = 0;
+    unsigned int mode = 0;
+    unsigned int k;
+    unsigned long *valuep;
+    unsigned long mask;
+
+#ifdef DEBUG_INTC
+    printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
+#endif
+
+    sh_intc_locate(desc, (unsigned long)offset, &valuep, 
+		   &enum_ids, &first, &width, &mode);
+
+    switch (mode) {
+    case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break;
+    case INTC_MODE_DUAL_SET: value |= *valuep; break;
+    case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break;
+    default: assert(0);
+    }
+
+    for (k = 0; k <= first; k++) {
+        mask = ((1 << width) - 1) << ((first - k) * width);
+
+	if ((*valuep & mask) == (value & mask))
+            continue;
+#if 0
+	printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n", 
+	       k, first, enum_ids[k], (unsigned int)mask);
+#endif
+        sh_intc_toggle(desc, enum_ids[k], value & mask, 0);
+    }
+
+    *valuep = value;
+
+#ifdef DEBUG_INTC
+    printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
+#endif
+}
+
+static CPUReadMemoryFunc *sh_intc_readfn[] = {
+    sh_intc_read,
+    sh_intc_read,
+    sh_intc_read
+};
+
+static CPUWriteMemoryFunc *sh_intc_writefn[] = {
+    sh_intc_write,
+    sh_intc_write,
+    sh_intc_write
+};
+
+struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id)
+{
+    if (id)
+        return desc->sources + id;
+
+    return NULL;
+}
+
+static void sh_intc_register(struct intc_desc *desc, 
+			     unsigned long address)
+{
+    if (address)
+        cpu_register_physical_memory(INTC_A7(address), 4, desc->iomemtype);
+}
+
+static void sh_intc_register_source(struct intc_desc *desc,
+				    intc_enum source,
+				    struct intc_group *groups,
+				    int nr_groups)
+{
+    unsigned int i, k;
+    struct intc_source *s;
+
+    if (desc->mask_regs) {
+        for (i = 0; i < desc->nr_mask_regs; i++) {
+	    struct intc_mask_reg *mr = desc->mask_regs + i;
+
+	    for (k = 0; k < INTC_ARRAY(mr->enum_ids); k++) {
+                if (mr->enum_ids[k] != source)
+                    continue;
+
+		s = sh_intc_source(desc, mr->enum_ids[k]);
+		if (s)
+                    s->enable_max++;
+	    }
+	}
+    }
+
+    if (desc->prio_regs) {
+        for (i = 0; i < desc->nr_prio_regs; i++) {
+	    struct intc_prio_reg *pr = desc->prio_regs + i;
+
+	    for (k = 0; k < INTC_ARRAY(pr->enum_ids); k++) {
+                if (pr->enum_ids[k] != source)
+                    continue;
+
+		s = sh_intc_source(desc, pr->enum_ids[k]);
+		if (s)
+                    s->enable_max++;
+	    }
+	}
+    }
+
+    if (groups) {
+        for (i = 0; i < nr_groups; i++) {
+	    struct intc_group *gr = groups + i;
+
+	    for (k = 0; k < INTC_ARRAY(gr->enum_ids); k++) {
+                if (gr->enum_ids[k] != source)
+                    continue;
+
+		s = sh_intc_source(desc, gr->enum_ids[k]);
+		if (s)
+                    s->enable_max++;
+	    }
+	}
+    }
+
+}
+
+void sh_intc_register_sources(struct intc_desc *desc,
+			      struct intc_vect *vectors,
+			      int nr_vectors,
+			      struct intc_group *groups,
+			      int nr_groups)
+{
+    unsigned int i, k;
+    struct intc_source *s;
+
+    for (i = 0; i < nr_vectors; i++) {
+	struct intc_vect *vect = vectors + i;
+
+	sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
+	s = sh_intc_source(desc, vect->enum_id);
+	if (s)
+	    s->vect = vect->vect;
+
+#ifdef DEBUG_INTC
+	printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
+	       vect->enum_id, s->vect, s->enable_count, s->enable_max);
+#endif
+    }
+
+    if (groups) {
+        for (i = 0; i < nr_groups; i++) {
+	    struct intc_group *gr = groups + i;
+
+	    s = sh_intc_source(desc, gr->enum_id);
+	    s->next_enum_id = gr->enum_ids[0];
+
+	    for (k = 1; k < INTC_ARRAY(gr->enum_ids); k++) {
+                if (!gr->enum_ids[k])
+                    continue;
+
+		s = sh_intc_source(desc, gr->enum_ids[k - 1]);
+		s->next_enum_id = gr->enum_ids[k];
+	    }
+
+#ifdef DEBUG_INTC
+	    printf("sh_intc: registered group %d (%d/%d)\n",
+		   gr->enum_id, s->enable_count, s->enable_max);
+#endif
+	}
+    }
+}
+
+int sh_intc_init(struct intc_desc *desc,
+		 int nr_sources,
+		 struct intc_mask_reg *mask_regs,
+		 int nr_mask_regs,
+		 struct intc_prio_reg *prio_regs,
+		 int nr_prio_regs)
+{
+    unsigned int i;
+
+    desc->nr_sources = nr_sources;
+    desc->mask_regs = mask_regs;
+    desc->nr_mask_regs = nr_mask_regs;
+    desc->prio_regs = prio_regs;
+    desc->nr_prio_regs = nr_prio_regs;
+
+    i = sizeof(struct intc_source) * nr_sources;
+    desc->sources = malloc(i);
+    if (!desc->sources)
+        return -1;
+
+    memset(desc->sources, 0, i);
+ 
+    desc->iomemtype = cpu_register_io_memory(0, sh_intc_readfn,
+					     sh_intc_writefn, desc);
+    if (desc->mask_regs) {
+        for (i = 0; i < desc->nr_mask_regs; i++) {
+	    struct intc_mask_reg *mr = desc->mask_regs + i;
+
+	    sh_intc_register(desc, mr->set_reg);
+	    sh_intc_register(desc, mr->clr_reg);
+	}
+    }
+
+    if (desc->prio_regs) {
+        for (i = 0; i < desc->nr_prio_regs; i++) {
+	    struct intc_prio_reg *pr = desc->prio_regs + i;
+
+	    sh_intc_register(desc, pr->set_reg);
+	    sh_intc_register(desc, pr->clr_reg);
+	}
+    }
+
+    return 0;
+}
--- /dev/null
+++ work/hw/sh_intc.h	2007-10-03 19:02:00.000000000 +0900
@@ -0,0 +1,69 @@
+#ifndef __SH_INTC_H__
+#define __SH_INTC_H__
+
+typedef unsigned char intc_enum;
+
+struct intc_vect {
+    intc_enum enum_id;
+    unsigned short vect;
+};
+
+#define INTC_VECT(enum_id, vect) { enum_id, vect }
+
+struct intc_group {
+    intc_enum enum_id;
+    intc_enum enum_ids[32];
+};
+
+#define INTC_GROUP(enum_id, ids...) { enum_id, { ids } }
+
+struct intc_mask_reg {
+    unsigned long set_reg, clr_reg, reg_width;
+    intc_enum enum_ids[32];
+    unsigned long value;
+};
+
+struct intc_prio_reg {
+    unsigned long set_reg, clr_reg, reg_width, field_width;
+    intc_enum enum_ids[16];
+    unsigned long value;
+};
+
+#define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a)
+
+struct intc_source {
+    unsigned short vect;
+    intc_enum next_enum_id;
+
+    int asserted;
+    int enable_count;
+    int enable_max;
+};
+
+struct intc_desc {
+    struct intc_source *sources;
+    int nr_sources;
+    struct intc_mask_reg *mask_regs;
+    int nr_mask_regs;
+    struct intc_prio_reg *prio_regs;
+    int nr_prio_regs;
+
+    int iomemtype;
+};
+
+struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id);
+
+void sh_intc_register_sources(struct intc_desc *desc,
+			      struct intc_vect *vectors,
+			      int nr_vectors,
+			      struct intc_group *groups,
+			      int nr_groups);
+
+int sh_intc_init(struct intc_desc *desc,
+		 int nr_sources,
+		 struct intc_mask_reg *mask_regs,
+		 int nr_mask_regs,
+		 struct intc_prio_reg *prio_regs,
+		 int nr_prio_regs);
+
+#endif /* __SH_INTC_H__ */

                 reply	other threads:[~2007-10-03 10:40 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aec7e5c30710030338k78c056dev7f82da47f7349d43@mail.gmail.com \
    --to=magnus.damm@gmail.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).