summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorwrstuden <wrstuden@NetBSD.org>1999-03-27 01:22:36 +0000
committerwrstuden <wrstuden@NetBSD.org>1999-03-27 01:22:36 +0000
commitdf93e67c02495f2868cb7bc05338a29337dfa6d4 (patch)
tree1919313ef94a7bf74e449e028fc9c984d274b1d5 /sys/dev
parent2adccc50fab242fb4991baf36264e7151b5acc52 (diff)
PPS support. Copied off of com.c support, with changes as DCD interupts
are only enabled if needed and if ok'd by MD layer.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ic/z8530sc.h4
-rw-r--r--sys/dev/ic/z8530tty.c265
2 files changed, 254 insertions, 15 deletions
diff --git a/sys/dev/ic/z8530sc.h b/sys/dev/ic/z8530sc.h
index 783fee5ccc1..73a6fce4f26 100644
--- a/sys/dev/ic/z8530sc.h
+++ b/sys/dev/ic/z8530sc.h
@@ -1,4 +1,4 @@
-/* $NetBSD: z8530sc.h,v 1.11 1999/02/03 20:22:28 mycroft Exp $ */
+/* $NetBSD: z8530sc.h,v 1.12 1999/03/27 01:22:36 wrstuden Exp $ */
/*
* Copyright (c) 1994 Gordon W. Ross
@@ -104,6 +104,7 @@ struct zs_chanstate {
u_char cs_rr0_mask; /* rr0 bits that stop output */
u_char cs_rr0_dcd; /* which bit to read as DCD */
u_char cs_rr0_cts; /* which bit to read as CTS */
+ u_char cs_rr0_pps; /* which bit to use for PPS */
/* the above is set only while CRTSCTS is enabled. */
u_char cs_wr5_dtr; /* which bit to write as DTR */
@@ -112,7 +113,6 @@ struct zs_chanstate {
char cs_softreq; /* need soft interrupt call */
char cs_spare1; /* (for skippy :) */
- char cs_spare2; /* (for skippy :) */
/* MD code might define a larger variant of this. */
};
diff --git a/sys/dev/ic/z8530tty.c b/sys/dev/ic/z8530tty.c
index bdae4b8e98e..cf6eb3f14f7 100644
--- a/sys/dev/ic/z8530tty.c
+++ b/sys/dev/ic/z8530tty.c
@@ -1,4 +1,4 @@
-/* $NetBSD: z8530tty.c,v 1.58 1999/02/03 23:51:06 mycroft Exp $ */
+/* $NetBSD: z8530tty.c,v 1.59 1999/03/27 01:22:36 wrstuden Exp $ */
/*-
* Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999
@@ -106,6 +106,7 @@
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
+#include <sys/timepps.h>
#include <sys/tty.h>
#include <sys/time.h>
#include <sys/kernel.h>
@@ -137,6 +138,15 @@ u_int zstty_rbuf_size = ZSTTY_RING_SIZE;
u_int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE * 1) / 4;
u_int zstty_rbuf_lowat = (ZSTTY_RING_SIZE * 3) / 4;
+static int zsppscap =
+ PPS_TSFMT_TSPEC |
+ PPS_CAPTUREASSERT |
+ PPS_CAPTURECLEAR |
+#ifdef PPS_SYNC
+ PPS_HARDPPSONASSERT | PPS_HARDPPSONCLEAR |
+#endif /* PPS_SYNC */
+ PPS_OFFSETASSERT | PPS_OFFSETCLEAR;
+
struct zstty_softc {
struct device zst_dev; /* required first: base device */
struct tty *zst_tty;
@@ -179,6 +189,13 @@ struct zstty_softc {
zst_tx_stopped, /* H/W level stop (lost CTS) */
zst_st_check, /* got a status interrupt */
zst_rx_ready;
+
+ /* PPS signal on DCD, with or without inkernel clock disciplining */
+ u_char zst_ppsmask; /* pps signal mask */
+ u_char zst_ppsassert; /* pps leading edge */
+ u_char zst_ppsclear; /* pps trailing edge */
+ pps_info_t ppsinfo;
+ pps_params_t ppsparam;
};
/* Macros to clear/set/test flags. */
@@ -209,6 +226,7 @@ static void tiocm_to_zs __P((struct zstty_softc *, int, int));
static int zs_to_tiocm __P((struct zstty_softc *));
static int zshwiflow __P((struct tty *, int));
static void zs_hwiflow __P((struct zstty_softc *));
+static void zs_maskintr __P((struct zstty_softc *));
/* Low-level routines. */
static void zstty_rxint __P((struct zs_chanstate *));
@@ -397,6 +415,10 @@ zs_shutdown(zst)
/* Clear any break condition set with TIOCSBRK. */
zs_break(cs, 0);
+ /* Turn off PPS capture on last close. */
+ zst->zst_ppsmask = 0;
+ zst->ppsparam.mode = 0;
+
/*
* Hang up if necessary. Wait a bit, so the other side has time to
* notice even if we immediately open the port again.
@@ -486,6 +508,10 @@ zsopen(dev, flags, mode, p)
*/
SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE);
+ /* Clear PPS capture state on first open. */
+ zst->zst_ppsmask = 0;
+ zst->ppsparam.mode = 0;
+
splx(s2);
/* Make sure zsparam will see changes. */
@@ -683,6 +709,151 @@ zsioctl(dev, cmd, data, flag, p)
*(int *)data = zs_to_tiocm(zst);
break;
+ case PPS_CREATE:
+ break;
+
+ case PPS_DESTROY:
+ break;
+
+ case PPS_GETPARAMS: {
+ pps_params_t *pp;
+ pp = (pps_params_t *)data;
+ *pp = zst->ppsparam;
+ break;
+ }
+
+ case PPS_SETPARAMS: {
+ pps_params_t *pp;
+ int mode;
+ if (cs->cs_rr0_pps == 0) {
+ error = EINVAL;
+ break;
+ }
+ pp = (pps_params_t *)data;
+ if (pp->mode & ~zsppscap) {
+ error = EINVAL;
+ break;
+ }
+ zst->ppsparam = *pp;
+ /*
+ * compute masks from user-specified timestamp state.
+ */
+ mode = zst->ppsparam.mode;
+#ifdef PPS_SYNC
+ if (mode & PPS_HARDPPSONASSERT) {
+ mode |= PPS_CAPTUREASSERT;
+ /* XXX revoke any previous HARDPPS source */
+ }
+ if (mode & PPS_HARDPPSONCLEAR) {
+ mode |= PPS_CAPTURECLEAR;
+ /* XXX revoke any previous HARDPPS source */
+ }
+#endif /* PPS_SYNC */
+ switch (mode & PPS_CAPTUREBOTH) {
+ case 0:
+ zst->zst_ppsmask = 0;
+ break;
+
+ case PPS_CAPTUREASSERT:
+ zst->zst_ppsmask = ZSRR0_DCD;
+ zst->zst_ppsassert = ZSRR0_DCD;
+ zst->zst_ppsclear = -1;
+ break;
+
+ case PPS_CAPTURECLEAR:
+ zst->zst_ppsmask = ZSRR0_DCD;
+ zst->zst_ppsassert = -1;
+ zst->zst_ppsclear = 0;
+ break;
+
+ case PPS_CAPTUREBOTH:
+ zst->zst_ppsmask = ZSRR0_DCD;
+ zst->zst_ppsassert = ZSRR0_DCD;
+ zst->zst_ppsclear = 0;
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ /*
+ * Now update interrupts.
+ */
+ zs_maskintr(zst);
+ /*
+ * If nothing is being transmitted, set up new current values,
+ * else mark them as pending.
+ */
+ if (!cs->cs_heldchange) {
+ if (zst->zst_tx_busy) {
+ zst->zst_heldtbc = zst->zst_tbc;
+ zst->zst_tbc = 0;
+ cs->cs_heldchange = 1;
+ } else
+ zs_loadchannelregs(cs);
+ }
+
+ break;
+ }
+
+ case PPS_GETCAP:
+ *(int *)data = zsppscap;
+ break;
+
+ case PPS_FETCH: {
+ pps_info_t *pi;
+ pi = (pps_info_t *)data;
+ *pi = zst->ppsinfo;
+ break;
+ }
+
+ case PPS_WAIT:
+ /* XXX */
+ error = EOPNOTSUPP;
+ break;
+
+ case TIOCDCDTIMESTAMP: /* XXX old, overloaded API used by xntpd v3 */
+ if (cs->cs_rr0_pps == 0) {
+ error = EINVAL;
+ break;
+ }
+ /*
+ * Some GPS clocks models use the falling rather than
+ * rising edge as the on-the-second signal.
+ * The old API has no way to specify PPS polarity.
+ */
+ zst->zst_ppsmask = ZSRR0_DCD;
+#ifndef PPS_TRAILING_EDGE
+ zst->zst_ppsassert = ZSRR0_DCD;
+ zst->zst_ppsclear = -1;
+ TIMESPEC_TO_TIMEVAL((struct timeval *)data,
+ &zst->ppsinfo.assert_timestamp);
+#else
+ zst->zst_ppsassert = -1;
+ zst->zst_ppsclear = 01;
+ TIMESPEC_TO_TIMEVAL((struct timeval *)data,
+ &zst->ppsinfo.clear_timestamp);
+#endif
+ /*
+ * Now update interrupts.
+ */
+ zs_maskintr(zst);
+ /*
+ * If nothing is being transmitted, set up new current values,
+ * else mark them as pending.
+ */
+ if (!cs->cs_heldchange) {
+ if (zst->zst_tx_busy) {
+ zst->zst_heldtbc = zst->zst_tbc;
+ zst->zst_tbc = 0;
+ cs->cs_heldchange = 1;
+ } else
+ zs_loadchannelregs(cs);
+ }
+
+ break;
+
default:
error = ENOTTY;
break;
@@ -790,7 +961,7 @@ zsparam(tp, t)
struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(tp->t_dev)];
struct zs_chanstate *cs = zst->zst_cs;
int ospeed, cflag;
- u_char tmp3, tmp4, tmp5, tmp15;
+ u_char tmp3, tmp4, tmp5;
int s, error;
ospeed = t->c_ospeed;
@@ -844,17 +1015,10 @@ zsparam(tp, t)
*/
s = splzs();
- cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd;
- tmp15 = cs->cs_preg[15];
- if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD))
- SET(tmp15, ZSWR15_DCD_IE);
- else
- CLR(tmp15, ZSWR15_DCD_IE);
- if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS))
- SET(tmp15, ZSWR15_CTS_IE);
- else
- CLR(tmp15, ZSWR15_CTS_IE);
- cs->cs_preg[15] = tmp15;
+ /*
+ * Recalculate which status ints to enable.
+ */
+ zs_maskintr(zst);
/* Recompute character size bits. */
tmp3 = cs->cs_preg[3];
@@ -964,6 +1128,34 @@ zsparam(tp, t)
}
/*
+ * Compute interupt enable bits and set in the pending bits. Called both
+ * in zsparam() and when PPS (pulse per second timing) state changes.
+ * Must be called at splzs().
+ */
+static void
+zs_maskintr(zst)
+ struct zstty_softc *zst;
+{
+ struct zs_chanstate *cs = zst->zst_cs;
+ int tmp15;
+
+ cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd;
+ if (zst->zst_ppsmask != 0)
+ cs->cs_rr0_mask |= cs->cs_rr0_pps;
+ tmp15 = cs->cs_preg[15];
+ if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD))
+ SET(tmp15, ZSWR15_DCD_IE);
+ else
+ CLR(tmp15, ZSWR15_DCD_IE);
+ if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS))
+ SET(tmp15, ZSWR15_CTS_IE);
+ else
+ CLR(tmp15, ZSWR15_CTS_IE);
+ cs->cs_preg[15] = tmp15;
+}
+
+
+/*
* Raise or lower modem control (DTR/RTS) signals. If a character is
* in transmission, the change is deferred.
*/
@@ -1282,6 +1474,53 @@ zstty_stint(cs, force)
SET(cs->cs_rr0_delta, delta);
/*
+ * Pulse-per-second clock signal on edge of DCD?
+ */
+ if (ISSET(delta, zst->zst_ppsmask)) {
+ struct timeval tv;
+ if (ISSET(rr0, zst->zst_ppsmask) == zst->zst_ppsassert) {
+ /* XXX nanotime() */
+ microtime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv,
+ &zst->ppsinfo.assert_timestamp);
+ if (zst->ppsparam.mode & PPS_OFFSETASSERT) {
+ timespecadd(&zst->ppsinfo.assert_timestamp,
+ &zst->ppsparam.assert_offset,
+ &zst->ppsinfo.assert_timestamp);
+ TIMESPEC_TO_TIMEVAL(&tv,
+ &zst->ppsinfo.assert_timestamp);
+ }
+
+#ifdef PPS_SYNC
+ if (zst->ppsparam.mode & PPS_HARDPPSONASSERT)
+ hardpps(&tv, tv.tv_usec);
+#endif
+ zst->ppsinfo.assert_sequence++;
+ zst->ppsinfo.current_mode = zst->ppsparam.mode;
+ } else if (ISSET(rr0, zst->zst_ppsmask) ==
+ zst->zst_ppsclear) {
+ /* XXX nanotime() */
+ microtime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv,
+ &zst->ppsinfo.clear_timestamp);
+ if (zst->ppsparam.mode & PPS_OFFSETCLEAR) {
+ timespecadd(&zst->ppsinfo.clear_timestamp,
+ &zst->ppsparam.clear_offset,
+ &zst->ppsinfo.clear_timestamp);
+ TIMESPEC_TO_TIMEVAL(&tv,
+ &zst->ppsinfo.clear_timestamp);
+ }
+
+#ifdef PPS_SYNC
+ if (zst->ppsparam.mode & PPS_HARDPPSONCLEAR)
+ hardpps(&tv, tv.tv_usec);
+#endif
+ zst->ppsinfo.clear_sequence++;
+ zst->ppsinfo.current_mode = zst->ppsparam.mode;
+ }
+ }
+
+ /*
* Stop output immediately if we lose the output
* flow control signal or carrier detect.
*/