Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/uart/uart_core.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <dev/uart/uart.h> | #include <dev/uart/uart.h> | ||||
#include <dev/uart/uart_bus.h> | #include <dev/uart/uart_bus.h> | ||||
#include <dev/uart/uart_cpu.h> | #include <dev/uart/uart_cpu.h> | ||||
#include <dev/uart/uart_ppstypes.h> | |||||
#include "uart_if.h" | #include "uart_if.h" | ||||
devclass_t uart_devclass; | devclass_t uart_devclass; | ||||
const char uart_driver_name[] = "uart"; | const char uart_driver_name[] = "uart"; | ||||
SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs = | SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs = | ||||
SLIST_HEAD_INITIALIZER(uart_sysdevs); | SLIST_HEAD_INITIALIZER(uart_sysdevs); | ||||
static MALLOC_DEFINE(M_UART, "UART", "UART driver"); | static MALLOC_DEFINE(M_UART, "UART", "UART driver"); | ||||
#ifndef UART_POLL_FREQ | #ifndef UART_POLL_FREQ | ||||
#define UART_POLL_FREQ 50 | #define UART_POLL_FREQ 50 | ||||
#endif | #endif | ||||
static int uart_poll_freq = UART_POLL_FREQ; | static int uart_poll_freq = UART_POLL_FREQ; | ||||
SYSCTL_INT(_debug, OID_AUTO, uart_poll_freq, CTLFLAG_RDTUN, &uart_poll_freq, | SYSCTL_INT(_debug, OID_AUTO, uart_poll_freq, CTLFLAG_RDTUN, &uart_poll_freq, | ||||
0, "UART poll frequency"); | 0, "UART poll frequency"); | ||||
static int uart_force_poll; | static int uart_force_poll; | ||||
SYSCTL_INT(_debug, OID_AUTO, uart_force_poll, CTLFLAG_RDTUN, &uart_force_poll, | SYSCTL_INT(_debug, OID_AUTO, uart_force_poll, CTLFLAG_RDTUN, &uart_force_poll, | ||||
0, "Force UART polling"); | 0, "Force UART polling"); | ||||
#define PPS_MODE_DISABLED 0 | |||||
#define PPS_MODE_CTS 1 | |||||
#define PPS_MODE_DCD 2 | |||||
static inline int | static inline int | ||||
uart_pps_signal(int pps_mode) | uart_pps_mode_valid(int pps_mode) | ||||
{ | { | ||||
int opt; | |||||
switch(pps_mode) { | switch(pps_mode & UART_PPS_SIGNAL_MASK) { | ||||
case PPS_MODE_CTS: | case UART_PPS_DISABLED: | ||||
return (SER_CTS); | case UART_PPS_CTS: | ||||
case PPS_MODE_DCD: | case UART_PPS_DCD: | ||||
return (SER_DCD); | break; | ||||
default: | |||||
return (false); | |||||
} | } | ||||
return (0); | |||||
} | |||||
static inline int | |||||
uart_pps_mode_valid(int pps_mode) | |||||
{ | |||||
switch(pps_mode) { | opt = pps_mode & UART_PPS_OPTION_MASK; | ||||
case PPS_MODE_DISABLED: | if ((opt & ~(UART_PPS_INVERT_PULSE | UART_PPS_NARROW_PULSE)) != 0) | ||||
case PPS_MODE_CTS: | return (false); | ||||
case PPS_MODE_DCD: | |||||
return (true); | return (true); | ||||
} | } | ||||
return (false); | |||||
} | |||||
static const char * | static void | ||||
uart_pps_mode_name(int pps_mode) | uart_pps_print_mode(struct uart_softc *sc) | ||||
{ | { | ||||
switch(pps_mode) { | |||||
case PPS_MODE_DISABLED: | device_printf(sc->sc_dev, "PPS capture mode: "); | ||||
return ("disabled"); | switch(sc->sc_pps_mode) { | ||||
case PPS_MODE_CTS: | case UART_PPS_DISABLED: | ||||
return ("CTS"); | printf("disabled"); | ||||
case PPS_MODE_DCD: | case UART_PPS_CTS: | ||||
return ("DCD"); | printf("CTS"); | ||||
case UART_PPS_DCD: | |||||
printf("DCD"); | |||||
default: | |||||
printf("invalid"); | |||||
} | } | ||||
return ("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 | static int | ||||
uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS) | uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct uart_softc *sc; | struct uart_softc *sc; | ||||
int err, tmp; | int err, tmp; | ||||
sc = arg1; | sc = arg1; | ||||
tmp = sc->sc_pps_mode; | tmp = sc->sc_pps_mode; | ||||
err = sysctl_handle_int(oidp, &tmp, 0, req); | err = sysctl_handle_int(oidp, &tmp, 0, req); | ||||
if (err != 0 || req->newptr == NULL) | if (err != 0 || req->newptr == NULL) | ||||
return (err); | return (err); | ||||
if (!uart_pps_mode_valid(tmp)) | if (!uart_pps_mode_valid(tmp)) | ||||
return (EINVAL); | return (EINVAL); | ||||
sc->sc_pps_mode = tmp; | sc->sc_pps_mode = tmp; | ||||
return(0); | return(0); | ||||
} | } | ||||
static void | 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) | uart_pps_init(struct uart_softc *sc) | ||||
{ | { | ||||
struct sysctl_ctx_list *ctx; | struct sysctl_ctx_list *ctx; | ||||
struct sysctl_oid *tree; | struct sysctl_oid *tree; | ||||
ctx = device_get_sysctl_ctx(sc->sc_dev); | ctx = device_get_sysctl_ctx(sc->sc_dev); | ||||
tree = device_get_sysctl_tree(sc->sc_dev); | tree = device_get_sysctl_tree(sc->sc_dev); | ||||
/* | /* | ||||
* The historical default for pps capture mode is either DCD or CTS, | * The historical default for pps capture mode is either DCD or CTS, | ||||
* depending on the UART_PPS_ON_CTS kernel option. Start with that, | * 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 | * 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 | * devices, then try to fetch the sysctl-tunable that overrides the mode | ||||
* for one specific device. | * for one specific device. | ||||
*/ | */ | ||||
#ifdef UART_PPS_ON_CTS | #ifdef UART_PPS_ON_CTS | ||||
sc->sc_pps_mode = PPS_MODE_CTS; | sc->sc_pps_mode = UART_PPS_CTS; | ||||
#else | #else | ||||
sc->sc_pps_mode = PPS_MODE_DCD; | sc->sc_pps_mode = UART_PPS_DCD; | ||||
#endif | #endif | ||||
TUNABLE_INT_FETCH("hw.uart.pps_mode", &sc->sc_pps_mode); | TUNABLE_INT_FETCH("hw.uart.pps_mode", &sc->sc_pps_mode); | ||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pps_mode", | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pps_mode", | ||||
CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, uart_pps_mode_sysctl, "I", | 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)) { | if (!uart_pps_mode_valid(sc->sc_pps_mode)) { | ||||
device_printf(sc->sc_dev, | 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); | ||||
sc->sc_pps_mode = PPS_MODE_DISABLED; | sc->sc_pps_mode = UART_PPS_DISABLED; | ||||
} else if (bootverbose) { | } else if (bootverbose) { | ||||
device_printf(sc->sc_dev, "PPS capture mode %d (%s)\n", | uart_pps_print_mode(sc); | ||||
sc->sc_pps_mode, uart_pps_mode_name(sc->sc_pps_mode)); | |||||
} | } | ||||
sc->sc_pps.ppscap = PPS_CAPTUREBOTH; | sc->sc_pps.ppscap = PPS_CAPTUREBOTH; | ||||
sc->sc_pps.driver_mtx = uart_tty_getlock(sc); | sc->sc_pps.driver_mtx = uart_tty_getlock(sc); | ||||
sc->sc_pps.driver_abi = PPS_ABI_VERSION; | sc->sc_pps.driver_abi = PPS_ABI_VERSION; | ||||
pps_init_abi(&sc->sc_pps); | pps_init_abi(&sc->sc_pps); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 133 Lines • ▼ Show 20 Lines | |||||
* processing. Note that we merge the delta bits, but set the state | * processing. Note that we merge the delta bits, but set the state | ||||
* bits. This is to avoid losing state transitions due to having more | * bits. This is to avoid losing state transitions due to having more | ||||
* than 1 hardware interrupt between software interrupts. | * than 1 hardware interrupt between software interrupts. | ||||
*/ | */ | ||||
static __inline int | static __inline int | ||||
uart_intr_sigchg(void *arg) | uart_intr_sigchg(void *arg) | ||||
{ | { | ||||
struct uart_softc *sc = arg; | struct uart_softc *sc = arg; | ||||
int new, old, pps_sig, sig; | int new, old, sig; | ||||
sig = UART_GETSIG(sc); | sig = UART_GETSIG(sc); | ||||
/* | /* | ||||
* Time pulse counting support. Note that both CTS and DCD are | * Time pulse counting support, invoked whenever the PPS parameters are | ||||
* active-low signals. The status bit is high to indicate that | * currently set to capture either edge of the signal. | ||||
* the signal on the line is low, which corresponds to a PPS | |||||
* clear event. | |||||
*/ | */ | ||||
if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) { | if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) { | ||||
pps_sig = uart_pps_signal(sc->sc_pps_mode); | uart_pps_process(sc, sig); | ||||
if (sig & SER_DELTA(pps_sig)) { | |||||
pps_capture(&sc->sc_pps); | |||||
pps_event(&sc->sc_pps, (sig & pps_sig) ? | |||||
PPS_CAPTURECLEAR : PPS_CAPTUREASSERT); | |||||
} | |||||
} | } | ||||
/* | /* | ||||
* Keep track of signal changes, even when the device is not | * Keep track of signal changes, even when the device is not | ||||
* opened. This allows us to inform upper layers about a | * opened. This allows us to inform upper layers about a | ||||
* possible loss of DCD and thus the existence of a (possibly) | * possible loss of DCD and thus the existence of a (possibly) | ||||
* different connection when we have DCD back, during the time | * different connection when we have DCD back, during the time | ||||
* that the device was closed. | * that the device was closed. | ||||
▲ Show 20 Lines • Show All 429 Lines • Show Last 20 Lines |