Tue Oct 27 20:13:21 2020 UTC ()
Add additional debugging output.
Track more chip state in the softc and check the chip state via a timer.
This allows us to easily observe changes caused by external events
(e.g. disk removal or PSU failure).


(jdc)
diff -r1.3 -r1.4 src/sys/dev/i2c/pcagpio.c

cvs diff -r1.3 -r1.4 src/sys/dev/i2c/pcagpio.c (expand / switch to context diff)
--- src/sys/dev/i2c/pcagpio.c 2020/02/02 06:43:14 1.3
+++ src/sys/dev/i2c/pcagpio.c 2020/10/27 20:13:21 1.4
@@ -1,4 +1,4 @@
-/* $NetBSD: pcagpio.c,v 1.3 2020/02/02 06:43:14 macallan Exp $ */
+/* $NetBSD: pcagpio.c,v 1.4 2020/10/27 20:13:21 jdc Exp $ */
 
 /*-
  * Copyright (c) 2020 Michael Lorenz
@@ -31,11 +31,14 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.3 2020/02/02 06:43:14 macallan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pcagpio.c,v 1.4 2020/10/27 20:13:21 jdc Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#ifdef PCAGPIO_DEBUG
+#include <sys/kernel.h>
+#endif
 #include <sys/conf.h>
 #include <sys/bus.h>
 
@@ -56,6 +59,10 @@
 
 static int	pcagpio_match(device_t, cfdata_t, void *);
 static void	pcagpio_attach(device_t, device_t, void *);
+static int	pcagpio_detach(device_t, int);
+#ifdef PCAGPIO_DEBUG
+static void	pcagpio_timeout(void *);
+#endif
 
 /* we can only pass one cookie to led_attach() but we need several values... */
 struct pcagpio_led {
@@ -72,6 +79,11 @@
 	uint32_t	sc_state;
 	struct pcagpio_led sc_leds[16];
 	int		sc_nleds;
+
+#ifdef PCAGPIO_DEBUG
+	uint32_t	sc_dir, sc_in;
+	callout_t	sc_timer;
+#endif
 };
 
 
@@ -83,7 +95,7 @@
 static void	pcagpio_set(void *, int);
 
 CFATTACH_DECL_NEW(pcagpio, sizeof(struct pcagpio_softc),
-    pcagpio_match, pcagpio_attach, NULL, NULL);
+    pcagpio_match, pcagpio_attach, pcagpio_detach, NULL);
 
 static const struct device_compatible_entry compat_data[] = {
 	{ "i2c-pca9555",	1 },
@@ -107,7 +119,7 @@
 
 #ifdef PCAGPIO_DEBUG
 static void
-printdir(uint32_t val, uint32_t mask, char letter)
+printdir(char* name, uint32_t val, uint32_t mask, char letter)
 {
 	char flags[17], bits[17];
 	uint32_t bit = 0x8000;
@@ -121,8 +133,8 @@
 	}
 	flags[16] = 0;
 	bits[16] = 0;
-	printf("dir: %s\n", flags);
-	printf("lvl: %s\n", bits);
+	printf("%s: dir: %s\n", name, flags);
+	printf("%s: lvl: %s\n", name, bits);
 }	
 #endif
 
@@ -151,16 +163,21 @@
 	sc->sc_state = pcagpio_readreg(sc, PCAGPIO_OUTPUT);
 
 #ifdef PCAGPIO_DEBUG
-	uint32_t dir, in, out;
-	dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
-	in = pcagpio_readreg(sc, PCAGPIO_INPUT);
+	uint32_t in, out;
+	sc->sc_dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
+	sc->sc_in = pcagpio_readreg(sc, PCAGPIO_INPUT);
+	in = sc-> sc_in;
 	out = sc->sc_state;
 
-	out &= ~dir;
-	in &= dir;
+	out &= ~sc->sc_dir;
+	in &= sc->sc_dir;
 	
-	printdir(in, dir, 'I');
-	printdir(out, ~dir, 'O');
+	printdir(sc->sc_dev->dv_xname, in, sc->sc_dir, 'I');
+	printdir(sc->sc_dev->dv_xname, out, ~sc->sc_dir, 'O');
+
+	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
+	callout_reset(&sc->sc_timer, hz*20, pcagpio_timeout, sc);
+
 #endif
 
 	pins = prop_dictionary_get(dict, "pins");
@@ -188,6 +205,49 @@
 		}
 	}
 }
+
+static int
+pcagpio_detach(device_t self, int flags)
+{
+#ifdef PCAGPIO_DEBUG
+	struct pcagpio_softc *sc = device_private(self);
+
+	callout_halt(&sc->sc_timer, NULL);
+	callout_destroy(&sc->sc_timer);
+#endif
+
+	return 0;
+}
+
+#ifdef PCAGPIO_DEBUG
+static void
+pcagpio_timeout(void *v)
+{
+	struct pcagpio_softc *sc = v;
+	uint32_t out, dir, in, o_out, o_in;
+
+	out = pcagpio_readreg(sc, PCAGPIO_OUTPUT);
+	dir = pcagpio_readreg(sc, PCAGPIO_CONFIG);
+	in = pcagpio_readreg(sc, PCAGPIO_INPUT);
+	if (out != sc->sc_state || dir != sc->sc_dir || in != sc->sc_in) {
+		aprint_normal_dev(sc->sc_dev, "status change\n");
+		o_out = sc->sc_state;
+		o_in = sc->sc_in;
+		o_out &= ~sc->sc_dir;
+		o_in &= sc->sc_dir;
+		printdir(sc->sc_dev->dv_xname, o_in, sc->sc_dir, 'I');
+		printdir(sc->sc_dev->dv_xname, o_out, ~sc->sc_dir, 'O');
+		sc->sc_state = out;
+		sc->sc_dir = dir;
+		sc->sc_in = in;
+		out &= ~sc->sc_dir;
+		in &= sc->sc_dir;
+		printdir(sc->sc_dev->dv_xname, in, sc->sc_dir, 'I');
+		printdir(sc->sc_dev->dv_xname, out, ~sc->sc_dir, 'O');
+	}
+	callout_reset(&sc->sc_timer, hz*60, pcagpio_timeout, sc);
+}
+#endif
 
 static void
 pcagpio_writereg(struct pcagpio_softc *sc, int reg, uint32_t val)