diff options
author | ian <ian@FreeBSD.org> | 2015-08-10 20:08:09 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2015-08-10 20:08:09 +0000 |
commit | 744b707df1834ba1ecc4c3700dfcbda1f0b12efe (patch) | |
tree | 84165230748e4a131bbbe7b8e250f358cebeb12a /sys/dev/uart | |
parent | 888240c5585284009d57950853f5b37aa925555f (diff) | |
download | FreeBSD-src-744b707df1834ba1ecc4c3700dfcbda1f0b12efe.zip FreeBSD-src-744b707df1834ba1ecc4c3700dfcbda1f0b12efe.tar.gz |
Allow the choice of PPS signal captured by uart(4) to be runtime-configured,
eliminating the need to build a custom kernel to use the CTS signal.
The historical UART_PPS_ON_CTS kernel option is still honored, but now it
can be overridden at runtime using a tunable to configure all uart devices
(hw.uart.pps_mode) or specific devices (dev.uart.#.pps_mode). The per-
device config is both a tunable and a writable sysctl.
This syncs the PPS capabilities of uart(4) with the enhancements recently
recently added to ucom(4) for capturing from USB serial devices.
Relnotes: yes
Diffstat (limited to 'sys/dev/uart')
-rw-r--r-- | sys/dev/uart/uart_bus.h | 9 | ||||
-rw-r--r-- | sys/dev/uart/uart_core.c | 117 |
2 files changed, 111 insertions, 15 deletions
diff --git a/sys/dev/uart/uart_bus.h b/sys/dev/uart/uart_bus.h index e42c4f5..11d599e67 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 @@ -120,6 +112,7 @@ struct uart_softc { /* Pulse capturing support (PPS). */ struct pps_state sc_pps; + int sc_pps_mode; /* Upper layer data. */ void *sc_softih; diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c index a2895b1..d83c31c 100644 --- a/sys/dev/uart/uart_core.c +++ b/sys/dev/uart/uart_core.c @@ -70,6 +70,111 @@ 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) +{ + + 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) { @@ -211,14 +316,15 @@ 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); 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_event(&sc->sc_pps, (sig & pps_sig) ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } @@ -571,10 +677,7 @@ uart_bus_attach(device_t dev) } else { if ((error = uart_tty_attach(sc)) != 0) goto fail; - 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); + uart_pps_init(sc); } if (sc->sc_sysdev != NULL) |