xref: /freebsd/sys/dev/ppbus/pps.c (revision 2da199da53835ee2d9228a60717fd2d0fccf9e50)
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  *
9  * $Id: pps.c,v 1.12 1998/12/07 21:58:16 archie Exp $
10  *
11  * This driver implements a draft-mogul-pps-api-02.txt PPS source.
12  *
13  * The input pin is pin#10
14  * The echo output pin is pin#14
15  *
16  */
17 
18 #include "opt_devfs.h"
19 #include "opt_ntp.h"
20 
21 #include <sys/param.h>
22 #include <sys/kernel.h>
23 #include <sys/systm.h>
24 #include <sys/conf.h>
25 #include <sys/timepps.h>
26 #ifdef DEVFS
27 #include <sys/devfsext.h>
28 #endif
29 #include <sys/malloc.h>
30 
31 #include <dev/ppbus/ppbconf.h>
32 #include "pps.h"
33 
34 #define PPS_NAME	"lppps"		/* our official name */
35 
36 static struct pps_data {
37 	int	pps_unit;
38 	struct	ppb_device pps_dev;
39 	pps_params_t	ppsparam;
40 	pps_info_t	ppsinfo;
41 } *softc[NPPS];
42 
43 static int ppscap =
44 	PPS_CAPTUREASSERT |
45 #ifdef PPS_SYNC
46 	PPS_HARDPPSONASSERT |
47 #endif /* PPS_SYNC */
48 	PPS_OFFSETASSERT |
49 	PPS_ECHOASSERT |
50 	PPS_TSFMT_TSPEC;
51 
52 static int npps;
53 
54 /*
55  * Make ourselves visible as a ppbus driver
56  */
57 
58 static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
59 static int			ppsattach(struct ppb_device *dev);
60 static void			ppsintr(int unit);
61 static void			pps_drvinit(void *unused);
62 
63 static struct ppb_driver ppsdriver = {
64     ppsprobe, ppsattach, PPS_NAME
65 };
66 
67 DATA_SET(ppbdriver_set, ppsdriver);
68 
69 static	d_open_t	ppsopen;
70 static	d_close_t	ppsclose;
71 static	d_ioctl_t	ppsioctl;
72 
73 #define CDEV_MAJOR 89
74 static struct cdevsw pps_cdevsw =
75 	{ ppsopen,	ppsclose,	noread,		nowrite,
76 	  ppsioctl,	nullstop,	nullreset,	nodevtotty,
77 	  seltrue,	nommap,		nostrat,	PPS_NAME,
78 	  NULL,		-1 };
79 
80 static struct ppb_device *
81 ppsprobe(struct ppb_data *ppb)
82 {
83 	struct pps_data *sc;
84 
85 	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
86 							M_TEMP, M_NOWAIT);
87 	if (!sc) {
88 		printf(PPS_NAME ": cannot malloc!\n");
89 		return (0);
90 	}
91 	bzero(sc, sizeof(struct pps_data));
92 
93 	softc[npps] = sc;
94 
95 	sc->pps_unit = npps++;
96 
97 	sc->pps_dev.id_unit = sc->pps_unit;
98 	sc->pps_dev.ppb = ppb;
99 	sc->pps_dev.name = ppsdriver.name;
100 	sc->pps_dev.intr = ppsintr;
101 
102 	return (&sc->pps_dev);
103 }
104 
105 static int
106 ppsattach(struct ppb_device *dev)
107 {
108 	/*
109 	 * Report ourselves
110 	 */
111 	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
112 	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
113 
114 #ifdef DEVFS
115 	devfs_add_devswf(&pps_cdevsw,
116 		dev->id_unit, DV_CHR,
117 		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
118 #endif
119 
120 	return (1);
121 }
122 
123 static	int
124 ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
125 {
126 	struct pps_data *sc;
127 	u_int unit = minor(dev);
128 
129 	if ((unit >= npps))
130 		return (ENXIO);
131 
132 	sc = softc[unit];
133 
134 	if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
135 		return (EINTR);
136 
137 	ppb_wctr(&sc->pps_dev, 0);
138 	ppb_wctr(&sc->pps_dev, IRQENABLE);
139 
140 	return(0);
141 }
142 
143 static	int
144 ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
145 {
146 	struct pps_data *sc = softc[minor(dev)];
147 
148 	sc->ppsparam.mode = 0;
149 
150 	ppb_wdtr(&sc->pps_dev, 0);
151 	ppb_wctr(&sc->pps_dev, 0);
152 
153 	ppb_release_bus(&sc->pps_dev);
154 	return(0);
155 }
156 
157 static void
158 ppsintr(int unit)
159 {
160 	struct pps_data *sc = softc[unit];
161 	struct timespec tc;
162 
163 	nanotime(&tc);
164 	if (!(ppb_rstr(&sc->pps_dev) & nACK))
165 		return;
166 	if (sc->ppsparam.mode & PPS_ECHOASSERT)
167 		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
168 	if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
169 		timespecadd(&tc, &sc->ppsparam.assert_offset);
170 		if (tc.tv_nsec < 0) {
171 			tc.tv_sec--;
172 			tc.tv_nsec += 1000000000;
173 		}
174 	}
175 	sc->ppsinfo.assert_timestamp = tc;
176 	sc->ppsinfo.assert_sequence++;
177 #ifdef PPS_SYNC
178 	if (sc->ppsparam.mode & PPS_HARDPPSONASSERT) {
179 		struct timeval tv;
180 
181 		tv.tv_sec = tc.tv_sec;
182 		tv.tv_usec = tc.tv_nsec / 1000;
183 		hardpps(&tv, tv.tv_usec);
184 	}
185 #endif /* PPS_SYNC */
186 	if (sc->ppsparam.mode & PPS_ECHOASSERT)
187 		ppb_wctr(&sc->pps_dev, IRQENABLE);
188 }
189 
190 static int
191 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
192 {
193 	struct pps_data *sc = softc[minor(dev)];
194 
195 	return (std_pps_ioctl(cmd, data, &sc->ppsparam, &sc->ppsinfo, ppscap));
196 }
197 
198 static pps_devsw_installed = 0;
199 
200 static void
201 pps_drvinit(void *unused)
202 {
203 	dev_t dev;
204 
205 	if( ! pps_devsw_installed ) {
206 		dev = makedev(CDEV_MAJOR, 0);
207 		cdevsw_add(&dev, &pps_cdevsw, NULL);
208 		pps_devsw_installed = 1;
209     	}
210 }
211 
212 SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL)
213