Module Name:    src
Committed By:   thorpej
Date:           Sun May 20 14:08:33 UTC 2018

Modified Files:
        src/distrib/sets/lists/man: mi
        src/distrib/sets/lists/modules: mi
        src/etc: MAKEDEV.tmpl
        src/share/man/man4: Makefile
        src/share/man/man8: MAKEDEV.8
        src/sys/conf: majors
        src/sys/dev/gpio: files.gpio
        src/sys/modules: Makefile
Added Files:
        src/sys/dev/gpio: gpiopps.c

Log Message:
Add a 1PPS-over-GPIO driver, originally by Brad Spencer, with changes by
me to adapt to the new GPIO interrupt interface and support a wider variety
of GPIO pin configuations.

PR kern/51676


To generate a diff of this commit:
cvs rdiff -u -r1.1587 -r1.1588 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.115 -r1.116 src/distrib/sets/lists/modules/mi
cvs rdiff -u -r1.189 -r1.190 src/etc/MAKEDEV.tmpl
cvs rdiff -u -r1.653 -r1.654 src/share/man/man4/Makefile
cvs rdiff -u -r1.47 -r1.48 src/share/man/man8/MAKEDEV.8
cvs rdiff -u -r1.78 -r1.79 src/sys/conf/majors
cvs rdiff -u -r1.15 -r1.16 src/sys/dev/gpio/files.gpio
cvs rdiff -u -r0 -r1.1 src/sys/dev/gpio/gpiopps.c
cvs rdiff -u -r1.203 -r1.204 src/sys/modules/Makefile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1587 src/distrib/sets/lists/man/mi:1.1588
--- src/distrib/sets/lists/man/mi:1.1587	Sat May 19 14:15:39 2018
+++ src/distrib/sets/lists/man/mi	Sun May 20 14:08:32 2018
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1587 2018/05/19 14:15:39 thorpej Exp $
+# $NetBSD: mi,v 1.1588 2018/05/20 14:08:32 thorpej Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1124,6 +1124,7 @@
 ./usr/share/man/cat4/gpiolock.0			man-sys-catman		.cat
 ./usr/share/man/cat4/gpioow.0			man-sys-catman		.cat
 ./usr/share/man/cat4/gpioirq.0			man-sys-catman		.cat
+./usr/share/man/cat4/gpiopps.0			man-sys-catman		.cat
 ./usr/share/man/cat4/gpiopwm.0			man-sys-catman		.cat
 ./usr/share/man/cat4/gpiosim.0			man-sys-catman		.cat
 ./usr/share/man/cat4/gre.0			man-sys-catman		.cat
@@ -4265,6 +4266,7 @@
 ./usr/share/man/html4/gpiolock.html		man-sys-htmlman		html
 ./usr/share/man/html4/gpioow.html		man-sys-htmlman		html
 ./usr/share/man/html4/gpioirq.html		man-sys-htmlman		html
+./usr/share/man/html4/gpiopps.html		man-sys-htmlman		html
 ./usr/share/man/html4/gpiopwm.html		man-sys-htmlman		html
 ./usr/share/man/html4/gpiosim.html		man-sys-htmlman		html
 ./usr/share/man/html4/gre.html			man-sys-htmlman		html
@@ -7180,6 +7182,7 @@
 ./usr/share/man/man4/gpiolock.4			man-sys-man		.man
 ./usr/share/man/man4/gpioow.4			man-sys-man		.man
 ./usr/share/man/man4/gpioirq.4			man-sys-man		.man
+./usr/share/man/man4/gpiopps.4			man-sys-man		.man
 ./usr/share/man/man4/gpiopwm.4			man-sys-man		.man
 ./usr/share/man/man4/gpiosim.4			man-sys-man		.man
 ./usr/share/man/man4/gre.4			man-sys-man		.man

Index: src/distrib/sets/lists/modules/mi
diff -u src/distrib/sets/lists/modules/mi:1.115 src/distrib/sets/lists/modules/mi:1.116
--- src/distrib/sets/lists/modules/mi:1.115	Sat May 19 14:15:39 2018
+++ src/distrib/sets/lists/modules/mi	Sun May 20 14:08:33 2018
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.115 2018/05/19 14:15:39 thorpej Exp $
+# $NetBSD: mi,v 1.116 2018/05/20 14:08:33 thorpej Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -136,6 +136,8 @@
 ./@MODULEDIR@/gpioirq/gpioirq.kmod		base-kernel-modules	kmod
 ./@MODULEDIR@/gpioow				base-kernel-modules	kmod
 ./@MODULEDIR@/gpioow/gpioow.kmod		base-kernel-modules	kmod
+./@MODULEDIR@/gpiopps				base-kernel-modules	kmod
+./@MODULEDIR@/gpiopps/gpiopps.kmod		base-kernel-modules	kmod
 ./@MODULEDIR@/gpiosim				base-kernel-modules	kmod
 ./@MODULEDIR@/gpiosim/gpiosim.kmod		base-kernel-modules	kmod
 ./@MODULEDIR@/hfs				base-kernel-modules	kmod

Index: src/etc/MAKEDEV.tmpl
diff -u src/etc/MAKEDEV.tmpl:1.189 src/etc/MAKEDEV.tmpl:1.190
--- src/etc/MAKEDEV.tmpl:1.189	Tue Jan  9 03:31:14 2018
+++ src/etc/MAKEDEV.tmpl	Sun May 20 14:08:33 2018
@@ -1,5 +1,5 @@
 #!/bin/sh -
-#	$NetBSD: MAKEDEV.tmpl,v 1.189 2018/01/09 03:31:14 christos Exp $
+#	$NetBSD: MAKEDEV.tmpl,v 1.190 2018/05/20 14:08:33 thorpej Exp $
 #
 # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -242,6 +242,7 @@
 #	dtv*	Digital TV interface
 #	fb*	PMAX generic framebuffer pseudo-device
 #	fd	file descriptors
+#	gpiopps* 1PPS signals on GPIO pins
 #	grf*	graphics frame buffer device
 #	hdaudio* High Definition audio control device
 #	hdmicec* HDMI CEC devices
@@ -829,6 +830,7 @@ all)
 	makedev atabus0 atabus1 atabus2 atabus3 atabus4 atabus5 atabus6 atabus7
 	makedev tap tap0 tap1 tap2 tap3
 	makedev gpio gpio0 gpio1 gpio2 gpio3 gpio4 gpio5 gpio6 gpio7
+	makedev gpiopps0
 	makedev pad pad0 pad1 pad2 pad3
 	makedev bthub
 	makedev putter
@@ -866,6 +868,11 @@ gpio)
 	lndev gpio0 gpio
 	;;
 
+gpiopps)
+	makedev gpiopps0
+	lndev gpiopps0 gpiopps
+	;;
+
 lua)
 	makedev lua0
 	lndev lua0 lua
@@ -1535,6 +1542,11 @@ gpio[0-9]*)
 	mkdev gpio$unit c %gpio_chr% $unit 664 $g_gpio
 	;;
 
+gpiopps[0-9]*)
+	unit=${i#gpiopps}
+	mkdev gpiopps$unit c %gpiopps_chr% $unit 664 $g_gpio
+	;;
+
 lua[0-9]*)
 	unit=${i#lua}
 	mkdev lua$unit c %lua_chr% $unit 664

Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.653 src/share/man/man4/Makefile:1.654
--- src/share/man/man4/Makefile:1.653	Sat May 19 14:15:39 2018
+++ src/share/man/man4/Makefile	Sun May 20 14:08:33 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.653 2018/05/19 14:15:39 thorpej Exp $
+#	$NetBSD: Makefile,v 1.654 2018/05/20 14:08:33 thorpej Exp $
 #	@(#)Makefile	8.1 (Berkeley) 6/18/93
 
 MAN=	aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@@ -26,7 +26,7 @@ MAN=	aac.4 ac97.4 acardide.4 aceride.4 a
 	fast_ipsec.4 fd.4 filemon.4 finsio.4 flash.4 fpa.4 fms.4 fss.4 \
 	fujbp.4 full.4 fxp.4 \
 	gcscaudio.4 gem.4 genfb.4 gentbi.4 geodeide.4 \
-	glxtphy.4 gpib.4 gpio.4 gpioirq.4 gpiolock.4 gpiopwm.4 \
+	glxtphy.4 gpib.4 gpio.4 gpioirq.4 gpiolock.4 gpiopps.4 gpiopwm.4 \
 	gpiosim.4 gre.4 gphyter.4 gsip.4 \
 	hdaudio.4 hifn.4 hme.4 hpacel.4 hpqlb.4 hptide.4 hythygtemp.4 \
 	ibmcd.4 ibmhawk.4 ichsmb.4 icmp.4 icp.4 icsphy.4 iee.4 ieee80211.4 \

Index: src/share/man/man8/MAKEDEV.8
diff -u src/share/man/man8/MAKEDEV.8:1.47 src/share/man/man8/MAKEDEV.8:1.48
--- src/share/man/man8/MAKEDEV.8:1.47	Tue Jan 23 19:14:09 2018
+++ src/share/man/man8/MAKEDEV.8	Sun May 20 14:08:33 2018
@@ -6,7 +6,7 @@
 .\" *** DO NOT EDIT - any changes will be lost!!!
 .\" *** ------------------------------------------------------------------
 .\"
-.\" $NetBSD: MAKEDEV.8,v 1.47 2018/01/23 19:14:09 sevan Exp $
+.\" $NetBSD: MAKEDEV.8,v 1.48 2018/05/20 14:08:33 thorpej Exp $
 .\"
 .\" Copyright (c) 2001, 2003, 2007, 2008 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -35,7 +35,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd January 23, 2018
+.Dd May 11, 2018
 .Dt MAKEDEV 8
 .Os
 .Sh NAME
@@ -736,6 +736,9 @@ Digital TV interface, see
 PMAX generic framebuffer pseudo-device
 . It Ar fd
 File descriptors
+. It Ar gpiopps#
+1PPS signals on GPIO pins, see
+.Xr \&gpiopps 4
 . It Ar grf#
 Graphics frame buffer device, see
 .Xr \&amiga/grf 4

Index: src/sys/conf/majors
diff -u src/sys/conf/majors:1.78 src/sys/conf/majors:1.79
--- src/sys/conf/majors:1.78	Tue Jan  9 03:31:12 2018
+++ src/sys/conf/majors	Sun May 20 14:08:33 2018
@@ -1,4 +1,4 @@
-# $NetBSD: majors,v 1.78 2018/01/09 03:31:12 christos Exp $
+# $NetBSD: majors,v 1.79 2018/05/20 14:08:33 thorpej Exp $
 #
 # Device majors for Machine-Independent drivers.
 #
@@ -77,3 +77,4 @@ device-major hdmicec   char 340		   hdmi
 device-major nvme      char 341		   nvme
 device-major qemufwcfg char 342		   qemufwcfg
 device-major autofs    char 343		   autofs
+device-major gpiopps   char 344            gpiopps

Index: src/sys/dev/gpio/files.gpio
diff -u src/sys/dev/gpio/files.gpio:1.15 src/sys/dev/gpio/files.gpio:1.16
--- src/sys/dev/gpio/files.gpio:1.15	Sat May 19 14:15:39 2018
+++ src/sys/dev/gpio/files.gpio	Sun May 20 14:08:33 2018
@@ -1,4 +1,4 @@
-# $NetBSD: files.gpio,v 1.15 2018/05/19 14:15:39 thorpej Exp $
+# $NetBSD: files.gpio,v 1.16 2018/05/20 14:08:33 thorpej Exp $
 
 define	gpio {[offset = -1], [mask = 0], [flag = 0]}
 
@@ -44,3 +44,8 @@ file	dev/gpio/gpiobutton.c			gpiobutton
 device	gpioirq: gpiobus
 attach	gpioirq at gpio
 file	dev/gpio/gpioirq.c			gpioirq
+
+# GPIO PPS
+device	gpiopps: gpiobus
+attach	gpiopps at gpio
+file	dev/gpio/gpiopps.c			gpiopps

Index: src/sys/modules/Makefile
diff -u src/sys/modules/Makefile:1.203 src/sys/modules/Makefile:1.204
--- src/sys/modules/Makefile:1.203	Sat May 19 14:15:40 2018
+++ src/sys/modules/Makefile	Sun May 20 14:08:33 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.203 2018/05/19 14:15:40 thorpej Exp $
+#	$NetBSD: Makefile,v 1.204 2018/05/20 14:08:33 thorpej Exp $
 
 .include <bsd.own.mk>
 
@@ -48,6 +48,7 @@ SUBDIR+=	gpioiic
 SUBDIR+=	gpioow
 SUBDIR+=	gpiosim
 SUBDIR+=	gpioirq
+SUBDIR+=	gpiopps
 SUBDIR+=	hfs
 SUBDIR+=	hythygtemp
 SUBDIR+=	si70xxtemp

Added files:

Index: src/sys/dev/gpio/gpiopps.c
diff -u /dev/null src/sys/dev/gpio/gpiopps.c:1.1
--- /dev/null	Sun May 20 14:08:33 2018
+++ src/sys/dev/gpio/gpiopps.c	Sun May 20 14:08:33 2018
@@ -0,0 +1,540 @@
+/* $NetBSD: gpiopps.c,v 1.1 2018/05/20 14:08:33 thorpej Exp $ */
+
+/*
+ * Copyright (c) 2016 Brad Spencer <b...@anduin.eldar.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: gpiopps.c,v 1.1 2018/05/20 14:08:33 thorpej Exp $");
+
+/*
+ * GPIO interface to the pps subsystem for ntp support.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bitops.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/timepps.h>
+
+#include <sys/gpio.h>
+#include <dev/gpio/gpiovar.h>
+
+#define	GPIOPPS_NPINS		2
+
+struct gpiopps_softc {
+	device_t		sc_dev;
+	void *			sc_gpio;
+	struct gpio_pinmap	sc_map;
+	int			_map[GPIOPPS_NPINS];
+	struct {
+		char		sc_intrstr[128];
+		void *		sc_ih;
+		int		sc_irqmode;
+	} sc_intrs[GPIOPPS_NPINS];
+	int			sc_assert_val;
+	int			sc_npins;
+	struct pps_state	sc_pps_state;
+	bool			sc_functional;
+	bool			sc_busy;
+};
+
+#define	GPIOPPS_FLAGS_ASSERT_NEG_EDGE	0x01
+#define	GPIOPPS_FLAGS_NO_DOUBLE_EDGE	0x02
+
+static int	gpiopps_match(device_t, cfdata_t, void *);
+static void	gpiopps_attach(device_t, device_t, void *);
+static int	gpiopps_detach(device_t, int);
+
+CFATTACH_DECL_NEW(gpiopps, sizeof(struct gpiopps_softc),
+		  gpiopps_match, gpiopps_attach,
+		  gpiopps_detach, NULL /*activate*/);
+
+extern struct cfdriver gpiopps_cd;
+
+static dev_type_open(gpioppsopen);
+static dev_type_close(gpioppsclose);
+static dev_type_ioctl(gpioppsioctl);
+const struct cdevsw gpiopps_cdevsw = {
+	.d_open = gpioppsopen,
+	.d_close = gpioppsclose,
+	.d_read = noread,
+	.d_write = nowrite,
+	.d_ioctl = gpioppsioctl,
+	.d_stop = nostop,
+	.d_tty = notty,
+	.d_poll = nopoll,
+	.d_mmap = nommap,
+	.d_kqfilter = nokqfilter,
+	.d_discard = nodiscard,
+	.d_flag = D_OTHER
+};
+
+static int
+gpiopps_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct gpio_attach_args *ga = aux;
+	int bits;
+
+	if (strcmp(ga->ga_dvname, cf->cf_name))
+		return (0);
+	
+	if (ga->ga_offset == -1)
+		return (0);
+
+	/* One or 2 pins (unspecified, assume 1) */
+	bits = gpio_npins(ga->ga_mask);
+	if (bits > 2)
+		return (0);
+
+	return (1);
+}
+
+static void
+gpiopps_attach(device_t parent, device_t self, void *aux)
+{
+	struct gpiopps_softc *sc = device_private(self);
+	struct gpio_attach_args *ga = aux;
+	int flags, intrcaps, npins;
+	int assert_edge = GPIO_INTR_POS_EDGE;
+	int clear_edge  = GPIO_INTR_NEG_EDGE;
+	int mask = ga->ga_mask;
+
+	sc->sc_dev = self;
+	sc->sc_assert_val = GPIO_PIN_HIGH;
+
+	/* Map pins */
+	sc->sc_gpio = ga->ga_gpio;
+	sc->sc_map.pm_map = sc->_map;
+
+	/* Determine our pin configuation. */
+	npins = gpio_npins(mask);
+	if (npins == 0) {
+		npins = 1;
+		mask = 0x1;
+	}
+
+	/*
+	 * Here's the different pin configurations we handle:
+	 *
+	 * 1 pin, single-edge capable pin -- interrupt on single-edge,
+	 * only trigger ASSERT signal.
+	 *
+	 * 1 pin, double-edge capable pin -- interrupt on double-edge,
+	 * trigger ASSERT and CLEAR signals, unless 0x2 is set in ga_flags,
+	 * in which case we degrade to ASSERT only.
+	 *
+	 * 2 pins -- pin #0 is ASSERT signal, pin #1 is CLEAR signal.
+	 *
+	 * If 0x1 is set in ga_flags, ASSERT is negative edge, otherwise
+	 * assert is positive edge.
+	 */
+	if (npins < 1 || npins > 2) {
+		aprint_error(": invalid pin configuration\n");
+		return;
+	}
+	if (ga->ga_flags & GPIOPPS_FLAGS_ASSERT_NEG_EDGE) {
+		assert_edge = GPIO_INTR_NEG_EDGE;
+		clear_edge  = GPIO_INTR_POS_EDGE;
+		sc->sc_assert_val = GPIO_PIN_LOW;
+	}
+
+	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, mask,
+			 &sc->sc_map)) {
+		aprint_error(": can't map pins\n");
+		return;
+	}
+	sc->sc_npins = npins;
+
+	aprint_normal("\n");
+
+	if (sc->sc_npins == 2) {
+		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 0);
+		if ((intrcaps & assert_edge) == 0) {
+			aprint_error_dev(sc->sc_dev,
+			    "%s edge interrupt not supported for ASSERT\n",
+			    assert_edge == GPIO_INTR_POS_EDGE ? "positive"
+			    				      : "negative");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		sc->sc_intrs[0].sc_irqmode = assert_edge;
+		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0,
+				   sc->sc_intrs[0].sc_irqmode,
+				   sc->sc_intrs[0].sc_intrstr,
+				   sizeof(sc->sc_intrs[0].sc_intrstr))) {
+			aprint_error_dev(self,
+			    "failed to decode ASSERT interrupt\n");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
+		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
+		    GPIO_PIN_INPUT;
+		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
+			aprint_error_dev(sc->sc_dev,
+			    "ASSERT pin not capable of input\n");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+
+		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 1);
+		if ((intrcaps & clear_edge) == 0) {
+			aprint_error_dev(sc->sc_dev,
+			    "%s edge interrupt not supported for CLEAR\n",
+			    clear_edge == GPIO_INTR_POS_EDGE ? "positive"
+			    				     : "negative");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		sc->sc_intrs[1].sc_irqmode = clear_edge;
+		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0,
+				   sc->sc_intrs[0].sc_irqmode,
+				   sc->sc_intrs[0].sc_intrstr,
+				   sizeof(sc->sc_intrs[1].sc_intrstr))) {
+			aprint_error_dev(self,
+			    "failed to decode CLEAR interrupt\n");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 1);
+		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
+		    GPIO_PIN_INPUT;
+		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 1, flags)) {
+			aprint_error_dev(sc->sc_dev,
+			    "CLEAR pin not capable of input\n");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+
+		aprint_normal_dev(self, "ASSERT interrupting on %s\n",
+				  sc->sc_intrs[0].sc_intrstr);
+		aprint_normal_dev(self, "CLEAR interrupting on %s\n",
+				  sc->sc_intrs[1].sc_intrstr);
+	} else {
+		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 0);
+		bool double_edge = false;
+		if ((intrcaps & GPIO_INTR_DOUBLE_EDGE) &&
+		    (ga->ga_flags & GPIOPPS_FLAGS_NO_DOUBLE_EDGE) == 0) {
+			sc->sc_intrs[0].sc_irqmode = GPIO_INTR_DOUBLE_EDGE;
+			double_edge = true;
+		} else if (intrcaps & assert_edge) {
+			sc->sc_intrs[0].sc_irqmode = assert_edge;
+		} else {
+			aprint_error_dev(sc->sc_dev,
+			    "%s edge interrupt not supported for ASSERT\n",
+			    assert_edge == GPIO_INTR_POS_EDGE ? "positive"
+			    				      : "negative");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0,
+				   sc->sc_intrs[0].sc_irqmode,
+				   sc->sc_intrs[0].sc_intrstr,
+				   sizeof(sc->sc_intrs[0].sc_intrstr))) {
+			aprint_error_dev(self,
+			    "failed to decode interrupt\n");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
+		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
+		    GPIO_PIN_INPUT;
+		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
+			aprint_error_dev(sc->sc_dev,
+			    "ASSERT%s pin not capable of input\n",
+			    double_edge ? "+CLEAR" : "");
+			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+			return;
+		}
+
+		aprint_normal_dev(self, "ASSERT%s interrupting on %s\n",
+				  double_edge ? "+CLEAR" : "",
+				  sc->sc_intrs[0].sc_intrstr);
+	}
+
+	/* Interrupt will be registered when device is opened for use. */
+
+	sc->sc_functional = true;
+}
+
+static int
+gpiopps_assert_intr(void *arg)
+{
+	struct gpiopps_softc *sc = arg;
+
+	mutex_spin_enter(&timecounter_lock);
+	pps_capture(&sc->sc_pps_state);
+	pps_event(&sc->sc_pps_state, PPS_CAPTUREASSERT);
+	mutex_spin_exit(&timecounter_lock);
+
+	return (1);
+}
+
+static int
+gpiopps_clear_intr(void *arg)
+{
+	struct gpiopps_softc *sc = arg;
+
+	mutex_spin_enter(&timecounter_lock);
+	pps_capture(&sc->sc_pps_state);
+	pps_event(&sc->sc_pps_state, PPS_CAPTURECLEAR);
+	mutex_spin_exit(&timecounter_lock);
+
+	return (1);
+}
+
+static int
+gpiopps_double_intr(void *arg)
+{
+	struct gpiopps_softc *sc = arg;
+	int val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0);
+
+	if (val == sc->sc_assert_val)
+		return (gpiopps_assert_intr(arg));
+	return (gpiopps_clear_intr(arg));
+}
+
+static void
+gpiopps_disable_interrupts(struct gpiopps_softc *sc)
+{
+	int i;
+
+	for (i = 0; i < GPIOPPS_NPINS; i++) {
+		if (sc->sc_intrs[i].sc_ih != NULL) {
+			gpio_intr_disestablish(sc->sc_gpio,
+					       sc->sc_intrs[i].sc_ih);
+			sc->sc_intrs[i].sc_ih = NULL;
+		}
+	}
+}
+
+static void
+gpiopps_reset(struct gpiopps_softc *sc)
+{
+	mutex_spin_enter(&timecounter_lock);
+	sc->sc_pps_state.ppsparam.mode = 0;
+	sc->sc_busy = false;
+	mutex_spin_exit(&timecounter_lock);
+}
+
+static int
+gpiopps_detach(device_t self, int flags)
+{
+	struct gpiopps_softc *sc = device_private(self);
+
+	if (!sc->sc_functional) {
+		/* Attach failed, no work to do; resources already released. */
+		return (0);
+	}
+
+	if (sc->sc_busy)
+		return (EBUSY);
+	
+	/*
+	 * Clear the handler and disable the interrupt.
+	 * NOTE: This should never be true, because we
+	 * register the interrupt handler at open, and
+	 * remove it at close.  We keep this as a backstop.
+	 */
+	gpiopps_disable_interrupts(sc);
+
+	/* Release the pin. */
+	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
+
+	return (0);
+}
+
+static int
+gpioppsopen(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+	struct gpiopps_softc *sc;
+	int error = EIO;
+
+	sc = device_lookup_private(&gpiopps_cd, minor(dev));
+	if (sc == NULL)
+		return (ENXIO);
+
+	if (!sc->sc_functional)
+		return (EIO);
+
+	mutex_spin_enter(&timecounter_lock);
+	
+	if (sc->sc_busy) {
+		mutex_spin_exit(&timecounter_lock);
+		return (0);
+	}
+
+	memset(&sc->sc_pps_state, 0, sizeof(sc->sc_pps_state));
+	sc->sc_pps_state.ppscap = PPS_CAPTUREASSERT;
+	if (sc->sc_npins == 2 ||
+	    sc->sc_intrs[0].sc_irqmode == GPIO_INTR_DOUBLE_EDGE)
+	    	sc->sc_pps_state.ppscap |= PPS_CAPTURECLEAR;
+	pps_init(&sc->sc_pps_state);
+	sc->sc_busy = true;
+
+	mutex_spin_exit(&timecounter_lock);
+
+	if (sc->sc_npins == 2) {
+		sc->sc_intrs[0].sc_ih = gpio_intr_establish(sc->sc_gpio,
+		    &sc->sc_map, 0, IPL_VM,
+		    sc->sc_intrs[0].sc_irqmode | GPIO_INTR_MPSAFE,
+		    gpiopps_assert_intr, sc);
+		if (sc->sc_intrs[0].sc_ih == NULL) {
+			aprint_error_dev(sc->sc_dev,
+			    "unable to establish ASSERT interrupt on %s\n",
+			    sc->sc_intrs[0].sc_intrstr);
+			goto out;
+		}
+
+		sc->sc_intrs[1].sc_ih = gpio_intr_establish(sc->sc_gpio,
+		    &sc->sc_map, 1, IPL_VM,
+		    sc->sc_intrs[1].sc_irqmode | GPIO_INTR_MPSAFE,
+		    gpiopps_clear_intr, sc);
+		if (sc->sc_intrs[1].sc_ih == NULL) {
+			aprint_error_dev(sc->sc_dev,
+			    "unable to establish CLEAR interrupt on %s\n",
+			    sc->sc_intrs[0].sc_intrstr);
+			gpio_intr_disestablish(sc->sc_gpio,
+					       sc->sc_intrs[0].sc_ih);
+			goto out;
+		}
+	} else {
+		bool double_edge =
+		    sc->sc_intrs[0].sc_irqmode == GPIO_INTR_DOUBLE_EDGE;
+		sc->sc_intrs[0].sc_ih = gpio_intr_establish(sc->sc_gpio,
+		    &sc->sc_map, 0, IPL_VM,
+		    sc->sc_intrs[0].sc_irqmode | GPIO_INTR_MPSAFE,
+		    double_edge ? gpiopps_double_intr
+				: gpiopps_assert_intr, sc);
+		if (sc->sc_intrs[0].sc_ih == NULL) {
+			aprint_error_dev(sc->sc_dev,
+			    "unable to establish ASSERT%s interrupt on %s\n",
+			    double_edge ? "+CLEAR" : "",
+			    sc->sc_intrs[0].sc_intrstr);
+			goto out;
+		}
+	}
+
+	error = 0;
+
+ out:
+	if (error) {
+		gpiopps_disable_interrupts(sc);
+		gpiopps_reset(sc);
+	}
+	return (error);
+}
+
+static int
+gpioppsclose(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+	struct gpiopps_softc *sc;
+
+	sc = device_lookup_private(&gpiopps_cd, minor(dev));
+
+	gpiopps_disable_interrupts(sc);
+	gpiopps_reset(sc);
+
+	return (0);
+}
+
+static int
+gpioppsioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
+{
+	struct gpiopps_softc *sc;
+	int error = 0;
+
+	sc = device_lookup_private(&gpiopps_cd, minor(dev));
+
+	switch (cmd) {
+	case PPS_IOC_CREATE:
+	case PPS_IOC_DESTROY:
+	case PPS_IOC_GETPARAMS:
+	case PPS_IOC_SETPARAMS:
+	case PPS_IOC_GETCAP:
+	case PPS_IOC_FETCH:
+	case PPS_IOC_KCBIND:
+		mutex_spin_enter(&timecounter_lock);
+		error = pps_ioctl(cmd, data, &sc->sc_pps_state);
+		mutex_spin_exit(&timecounter_lock);
+		break;
+	
+	default:
+		error = EPASSTHROUGH;
+	}
+
+	return (error);
+}
+
+MODULE(MODULE_CLASS_DRIVER, gpiopps, NULL);
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+gpiopps_modcmd(modcmd_t cmd, void *opaque)
+{
+	int error = 0;
+#ifdef _MODULE
+	int bmaj = -1, cmaj = -1;
+#endif
+
+	switch (cmd) {
+	case MODULE_CMD_INIT:
+#ifdef _MODULE
+		error = config_init_component(cfdriver_ioconf_gpiopps,
+		    cfattach_ioconf_gpiopps, cfdata_ioconf_gpiopps);
+		if (error) {
+			aprint_error("%s: unable to init component\n",
+			    gpiopps_cd.cd_name);
+			return (error);
+		}
+
+		error = devsw_attach("gpiopps", NULL, &bmaj,
+		    &gpiopps_cdevsw, &cmaj);
+		if (error) {
+			aprint_error("%s: unable to attach devsw\n",
+			    gpiopps_cd.cd_name);
+			config_fini_component(cfdriver_ioconf_gpiopps,
+			    cfattach_ioconf_gpiopps, cfdata_ioconf_gpiopps);
+		}
+#endif
+		return (error);
+	case MODULE_CMD_FINI:
+#ifdef _MODULE
+		devsw_detach(NULL, &gpiopps_cdevsw);
+		config_fini_component(cfdriver_ioconf_gpiopps,
+		    cfattach_ioconf_gpiopps, cfdata_ioconf_gpiopps);
+#endif
+		return (0);
+	default:
+		return (ENOTTY);
+	}
+}

Reply via email to