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