summaryrefslogtreecommitdiffstats
path: root/sys/dev/uart/uart_core.c
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2015-08-10 20:08:09 +0000
committerian <ian@FreeBSD.org>2015-08-10 20:08:09 +0000
commit744b707df1834ba1ecc4c3700dfcbda1f0b12efe (patch)
tree84165230748e4a131bbbe7b8e250f358cebeb12a /sys/dev/uart/uart_core.c
parent888240c5585284009d57950853f5b37aa925555f (diff)
downloadFreeBSD-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/uart_core.c')
-rw-r--r--sys/dev/uart/uart_core.c117
1 files changed, 110 insertions, 7 deletions
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)
OpenPOWER on IntegriCloud