Subject: proposed sysasic changes
To: None <port-dreamcast@netbsd.org>
From: ITOH Yasufumi <itohy@netbsd.org>
List: port-dreamcast
Date: 09/01/2002 01:19:08
------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <25650.1030808997.1@fmv.my.domain>
Content-Transfer-Encoding: 7bit

  Hello,

I made modification to sysasic-related code.
This is to handle more than three (= number of IRL) events
of peripheral interrupts.
This changes the interface of sysasic_intr_establish() to specify
the IRL (using processor level), and installes master interrupt
handler which calls event specific handlers.

Currently, the changes don't add any function but to make the interrupt
handlers slower, however. :)
Only two (gdrom and gapspci) events are handled for now.
I'm optimizing ffs(3) to minimize the overhead. :)

OK to commit this? / Any thoughts?
-- 
ITOH Yasufumi

------- =_aaaaaaaaaa0
Content-Type: text/plain; name="sysasic.diff"; charset="us-ascii"
Content-ID: <25650.1030808997.2@fmv.my.domain>
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="sysasic.diff"

Index: dreamcast/sysasic.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/dreamcast/dreamcast/sysasic.c,v
retrieving revision 1.2
diff -u -r1.2 sysasic.c
--- dreamcast/sysasic.c	2002/03/24 18:21:10	1.2
+++ dreamcast/sysasic.c	2002/08/31 15:41:39
@@ -36,66 +36,132 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#include <sys/syslog.h>
 
 #include <sh3/exception.h>
 
 #include <machine/intr.h>
 #include <machine/sysasicvar.h>
 
+#define SYSASIC_INTR_ST		0xa05f6900
+#define SYSASIC_INTR_EN(level)	(0xa05f6910 + ((level) << 4))
+
 #define SYSASIC_IRQ_LEVEL_13	0
 #define SYSASIC_IRQ_LEVEL_11	1
 #define SYSASIC_IRQ_LEVEL_9	2
 #define SYSASIC_IRQ_LEVEL_MAX	2
+#define SYSASIC_IRQ_INDEX_TO_IRQ(i)	(13 - 2 * (i))
+
+#define IPL_IRL9	IPL_BIO
+#define IPL_IRL11	IPL_NET
+#define IPL_IRL13	IPL_TTY
 
+/* per-irq */
 struct sysasic_intrhand {
+	/* for quick check on interrupt */
+	unsigned	syh_events[(SYSASIC_EVENT_MAX + 1 + (32 - 1)) / 32];
+#define SYSASIC_EVENT_NMAP	((SYSASIC_EVENT_MAX + 1 + (32 - 1)) / 32)
+#define SYSASIC_EVENT_INTR_MAP(ev)	((ev) >> 5)
+#define SYSASIC_EVENT_INTR_BIT(ev)	((unsigned) 1 << ((ev) & 31))
+
 	void	*syh_intc;
-	int	syh_event;
 	int	syh_idx;
 } sysasic_intrhand[SYSASIC_IRQ_LEVEL_MAX + 1];
+
+/* per-event */
+struct	syh_eventhand {
+	int	(*hnd_fn)(void *);	/* sub handler */
+	void	*hnd_arg;
+	struct sysasic_intrhand *hnd_syh;
+} sysasic_eventhand[SYSASIC_EVENT_MAX + 1];
 
-void sysasic_intr_enable(struct sysasic_intrhand *, int);
+void sysasic_intr_enable(struct sysasic_intrhand *, int /*event*/, int /*on*/);
+int sysasic_intr(void *);
 
+const char *
+sysasic_intr_string(int ipl)
+{
+
+	switch (ipl) {
+	default:
+#ifdef DIAGNOSTIC
+		panic("sysasic_intr_string: unknown ipl %d\n", ipl);
+#endif
+	case IPL_IRL9:
+		return "SH4 IRL 9";
+	case IPL_IRL11:
+		return "SH4 IRL 11";
+	case IPL_IRL13:
+		return "SH4 IRL 13";
+	}
+	/* NOTREACHED */
+}
+
 /*
  * Set up an interrupt handler to start being called.
  */
 void *
-sysasic_intr_establish(int event, int (*ih_fun)(void *), void *ih_arg)
+sysasic_intr_establish(int event, int ipl, int (*ih_fun)(void *), void *ih_arg)
 {
 	struct sysasic_intrhand *syh;
-	int evtcode, ipl, idx;
+	struct syh_eventhand *hnd;
+	int idx;
+	static const int idx2evt[3] = {
+		SH_INTEVT_IRL13, SH_INTEVT_IRL11, SH_INTEVT_IRL9
+	};
+#ifdef DIAGNOSTIC
+	int i;
+#endif
 
-	if (event < 0 || event > SYSASIC_EVENT_MAX)
-		panic("invalid sysasic event %d", event);
+	KASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
+	KASSERT(ih_fun);
 
 	/*
-	 * Dreamcast use SH4 INTC as IRL mode. if INTEVT code is specified,
+	 * Dreamcast use SH4 INTC as IRL mode.  If IRQ is specified,
 	 * its priority level is fixed.
+	 *
+	 * We use IPL to specify the IRQ to trap programming errors. :D
 	 */
-	switch (event) {
-	case SYSASIC_EVENT_EXT:
+	switch (ipl) {
+	default:
+#ifdef DIAGNOSTIC
+		panic("sysasic_intr_establish: unknown ipl %d\n", ipl);
+#endif
+	case IPL_IRL9:
+		idx = SYSASIC_IRQ_LEVEL_9;
+		break;
+	case IPL_IRL11:
 		idx = SYSASIC_IRQ_LEVEL_11;
-		ipl = IPL_NET;
-		evtcode = SH_INTEVT_IRL11;
 		break;
-	case SYSASIC_EVENT_GDROM:
-		idx = SYSASIC_IRQ_LEVEL_9;
-		ipl = IPL_BIO;
-		evtcode = SH_INTEVT_IRL9;
+	case IPL_IRL13:
+		idx = SYSASIC_IRQ_LEVEL_13;
 		break;
-	default:
-		panic("vaild but unknown event %d\n", event);
 	}
 
 	syh = &sysasic_intrhand[idx];
 
-	syh->syh_event	= event;
-	syh->syh_idx	= idx;
-	syh->syh_intc	= intc_intr_establish(evtcode, IST_LEVEL, ipl,
-	    ih_fun, ih_arg);
+	if (syh->syh_intc == NULL) {
+		syh->syh_idx	= idx;
+		syh->syh_intc	= intc_intr_establish(idx2evt[idx], IST_LEVEL,
+		    ipl, sysasic_intr, syh);
+	}
 
-	sysasic_intr_enable(syh, 1);
+#ifdef DIAGNOSTIC
+	/* check if the event handler is already installed */
+	for (i = 0; i <= SYSASIC_IRQ_LEVEL_MAX; i++)
+		if ((sysasic_intrhand[i].syh_events[SYSASIC_EVENT_INTR_MAP(event)] &
+		    SYSASIC_EVENT_INTR_BIT(event)) != 0)
+			panic("sysasic_intr_establish: event %d already insatlled irq %d",
+			    event, SYSASIC_IRQ_INDEX_TO_IRQ(i));
+#endif
+
+	hnd = &sysasic_eventhand[event];
+	hnd->hnd_fn = ih_fun;
+	hnd->hnd_arg = ih_arg;
+	hnd->hnd_syh = syh;
+	sysasic_intr_enable(syh, event, 1);
 
-	return (void *)syh;
+	return (void *)hnd;
 }
 
 /*
@@ -104,27 +170,123 @@
 void
 sysasic_intr_disestablish(void *arg)
 {
-	struct sysasic_intrhand *syh = arg;
+	struct syh_eventhand *hnd = arg;
+	struct sysasic_intrhand *syh = hnd->hnd_syh;
 	int event;
+	int i;
 
-	event = syh->syh_event;
+	event = hnd - sysasic_eventhand;
 
+#ifdef DISAGNOSTIC
 	if (event < 0 || event > SYSASIC_EVENT_MAX)
 		panic("invalid sysasic event %d", event);
-
-	sysasic_intr_enable(syh, 0);
+	if (syh->syh_events[SYSASIC_EVENT_INTR_MAP(event)] &
+	    SYSASIC_EVENT_INTR_BIT(event)) == 0)
+		panic("sysasic_intr_disestablish: event %d not installed for irq %d",
+		    event, SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx));
+#endif
+
+	sysasic_intr_enable(syh, event, 0);
+	hnd->hnd_fn = 0;
+	hnd->hnd_arg = 0;
+	hnd->hnd_syh = 0;
+
+	/* deinstall intrc if no event exists */
+	for (i = 0; i < SYSASIC_EVENT_NMAP; i++)
+		if (syh->syh_events[i])
+			return;
 	intc_intr_disestablish(syh->syh_intc);
+	syh->syh_intc = 0;
 }
 
 void
-sysasic_intr_enable(struct sysasic_intrhand *syh, int on)
+sysasic_intr_enable(struct sysasic_intrhand *syh, int event, int on)
 {
-	int event = syh->syh_event;
 	__volatile u_int32_t *masks =
-	    (__volatile u_int32_t *)(0xa05f6910 + (syh->syh_idx << 4));
+	    (__volatile u_int32_t *) SYSASIC_INTR_EN(syh->syh_idx);
+	__volatile u_int32_t *stats = (__volatile u_int32_t *) SYSASIC_INTR_ST;
+	int evmap = SYSASIC_EVENT_INTR_MAP(event);
+	unsigned evbit = SYSASIC_EVENT_INTR_BIT(event);
+
+	if (on) {
+		/* clear pending event if any */
+		stats[evmap] = evbit;
+
+		/* set event map */
+		syh->syh_events[evmap] |= evbit;
+
+		/* enable interrupt */
+		masks[evmap] = syh->syh_events[evmap];
+	} else {
+		/* disable interrupt */
+		masks[evmap] = syh->syh_events[evmap] & ~evbit;
+
+		/* clear pending event if any */
+		stats[evmap] = evbit;
+
+		/* clear event map */
+		syh->syh_events[evmap] &= ~evbit;
+	}
+}
+
+int
+sysasic_intr(void *arg)
+{
+	struct sysasic_intrhand *syh = arg;
+	unsigned ev0, ev;
+	unsigned m, n;
+	unsigned pos;
+	struct syh_eventhand *evh;
+#ifdef DIAGNOSTIC
+	int handled = 0;
+	static int reportcnt = 10;
+#endif
+
+	for (m = 0; m < SYSASIC_EVENT_NMAP; m++) {
+		if ((ev0 = syh->syh_events[m]) &&
+		    (ev = ev0 & ((__volatile u_int32_t *)SYSASIC_INTR_ST)[m])) {
+
+			/* clear interrupts */
+			((__volatile u_int32_t *)SYSASIC_INTR_ST)[m] = ev;
+
+			/* call handlers */
+			n = (m << 5) - 1;	/* -1 to point at current bit */
+			while ((pos = ffs(ev)) != 0) {
+				n += pos;
+#ifdef __OPTIMIZE__
+				/* optimized, assuming 1 <= pos <= 32 */
+				asm("shld	%2,%0"
+				    : "=r" (ev) : "0" (ev), "r" (-pos));
+#else
+				/* ``shift count >= bit width'' is undefined */
+				if (pos >= 32)
+					ev = 0;
+				else
+					ev >>= pos;
+#endif
+
+				evh = &sysasic_eventhand[n];
+#ifdef DIAGNOSTIC
+				KASSERT(evh->hnd_fn);
+				if ((*evh->hnd_fn)(evh->hnd_arg))
+					handled = 1;
+#else
+				(*evh->hnd_fn)(evh->hnd_arg);
+#endif
+			}
+		}
+	}
+
+#ifdef DIAGNOSTIC
+	if (!handled) {
+		if (reportcnt >= 0) {
+			reportcnt--;
+			log(LOG_ERR, "sysasic_intr: stray irq%d interrupt%s\n",
+			    SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx),
+			    reportcnt == 0 ? "; stopped logging" : "");
+		}
+	}
+#endif
 
-	masks[0] = 0;
-	masks[1] = 0;
-	if (on)
-		masks[event >> 5] = 1 << (event & 31);
+	return 0;
 }
Index: include/intr.h
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/dreamcast/include/intr.h,v
retrieving revision 1.3
diff -u -r1.3 intr.h
--- include/intr.h	2002/03/24 18:21:08	1.3
+++ include/intr.h	2002/08/31 15:41:40
@@ -39,12 +39,12 @@
 #include <sh3/intr.h>
 
 /* Number of interrupt source */
-#define _INTR_N		9	/* TMU0, TMU1, TMU2, SCIF * 4, GDROM, GAPSPCI */
+#define _INTR_N		10	/* TMU0, TMU1, TMU2, SCIF * 4, IRL * 3 */
 
 /* Interrupt priority levels */
 #define	IPL_BIO		9	/* block I/O	(GD-ROM) */
 #define	IPL_NET		11	/* network	(GAPS PCI) */
-#define	IPL_TTY		12	/* terminal */
+#define	IPL_TTY		12	/* terminal	(Maple) */
 #define	IPL_SERIAL	12	/* serial */
 #define	IPL_CLOCK	14	/* clock */
 #define	IPL_HIGH	15	/* everything */
Index: include/sysasicvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/dreamcast/include/sysasicvar.h,v
retrieving revision 1.2
diff -u -r1.2 sysasicvar.h
--- include/sysasicvar.h	2002/03/24 18:21:09	1.2
+++ include/sysasicvar.h	2002/08/31 15:41:40
@@ -39,13 +39,15 @@
 #ifndef _DREAMCAST_SYSASICVAR_H_
 #define	_DREAMCAST_SYSASICVAR_H_
 
-#define SYSASIC_EVENT_GDROM  32
-#define SYSASIC_EVENT_AICA   33
-#define SYSASIC_EVENT_EXT    35
-#define SYSASIC_EVENT_MAX    63
+#define SYSASIC_EVENT_MAPLE_DMADONE	12
+#define SYSASIC_EVENT_MAPLE_ERROR	13
+#define SYSASIC_EVENT_GDROM		32
+#define SYSASIC_EVENT_AICA		33
+#define SYSASIC_EVENT_EXT		35
+#define SYSASIC_EVENT_MAX		65
 
-void	*sysasic_intr_establish(int, int (*ih_fun)(void *), void *);
+const char *sysasic_intr_string(int /*ipl*/) __attribute__((__const__));
+void	*sysasic_intr_establish(int /*event*/, int /*ipl*/, int (*ih_fun)(void *), void *);
 void    sysasic_intr_disestablish(void *);
 
 #endif /* !_DREAMCAST_SYSASICVAR_H_ */
-
Index: dev/gdrom.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/dreamcast/dev/gdrom.c,v
retrieving revision 1.9
diff -u -r1.9 gdrom.c
--- dev/gdrom.c	2002/07/27 11:07:35	1.9
+++ dev/gdrom.c	2002/08/31 15:41:40
@@ -187,7 +187,7 @@
 	}
 
 	splx(s);
-	return (0);
+	return (1);
 }
 
 
@@ -372,8 +372,6 @@
 
 	sc = (struct gdrom_softc *)self;
 
-	printf(": SH4 IRL 9\n");
-
 	/*
 	 * Initialize and attach the disk structure.
 	 */
@@ -388,7 +386,8 @@
 	for (p = 0; p < 0x200000 / 4; p++)
 		x = ((__volatile u_int32_t *)0xa0000000)[p];
 
-	sysasic_intr_establish(SYSASIC_EVENT_GDROM, gdrom_intr, sc);
+	printf(": %s\n", sysasic_intr_string(IPL_BIO));
+	sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, gdrom_intr, sc);
 }
 
 int
Index: dev/g2/gapspci_pci.c
===================================================================
RCS file: /cvsroot/syssrc/sys/arch/dreamcast/dev/g2/gapspci_pci.c,v
retrieving revision 1.4
diff -u -r1.4 gapspci_pci.c
--- dev/g2/gapspci_pci.c	2002/05/15 17:09:04	1.4
+++ dev/g2/gapspci_pci.c	2002/08/31 15:41:40
@@ -199,7 +199,7 @@
 gaps_intr_string(void *v, pci_intr_handle_t ih)
 {
 
-	return ("SH4 IRL 11");
+	return sysasic_intr_string(IPL_NET);
 }
 
 void *
@@ -207,7 +207,7 @@
     int (*func)(void *), void *arg)
 {
 
-	return (sysasic_intr_establish(ih, func, arg));
+	return (sysasic_intr_establish(ih, IPL_NET, func, arg));
 }
 
 void

------- =_aaaaaaaaaa0--