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 <[email protected]>
+ *
+ * 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);
+ }
+}