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