Subject: Re: MD hooks in zsopen()/zsclose()
To: NetBSD tech-kern mailing list <tech-kern@netbsd.org>
From: Julian Coleman <J.D.Coleman@newcastle.ac.uk>
List: tech-kern
Date: 03/04/2000 20:15:35
Jason Thorpe wrote:
> Take a look at dev/ic/com.c for the enable/disable hooks present in that
> driver (for power management on PCMCIA cards).

Thanks.  Most useful.  Almost a cut and paste job ;-)

Bill Studenmund wrote:
> Since the routine you're calling is in the MD part of the zs code, struct
> zs_chanstate *cs is a good thing. Much better than the dev (which only has
> meaning to the zs device driver). What you're wanting to do is have the
> power turned on on this particular channel, not on a particular device
> number. Also, it would make sense to be able to turn on and off the power
> on the zs mouse and zs keyboard hooks, if you're using the ports that way
> and the hardware supports it. But the main thing is that you're calling a
> level where it makes sense to not be tty-centric. :-)

Passing cs is good, it's common between the MD zsc_softc and MI zstty_softc.
Thanks.  I wasn't sure whether anything was needed - on the Sparcbook, both
channels are powered on/off at the same time, so I only need to know when
any channel is opened or closed.

Agreed, the way the code is structured, I don't think it would make any sense
to have this tty-centric.  Not that I know of a machine where you can power
the mouse and keyboard channels on and off.  Then again, there's probably an
undocumented register value on the Sparcbook that does just that ;-)

> Oh, don't do reference counting here. The open() routine gets called each
> time someone opens the device, but the close only gets called after the
> last close. So if there's open open open close close close in userland,
> the driver only sees open open open close.

Yes.  I'm doing the reference counting in the register driver code.  This
handles the fact that both channels are powered on (and off) at the same
time and also keeps track of the power state (always off / on when open /
always on).  It seems that the shutdown() routine gets called on last close
and close() is called on every close?

> What happened with function pointers? They are a much cleaner way to go..
> If not, I'd vote for names other than zsopen() and zsclose(). You are
> really asking for power to come on or go off. :-)

I was doing it in a somewhat non-optimum way.  The problem is that the MD
code deals with zsc's and the MI code deals with zst's.

The names zsopen() and zsclose() are the functions in the MD z8530tty.c,
where I've put the hooks (I should have been more explicit).  More diffs
attached (apologies that arch/sparc/dev/zs.c is against 1.71).  The only
bit I don't like is the way the zst->en|disable is grabbed from
zsc->en|disable in zs_attach().  Is there a better way to do this?

> Note: both mac68k and macppc can use this too. There are portables with
> power control on the serial ports. :-)

Great!  Do they work like the Sparcbook and power both on/off at the same
time?

Thanks,

J

PS.  Now all I need to do is understand why one channel somestimes sends
garbage and why the external mouse doesn't work ... I think I'm going to
end up knowing somewhat more about the 8530 than I do now ;-)

-- 
                    My other computer also runs NetBSD
                          http://www.netbsd.org/

 ---8<---------------------------- Cut here ---------------------------->8---

--- dev/ic/z8530tty.c.dist	Thu Nov  4 12:20:53 1999
+++ dev/ic/z8530tty.c	Sat Mar  4 19:56:24 2000
@@ -190,6 +190,11 @@
 			zst_st_check,	/* got a status interrupt */
 			zst_rx_ready;
 
+	/* power management hooks */
+	int (*enable) __P((struct zs_chanstate *));
+	void (*disable) __P((struct zs_chanstate *));
+	int enabled;
+
 	/* PPS signal on DCD, with or without inkernel clock disciplining */
 	u_char  zst_ppsmask;			/* pps signal mask */
 	u_char  zst_ppsassert;			/* pps leading edge */
@@ -325,7 +330,12 @@
 	zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf;
 	zst->zst_rbavail = zstty_rbuf_size;
 
-	/* XXX - Do we need an MD hook here? */
+	/* if there are no enable/disable functions, assume the device
+	   is always enabled */
+	zst->enable = zsc->enable;
+	zst->disable = zsc->disable;
+	if (!zst->enable)
+		zst->enabled = 1;
 
 	/*
 	 * Hardware init
@@ -474,6 +484,21 @@
 
 	s = spltty();
 
+	/* 
+	 * Call the power management hook on every open.  The Z8530 has two
+	 * channels, so we can't base power on/off on first open and shutdown.
+	 */
+	if (zst->enable) {
+		if ((*zst->enable)(cs)) {
+			splx(s2);
+			splx(s);
+			printf("%s: device enable failed\n",
+			       zst->zst_dev.dv_xname);
+			return (EIO);
+		}
+		zst->enabled = 1;
+	}
+
 	/*
 	 * Do the following iff this is a first open.
 	 */
@@ -577,6 +602,15 @@
 		zs_shutdown(zst);
 	}
 
+	if (zst->disable) {
+#ifdef DIAGNOSTIC
+		if (!zst->enabled)
+			panic("zs_shutdown: not enabled?");
+#endif
+		(*zst->disable)(cs);
+		zst->enabled = 0;
+	}
+
 	return (error);
 }
 
@@ -607,6 +641,19 @@
 		 * processes waiting for carrier on the non-dialout node.
 		 */
 		zs_shutdown(zst);
+	}
+
+	/* 
+	 * Call the power management hook on every close.  The Z8530 has two
+	 * channels, so we can't base power on/off on first open and shutdown.
+	 */
+	if (zst->disable) {
+#ifdef DIAGNOSTIC
+		if (!zst->enabled)
+			panic("zs_shutdown: not enabled?");
+#endif
+		(*zst->disable)(zst->zst_cs);
+		zst->enabled = 0;
 	}
 
 	return (0);
--- arch/sparc/dev/zs.c.dist	Mon Feb 21 20:56:39 2000
+++ arch/sparc/dev/zs.c	Sat Mar  4 19:53:52 2000
@@ -71,6 +71,7 @@
 
 #include <sparc/sparc/vaddrs.h>
 #include <sparc/sparc/auxreg.h>
+#include <sparc/sparc/auxiotwo.h>
 #include <sparc/dev/cons.h>
 
 #include "kbd.h"	/* NKBD */
@@ -194,6 +195,9 @@
 static void zs_attach __P((struct zsc_softc *, int));
 static int  zs_print __P((void *, const char *name));
 
+int zs_enable __P((struct zs_chanstate *));
+void zs_disable __P((struct zs_chanstate *));
+
 struct cfattach zs_mainbus_ca = {
 	sizeof(struct zsc_softc), zs_match_mainbus, zs_attach_mainbus
 };
@@ -292,6 +296,17 @@
 		struct sbus_attach_args *sa = &uoba->uoba_sbus;
 		zsc->zsc_bustag = sa->sa_bustag;
 		zsc->zsc_dmatag = sa->sa_dmatag;
+
+		/*
+		 * Check if power state can be set, e.g. Tadpole 3GX
+		 */
+		if (getpropint(sa->sa_node, "pwr-on-auxio2", 0))
+		{
+			printf (" powered via auxio2");
+			zsc->enable = zs_enable;
+			zsc->disable = zs_disable;
+		}
+
 		if (sa->sa_nintr != 0)
 			zs_attach(zsc, sa->sa_pri);
 	} else {
@@ -1058,4 +1073,23 @@
 #ifdef	KGDB
 	zs_kgdb_init();
 #endif
+}
+
+/*
+ * Power management hooks for zsopen() and zsclose()
+ * We use them to power on/off the ports, if necessary.
+ */
+int
+zs_enable (cs)
+	struct zs_chanstate *cs;
+{
+	auxiotwoserialendis (ZS_ENABLE);
+	return (0);
+}
+
+void
+zs_disable (cs)
+	struct zs_chanstate *cs;
+{
+	auxiotwoserialendis (ZS_DISABLE);
 }