svn commit: r293781 - in head: share/man/man4 sys/dev/uart

Ian Lepore ian at FreeBSD.org
Tue Jan 12 18:42:02 UTC 2016


Author: ian
Date: Tue Jan 12 18:42:00 2016
New Revision: 293781
URL: https://svnweb.freebsd.org/changeset/base/293781

Log:
  Restore uart PPS signal capture polarity to its historical norm, and add an
  option to invert the polarity in software. Also add an option to capture
  very narrow pulses by using the hardware's MSR delta-bit capability of
  latching line state changes.
  
  This effectively reverts the mistake I made in r286595 which was based on
  empirical measurements made on hardware using TTL-level signaling, in which
  the logic levels are inverted from RS-232. Thus, this re-syncs the polarity
  with the requirements of RFC 2783, which is writen in terms of RS-232
  signaling.
  
  Narrow-pulse mode uses the ability of most ns8250 and similar chips to
  provide a delta indication in the modem status register. The hardware is
  able to notice and latch the change when the pulse width is shorter than
  interrupt latency, which results in the signal no longer being asserted by
  time the interrupt service code runs. When running in this mode we get
  notified only that "a pulse happened" so the driver synthesizes both an
  ASSERT and a CLEAR event (with the same timestamp for each). When the pulse
  width is about equal to the interrupt latency the driver may intermittantly
  see both edges of the pulse. To prevent generating spurious events, the
  driver implements a half-second lockout period after generating an event
  before it will generate another.
  
  Differential Revision:	https://reviews.freebsd.org/D4477

Added:
  head/sys/dev/uart/uart_ppstypes.h   (contents, props changed)
Modified:
  head/share/man/man4/uart.4
  head/sys/dev/uart/uart_bus.h
  head/sys/dev/uart/uart_core.c
  head/sys/dev/uart/uart_dev_ns8250.c

Modified: head/share/man/man4/uart.4
==============================================================================
--- head/share/man/man4/uart.4	Tue Jan 12 18:33:53 2016	(r293780)
+++ head/share/man/man4/uart.4	Tue Jan 12 18:42:00 2016	(r293781)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd August 10, 2015
+.Dd December 9, 2015
 .Dt UART 4
 .Os
 .Sh NAME
@@ -160,7 +160,9 @@ The API, accessed via
 is available on the tty device.
 To use the PPS capture feature with
 .Xr ntpd 8 ,
-symlink the tty device to
+symlink the tty callout device
+.Va /dev/cuau?
+to
 .Va /dev/pps0.
 .Pp
 The
@@ -175,15 +177,54 @@ it can be set in
 .Xr loader.conf 5
 or
 .Xr sysctl.conf 5 .
+.Pp
 The following capture modes are available:
 .Bl -tag -compact -offset "mmmm" -width "mmmm"
-.It 0
+.It 0x00
 Capture disabled.
-.It 1
+.It 0x01
 Capture pulses on the CTS line.
-.It 2
-Capture pulses on the DCD line (default).
+.It 0x02
+Capture pulses on the DCD line.
 .El
+.Pp
+The following values may be ORed with the capture mode to configure 
+capture processing options:
+.Bl -tag -compact -offset "mmmm" -width "mmmm"
+.It 0x10
+Invert the pulse (RS-232 logic low = ASSERT, high = CLEAR).
+.It 0x20
+Attempt to capture narrow pulses.
+.El
+.Pp
+Add the narrow pulse option when the incoming PPS pulse width is small
+enough to prevent reliable capture in normal mode.
+In narrow mode the driver uses the hardware's ability to latch a line
+state change; not all hardware has this capability.
+The hardware latch provides a reliable indication that a pulse occurred,
+but prevents distinguishing between the CLEAR and ASSERT edges of the pulse.
+For each detected pulse, the driver synthesizes both an ASSERT and a CLEAR
+event, using the same timestamp for each.
+To prevent spurious events when the hardware is intermittently able to
+see both edges of a pulse, the driver will not generate a new pair of
+events within a half second of the prior pair.
+Both normal and narrow pulse modes work with
+.Xr ntpd 8 .
+.Pp
+Add the invert option when the connection to the uart device uses TTL
+level signals, or when the PPS source emits inverted pulses.
+RFC 2783 defines an ASSERT event as a higher-voltage line level, and a CLEAR
+event as a lower-voltage line level, in the context of the RS-232 protocol.
+The modem control signals on a TTL-level connection are typically
+inverted from the RS-232 levels.
+For example, carrier presence is indicated by a high signal on an RS-232
+DCD line, and by a low signal on a TTL DCD line.
+This is due to the use of inverting line driver buffers to convert between
+TTL and RS-232 line levels in most hardware designs.
+Generally speaking, a connection to a DB-9 style connector is an RS-232
+level signal at up to 12 volts.
+A connection to header pins or an edge-connector on an embedded board
+is typically a TTL signal at 3.3 or 5 volts.
 .Sh FILES
 .Bl -tag -width ".Pa /dev/ttyu?.init" -compact
 .It Pa /dev/ttyu?

Modified: head/sys/dev/uart/uart_bus.h
==============================================================================
--- head/sys/dev/uart/uart_bus.h	Tue Jan 12 18:33:53 2016	(r293780)
+++ head/sys/dev/uart/uart_bus.h	Tue Jan 12 18:42:00 2016	(r293781)
@@ -113,6 +113,7 @@ struct uart_softc {
 	/* Pulse capturing support (PPS). */
 	struct pps_state sc_pps;
 	int		 sc_pps_mode;
+	sbintime_t	 sc_pps_captime;
 
 	/* Upper layer data. */
 	void		*sc_softih;

Modified: head/sys/dev/uart/uart_core.c
==============================================================================
--- head/sys/dev/uart/uart_core.c	Tue Jan 12 18:33:53 2016	(r293780)
+++ head/sys/dev/uart/uart_core.c	Tue Jan 12 18:42:00 2016	(r293781)
@@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/uart/uart.h>
 #include <dev/uart/uart_bus.h>
 #include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_ppstypes.h>
 
 #include "uart_if.h"
 
@@ -70,47 +71,47 @@ static int uart_force_poll;
 SYSCTL_INT(_debug, OID_AUTO, uart_force_poll, CTLFLAG_RDTUN, &uart_force_poll,
     0, "Force UART polling");
 
-#define	PPS_MODE_DISABLED	0
-#define	PPS_MODE_CTS		1
-#define	PPS_MODE_DCD		2
-
 static inline int
-uart_pps_signal(int pps_mode)
+uart_pps_mode_valid(int pps_mode)
 {
+	int opt;
 
-	switch(pps_mode) {
-	case PPS_MODE_CTS:
-		return (SER_CTS);
-	case PPS_MODE_DCD:
-		return (SER_DCD);
-	}
-	return (0);
+	switch(pps_mode & UART_PPS_SIGNAL_MASK) {
+	case UART_PPS_DISABLED:
+	case UART_PPS_CTS:
+	case UART_PPS_DCD:
+		break;
+	default:
+		return (false);
+	}
+
+	opt = pps_mode & UART_PPS_OPTION_MASK;
+	if ((opt & ~(UART_PPS_INVERT_PULSE | UART_PPS_NARROW_PULSE)) != 0)
+		return (false);
+
+	return (true);
 }
-static inline int
-uart_pps_mode_valid(int pps_mode)
+
+static void
+uart_pps_print_mode(struct uart_softc *sc)
 {
 
-	switch(pps_mode) {
-	case PPS_MODE_DISABLED:
-	case PPS_MODE_CTS:
-	case PPS_MODE_DCD:
-		return (true);
-	}
-	return (false);
-}
-
-static const char *
-uart_pps_mode_name(int pps_mode)
-{
-	switch(pps_mode) {
-	case PPS_MODE_DISABLED:
-		return ("disabled");
-	case PPS_MODE_CTS:
-		return ("CTS");
-	case PPS_MODE_DCD:
-		return ("DCD");
-	}
-	return ("invalid");
+	device_printf(sc->sc_dev, "PPS capture mode: ");
+	switch(sc->sc_pps_mode) {
+	case UART_PPS_DISABLED:
+		printf("disabled");
+	case UART_PPS_CTS:
+		printf("CTS");
+	case UART_PPS_DCD:
+		printf("DCD");
+	default:
+		printf("invalid");
+	}
+	if (sc->sc_pps_mode & UART_PPS_INVERT_PULSE)
+		printf("-Inverted");
+	if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE)
+		printf("-NarrowPulse");
+	printf("\n");
 }
 
 static int
@@ -131,6 +132,55 @@ uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS
 }
 
 static void
+uart_pps_process(struct uart_softc *sc, int ser_sig)
+{
+	sbintime_t now;
+	int is_assert, pps_sig;
+
+	/* Which signal is configured as PPS?  Early out if none. */
+	switch(sc->sc_pps_mode & UART_PPS_SIGNAL_MASK) {
+	case UART_PPS_CTS:
+		pps_sig = SER_CTS;
+		break;
+	case UART_PPS_DCD:
+		pps_sig = SER_DCD;
+		break;
+	default:
+		return;
+	}
+
+	/* Early out if there is no change in the signal configured as PPS. */
+	if ((ser_sig & SER_DELTA(pps_sig)) == 0)
+		return;
+
+	/*
+	 * In narrow-pulse mode we need to synthesize both capture and clear
+	 * events from a single "delta occurred" indication from the uart
+	 * hardware because the pulse width is too narrow to reliably detect
+	 * both edges.  However, when the pulse width is close to our interrupt
+	 * processing latency we might intermittantly catch both edges.  To
+	 * guard against generating spurious events when that happens, we use a
+	 * separate timer to ensure at least half a second elapses before we
+	 * generate another event.
+	 */
+	pps_capture(&sc->sc_pps);
+	if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE) {
+		now = getsbinuptime();
+		if (now > sc->sc_pps_captime + 500 * SBT_1MS) {
+			sc->sc_pps_captime = now;
+			pps_event(&sc->sc_pps, PPS_CAPTUREASSERT);
+			pps_event(&sc->sc_pps, PPS_CAPTURECLEAR);
+		}
+	} else  {
+		is_assert = ser_sig & pps_sig;
+		if (sc->sc_pps_mode & UART_PPS_INVERT_PULSE)
+			is_assert = !is_assert;
+		pps_event(&sc->sc_pps, is_assert ? PPS_CAPTUREASSERT :
+		    PPS_CAPTURECLEAR);
+	}
+}
+
+static void
 uart_pps_init(struct uart_softc *sc)
 {
 	struct sysctl_ctx_list *ctx;
@@ -147,23 +197,23 @@ uart_pps_init(struct uart_softc *sc)
 	 * for one specific device.
 	 */
 #ifdef UART_PPS_ON_CTS
-	sc->sc_pps_mode = PPS_MODE_CTS;
+	sc->sc_pps_mode = UART_PPS_CTS;
 #else
-	sc->sc_pps_mode = PPS_MODE_DCD;
+	sc->sc_pps_mode = UART_PPS_DCD;
 #endif
 	TUNABLE_INT_FETCH("hw.uart.pps_mode", &sc->sc_pps_mode);
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pps_mode",
 	    CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, uart_pps_mode_sysctl, "I",
-	    "pulse capturing mode - 0/1/2 - disabled/CTS/DCD");
+	    "pulse mode: 0/1/2=disabled/CTS/DCD; "
+	    "add 0x10 to invert, 0x20 for narrow pulse");
 
 	if (!uart_pps_mode_valid(sc->sc_pps_mode)) {
 		device_printf(sc->sc_dev, 
-		    "Invalid pps_mode %d configured; disabling PPS capture\n",
+		    "Invalid pps_mode 0x%02x configured; disabling PPS capture\n",
 		    sc->sc_pps_mode);
-		sc->sc_pps_mode = PPS_MODE_DISABLED;
+		sc->sc_pps_mode = UART_PPS_DISABLED;
 	} else if (bootverbose) {
-		device_printf(sc->sc_dev, "PPS capture mode %d (%s)\n",
-		    sc->sc_pps_mode, uart_pps_mode_name(sc->sc_pps_mode));
+		uart_pps_print_mode(sc);
 	}
 
 	sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
@@ -313,23 +363,16 @@ static __inline int
 uart_intr_sigchg(void *arg)
 {
 	struct uart_softc *sc = arg;
-	int new, old, pps_sig, sig;
+	int new, old, sig;
 
 	sig = UART_GETSIG(sc);
 
 	/*
-	 * Time pulse counting support. Note that both CTS and DCD are
-	 * active-low signals. The status bit is high to indicate that
-	 * the signal on the line is low, which corresponds to a PPS
-	 * clear event.
+	 * Time pulse counting support, invoked whenever the PPS parameters are
+	 * currently set to capture either edge of the signal.
 	 */
 	if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) {
-		pps_sig = uart_pps_signal(sc->sc_pps_mode);
-		if (sig & SER_DELTA(pps_sig)) {
-			pps_capture(&sc->sc_pps);
-			pps_event(&sc->sc_pps, (sig & pps_sig) ?
-			    PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
-		}
+		uart_pps_process(sc, sig);
 	}
 
 	/*

Modified: head/sys/dev/uart/uart_dev_ns8250.c
==============================================================================
--- head/sys/dev/uart/uart_dev_ns8250.c	Tue Jan 12 18:33:53 2016	(r293780)
+++ head/sys/dev/uart/uart_dev_ns8250.c	Tue Jan 12 18:42:00 2016	(r293781)
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
 #endif
 #include <dev/uart/uart_bus.h>
 #include <dev/uart/uart_dev_ns8250.h>
+#include <dev/uart/uart_ppstypes.h>
 
 #include <dev/ic/ns16550.h>
 
@@ -401,11 +402,40 @@ static struct ofw_compat_data compat_dat
 UART_FDT_CLASS_AND_DEVICE(compat_data);
 #endif
 
-#define	SIGCHG(c, i, s, d)				\
-	if (c) {					\
-		i |= (i & s) ? s : s | d;		\
-	} else {					\
-		i = (i & s) ? (i & ~s) | d : i;		\
+/* Use token-pasting to form SER_ and MSR_ named constants. */
+#define	SER(sig)	SER_##sig
+#define	SERD(sig)	SER_D##sig
+#define	MSR(sig)	MSR_##sig
+#define	MSRD(sig)	MSR_D##sig
+
+/*
+ * Detect signal changes using software delta detection.  The previous state of
+ * the signals is in 'var' the new hardware state is in 'msr', and 'sig' is the
+ * short name (DCD, CTS, etc) of the signal bit being processed; 'var' gets the
+ * new state of both the signal and the delta bits.
+ */
+#define SIGCHGSW(var, msr, sig)					\
+	if ((msr) & MSR(sig)) {					\
+		if ((var & SER(sig)) == 0)			\
+			var |= SERD(sig) | SER(sig);		\
+	} else {						\
+		if ((var & SER(sig)) != 0)			\
+			var = SERD(sig) | (var & ~SER(sig));	\
+	}
+
+/*
+ * Detect signal changes using the hardware msr delta bits.  This is currently
+ * used only when PPS timing information is being captured using the "narrow
+ * pulse" option.  With a narrow PPS pulse the signal may not still be asserted
+ * by time the interrupt handler is invoked.  The hardware will latch the fact
+ * that it changed in the delta bits.
+ */
+#define SIGCHGHW(var, msr, sig)					\
+	if ((msr) & MSRD(sig)) {				\
+		if (((msr) & MSR(sig)) != 0)			\
+			var |= SERD(sig) | SER(sig);		\
+		else						\
+			var = SERD(sig) | (var & ~SER(sig));	\
 	}
 
 int
@@ -532,21 +562,37 @@ ns8250_bus_flush(struct uart_softc *sc, 
 int
 ns8250_bus_getsig(struct uart_softc *sc)
 {
-	uint32_t new, old, sig;
+	uint32_t old, sig;
 	uint8_t msr;
 
+	/*
+	 * The delta bits are reputed to be broken on some hardware, so use
+	 * software delta detection by default.  Use the hardware delta bits
+	 * when capturing PPS pulses which are too narrow for software detection
+	 * to see the edges.  Hardware delta for RI doesn't work like the
+	 * others, so always use software for it.  Other threads may be changing
+	 * other (non-MSR) bits in sc_hwsig, so loop until it can succesfully
+	 * update without other changes happening.  Note that the SIGCHGxx()
+	 * macros carefully preserve the delta bits when we have to loop several
+	 * times and a signal transitions between iterations.
+	 */
 	do {
 		old = sc->sc_hwsig;
 		sig = old;
 		uart_lock(sc->sc_hwmtx);
 		msr = uart_getreg(&sc->sc_bas, REG_MSR);
 		uart_unlock(sc->sc_hwmtx);
-		SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR);
-		SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS);
-		SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD);
-		SIGCHG(msr & MSR_RI,  sig, SER_RI,  SER_DRI);
-		new = sig & ~SER_MASK_DELTA;
-	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
+		if (sc->sc_pps_mode & UART_PPS_NARROW_PULSE) {
+			SIGCHGHW(sig, msr, DSR);
+			SIGCHGHW(sig, msr, CTS);
+			SIGCHGHW(sig, msr, DCD);
+		} else {
+			SIGCHGSW(sig, msr, DSR);
+			SIGCHGSW(sig, msr, CTS);
+			SIGCHGSW(sig, msr, DCD);
+		}
+		SIGCHGSW(sig, msr, RI);
+	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, sig & ~SER_MASK_DELTA));
 	return (sig);
 }
 
@@ -900,12 +946,10 @@ ns8250_bus_setsig(struct uart_softc *sc,
 		old = sc->sc_hwsig;
 		new = old;
 		if (sig & SER_DDTR) {
-			SIGCHG(sig & SER_DTR, new, SER_DTR,
-			    SER_DDTR);
+			new = (new & ~SER_DTR) | (sig & (SER_DTR | SER_DDTR));
 		}
 		if (sig & SER_DRTS) {
-			SIGCHG(sig & SER_RTS, new, SER_RTS,
-			    SER_DRTS);
+			new = (new & ~SER_RTS) | (sig & (SER_RTS | SER_DRTS));
 		}
 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
 	uart_lock(sc->sc_hwmtx);

Added: head/sys/dev/uart/uart_ppstypes.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/uart/uart_ppstypes.h	Tue Jan 12 18:42:00 2016	(r293781)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2015 	Ian Lepore
+ * All rights reserved.
+ *
+ * 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 ``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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_UART_PPSTYPES_H_
+#define _DEV_UART_PPSTYPES_H_
+
+/*
+ * These constants are shared by several drivers including uart and usb_serial.
+ */
+
+#define	UART_PPS_SIGNAL_MASK	0x0f
+#define	UART_PPS_OPTION_MASK	0xf0
+
+#define	UART_PPS_DISABLED	0x00
+#define	UART_PPS_CTS		0x01
+#define	UART_PPS_DCD		0x02
+
+#define	UART_PPS_INVERT_PULSE	0x10
+#define	UART_PPS_NARROW_PULSE	0x20
+
+#endif /* _DEV_UART_PPSTYPES_H_ */


More information about the svn-src-head mailing list