summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--UPDATING14
-rw-r--r--share/man/man4/uart.435
-rw-r--r--sys/dev/uart/uart_bus.h10
-rw-r--r--sys/dev/uart/uart_core.c133
-rw-r--r--sys/dev/uart/uart_tty.c10
5 files changed, 182 insertions, 20 deletions
diff --git a/UPDATING b/UPDATING
index 3263818..5f0b398 100644
--- a/UPDATING
+++ b/UPDATING
@@ -16,6 +16,20 @@ from older versions of FreeBSD, try WITHOUT_CLANG to bootstrap to the tip of
stable/10, and then rebuild without this option. The bootstrap process from
older version of current is a bit fragile.
+20150823:
+ The polarity of Pulse Per Second (PPS) capture events with the
+ uart(4) driver has been corrected. Prior to this change the PPS
+ "assert" event corresponded to the trailing edge of a positive PPS
+ pulse and the "clear" event was the leading edge of the next pulse.
+
+ As the width of a PPS pulse in a typical GPS receiver is on the
+ order of 1 millisecond, most users will not notice any significant
+ difference with this change.
+
+ Anyone who has compensated for the historical polarity reversal by
+ configuring a negative offset equal to the pulse width will need to
+ remove that workaround.
+
20150822:
From legacy ata(4) driver was removed support for SATA controllers
supported by more functional drivers ahci(4), siis(4) and mvs(4).
diff --git a/share/man/man4/uart.4 b/share/man/man4/uart.4
index 644c37d..d520409 100644
--- a/share/man/man4/uart.4
+++ b/share/man/man4/uart.4
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 12, 2008
+.Dd August 10, 2015
.Dt UART 4
.Os
.Sh NAME
@@ -151,6 +151,39 @@ SCC: serial communications controllers supported by the
device driver.
.El
.\"
+.Sh Pulse Per Second (PPS) Timing Interface
+The
+.Nm
+driver can capture PPS timing information as defined in RFC 2783.
+The API, accessed via
+.Xr ioctl 8 ,
+is available on the tty device.
+To use the PPS capture feature with
+.Xr ntpd 8 ,
+symlink the tty device to
+.Va /dev/pps0.
+.Pp
+The
+.Va hw.uart.pps_mode
+tunable configures the PPS capture mode for all uart devices;
+it can be set in
+.Xr loader.conf 5 .
+The
+.Va dev.uart.0.pps_mode
+sysctl configures the PPS capture mode for a specific uart device;
+it can be set in
+.Xr loader.conf 5
+or
+.Xr sysctl.conf 5 .
+The following capture modes are available:
+.Bl -tag -compact -offset "mmmm" -width "mmmm"
+.It 0
+Capture disabled.
+.It 1
+Capture pulses on the CTS line.
+.It 2
+Capture pulses on the DCD line (default).
+.El
.Sh FILES
.Bl -tag -width ".Pa /dev/ttyu?.init" -compact
.It Pa /dev/ttyu?
diff --git a/sys/dev/uart/uart_bus.h b/sys/dev/uart/uart_bus.h
index c455bb9..4a2ab91 100644
--- a/sys/dev/uart/uart_bus.h
+++ b/sys/dev/uart/uart_bus.h
@@ -48,14 +48,6 @@
#define UART_STAT_OVERRUN 0x0400
#define UART_STAT_PARERR 0x0800
-#ifdef UART_PPS_ON_CTS
-#define UART_SIG_DPPS SER_DCTS
-#define UART_SIG_PPS SER_CTS
-#else
-#define UART_SIG_DPPS SER_DDCD
-#define UART_SIG_PPS SER_DCD
-#endif
-
/* UART_IOCTL() requests */
#define UART_IOCTL_BREAK 1
#define UART_IOCTL_IFLOW 2
@@ -119,6 +111,7 @@ struct uart_softc {
/* Pulse capturing support (PPS). */
struct pps_state sc_pps;
+ int sc_pps_mode;
/* Upper layer data. */
void *sc_softih;
@@ -149,6 +142,7 @@ void uart_sched_softih(struct uart_softc *, uint32_t);
int uart_tty_attach(struct uart_softc *);
int uart_tty_detach(struct uart_softc *);
+struct mtx *uart_tty_getlock(struct uart_softc *);
void uart_tty_intr(void *arg);
/*
diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c
index 76b6a78..3da37e0 100644
--- a/sys/dev/uart/uart_core.c
+++ b/sys/dev/uart/uart_core.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/reboot.h>
+#include <sys/sysctl.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
@@ -64,6 +65,108 @@ static MALLOC_DEFINE(M_UART, "UART", "UART driver");
static int uart_poll_freq = UART_POLL_FREQ;
TUNABLE_INT("debug.uart_poll_freq", &uart_poll_freq);
+#define PPS_MODE_DISABLED 0
+#define PPS_MODE_CTS 1
+#define PPS_MODE_DCD 2
+
+static inline int
+uart_pps_signal(int pps_mode)
+{
+
+ switch(pps_mode) {
+ case PPS_MODE_CTS:
+ return (SER_CTS);
+ case PPS_MODE_DCD:
+ return (SER_DCD);
+ }
+ return (0);
+}
+static inline int
+uart_pps_mode_valid(int pps_mode)
+{
+
+ 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");
+}
+
+static int
+uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct uart_softc *sc;
+ int err, tmp;
+
+ sc = arg1;
+ tmp = sc->sc_pps_mode;
+ err = sysctl_handle_int(oidp, &tmp, 0, req);
+ if (err != 0 || req->newptr == NULL)
+ return (err);
+ if (!uart_pps_mode_valid(tmp))
+ return (EINVAL);
+ sc->sc_pps_mode = tmp;
+ return(0);
+}
+
+static void
+uart_pps_init(struct uart_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree = device_get_sysctl_tree(sc->sc_dev);
+
+ /*
+ * The historical default for pps capture mode is either DCD or CTS,
+ * depending on the UART_PPS_ON_CTS kernel option. Start with that,
+ * then try to fetch the tunable that overrides the mode for all uart
+ * devices, then try to fetch the sysctl-tunable that overrides the mode
+ * for one specific device.
+ */
+#ifdef UART_PPS_ON_CTS
+ sc->sc_pps_mode = PPS_MODE_CTS;
+#else
+ sc->sc_pps_mode = PPS_MODE_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");
+
+ if (!uart_pps_mode_valid(sc->sc_pps_mode)) {
+ device_printf(sc->sc_dev,
+ "Invalid pps_mode %d configured; disabling PPS capture\n",
+ sc->sc_pps_mode);
+ sc->sc_pps_mode = PPS_MODE_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));
+ }
+
+ sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
+ sc->sc_pps.driver_mtx = uart_tty_getlock(sc);
+ sc->sc_pps.driver_abi = PPS_ABI_VERSION;
+ pps_init_abi(&sc->sc_pps);
+}
+
void
uart_add_sysdev(struct uart_devinfo *di)
{
@@ -199,15 +302,22 @@ static __inline int
uart_intr_sigchg(void *arg)
{
struct uart_softc *sc = arg;
- int new, old, sig;
+ int new, old, pps_sig, 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.
+ */
if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) {
- if (sig & UART_SIG_DPPS) {
+ 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 & UART_SIG_PPS) ?
- PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
+ pps_event(&sc->sc_pps, (sig & pps_sig) ?
+ PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
}
}
@@ -499,9 +609,6 @@ uart_bus_attach(device_t dev)
sc->sc_sysdev->stopbits);
}
- sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
- pps_init(&sc->sc_pps);
-
sc->sc_leaving = 0;
sc->sc_testintr = 1;
filt = uart_intr(sc);
@@ -554,10 +661,14 @@ uart_bus_attach(device_t dev)
printf("\n");
}
- error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL)
- ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc);
- if (error)
- goto fail;
+ if (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL) {
+ if ((error = sc->sc_sysdev->attach(sc)) != 0)
+ goto fail;
+ } else {
+ if ((error = uart_tty_attach(sc)) != 0)
+ goto fail;
+ uart_pps_init(sc);
+ }
if (sc->sc_sysdev != NULL)
sc->sc_sysdev->hwmtx = sc->sc_hwmtx;
diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c
index 56f879b..e676d50 100644
--- a/sys/dev/uart/uart_tty.c
+++ b/sys/dev/uart/uart_tty.c
@@ -410,3 +410,13 @@ uart_tty_detach(struct uart_softc *sc)
return (0);
}
+
+struct mtx *
+uart_tty_getlock(struct uart_softc *sc)
+{
+
+ if (sc->sc_u.u_tty.tp != NULL)
+ return (tty_getlock(sc->sc_u.u_tty.tp));
+ else
+ return (NULL);
+}
OpenPOWER on IntegriCloud