Subject: i2c device interrupt handlers
To: None <tech-kern@NetBSD.org>
From: Jared D. McNeill <jmcneill@invisible.ca>
List: tech-kern
Date: 02/05/2007 08:40:03
--Apple-Mail-3--897924955
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	delsp=yes;
	format=flowed

Heyas folks --

NetBSD/xbox requires some magic to happen in an interrupt handler to  
prevent the machine from resetting when the eject button is pressed.  
Since iic_exec *may* sleep, I've created a separate kthread to  
actually run the interrupt handlers and wake it up on an smbus  
interrupt.

Something has gone wrong though; in the original non-kthread  
implementation I had, I was successfully trapping the interrupt and  
responding in time, and I could eject / close the tray (the system  
would freeze shortly after, but at least wouldn't reset). Now with  
this kthread implementation, the system resets immediately after I  
press the eject button.

Is there something obviously wrong with my logic here? Patch is  
attached.

Cheers,
Jared


--Apple-Mail-3--897924955
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name=i2c-intr.patch
Content-Disposition: attachment;
	filename=i2c-intr.patch

Index: i2c.c
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/i2c.c,v
retrieving revision 1.10
diff -u -r1.10 i2c.c
--- i2c.c	16 Nov 2006 01:32:50 -0000	1.10
+++ i2c.c	5 Feb 2007 13:33:58 -0000
@@ -40,6 +40,10 @@
 #include <sys/device.h>
 #include <sys/event.h>
 #include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/kthread.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
 
 #include <dev/i2c/i2cvar.h>
 
@@ -99,6 +103,47 @@
 }
 
 static void
+iic_smbus_intr_thread1(void *aux)
+{
+	i2c_tag_t ic;
+	struct ic_intr_list *il;
+	int rv;
+
+	ic = (i2c_tag_t)aux;
+	ic->ic_running = 1;
+	ic->ic_pending = 0;
+
+	printf("iic_smbus_intr_thread1: started\n");
+	while (ic->ic_running) {
+		if (ic->ic_pending == 0)
+			rv = tsleep(ic, PZERO, "iicintr", hz);
+		if (ic->ic_pending > 0) {
+			LIST_FOREACH(il, &(ic->ic_list), il_next) {
+				(*il->il_intr)(il->il_intrarg);
+			}
+			ic->ic_pending--;
+		}
+	}
+
+	kthread_exit(0);
+}
+
+static void
+iic_smbus_intr_thread(void *aux)
+{
+	i2c_tag_t ic;
+	int rv;
+
+	ic = (i2c_tag_t)aux;
+
+	rv = kthread_create1(iic_smbus_intr_thread1, ic, &ic->ic_intr_thread,
+	    "%s", ic->ic_devname);
+	if (rv)
+		printf("%s: unable to create intr thread\n", ic->ic_devname);
+}
+
+
+static void
 iic_attach(struct device *parent, struct device *self, void *aux)
 {
 	struct iic_softc *sc = device_private(self);
@@ -109,6 +154,9 @@
 
 	sc->sc_tag = iba->iba_tag;
 	sc->sc_type = iba->iba_type;
+	sc->sc_tag->ic_devname = self->dv_xname;
+
+	kthread_create(iic_smbus_intr_thread, sc->sc_tag);
 
 	/*
 	 * Attach all i2c devices described in the kernel
@@ -117,5 +165,45 @@
 	config_search_ia(iic_search, self, "iic", NULL);
 }
 
+void *
+iic_smbus_intr_establish(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
+{
+	struct ic_intr_list *il;
+
+	il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
+	if (il == NULL)
+		return NULL;
+	    
+	il->il_intr = intr;
+	il->il_intrarg = intrarg;
+
+	LIST_INSERT_HEAD(&(ic->ic_list), il, il_next);
+
+	return il;
+}
+
+void
+iic_smbus_intr_disestablish(i2c_tag_t ic, void *hdl)
+{
+	struct ic_intr_list *il;
+
+	il = (struct ic_intr_list *)hdl;
+
+	LIST_REMOVE(il, il_next);
+	free(il, M_DEVBUF);
+
+	return;
+}
+
+int
+iic_smbus_intr(i2c_tag_t ic)
+{
+
+	ic->ic_pending++;
+	wakeup(ic);
+
+	return 1;
+}
+
 CFATTACH_DECL(iic, sizeof(struct iic_softc),
     iic_match, iic_attach, NULL, NULL);
Index: i2cvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/i2cvar.h,v
retrieving revision 1.4
diff -u -r1.4 i2cvar.h
--- i2cvar.h	26 Jun 2006 18:19:40 -0000	1.4
+++ i2cvar.h	5 Feb 2007 13:33:58 -0000
@@ -48,6 +48,12 @@
 #define	I2C_F_POLL		0x08	/* poll, don't sleep */
 #define	I2C_F_PEC		0x10	/* smbus packet error checking */
 
+struct ic_intr_list {
+	LIST_ENTRY(ic_intr_list) il_next;
+	int (*il_intr)(void *);
+	void *il_intrarg;
+};
+
 /*
  * This structure provides the interface between the i2c framework
  * and the underlying i2c controller.
@@ -88,6 +94,12 @@
 	int	(*ic_initiate_xfer)(void *, i2c_addr_t, int);
 	int	(*ic_read_byte)(void *, uint8_t *, int);
 	int	(*ic_write_byte)(void *, uint8_t, int);
+
+	LIST_HEAD(, ic_intr_list) ic_list;
+	int	ic_running;
+	int	ic_pending;
+	struct proc *ic_intr_thread;
+	const char *ic_devname;
 } *i2c_tag_t;
 
 /* I2C bus types */
@@ -154,4 +166,8 @@
 int	iic_smbus_block_write(i2c_tag_t, i2c_addr_t, uint8_t, uint8_t *,
 	    size_t, int);
 
+void *	iic_smbus_intr_establish(i2c_tag_t, int (*)(void *), void *);
+void	iic_smbus_intr_disestablish(i2c_tag_t, void *);
+int	iic_smbus_intr(i2c_tag_t);
+
 #endif /* _DEV_I2C_I2CVAR_H_ */

--Apple-Mail-3--897924955--