diff options
-rw-r--r-- | UPDATING | 14 | ||||
-rw-r--r-- | share/man/man4/uart.4 | 35 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus.h | 10 | ||||
-rw-r--r-- | sys/dev/uart/uart_core.c | 133 | ||||
-rw-r--r-- | sys/dev/uart/uart_tty.c | 10 |
5 files changed, 182 insertions, 20 deletions
@@ -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); +} |