xref: /freebsd/sys/dev/ppbus/pps.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
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.10 1998/08/03 19:14:31 msmith 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 	struct timeval tv;
157 
158 	nanotime(&tc);
159 	if (!(ppb_rstr(&sc->pps_dev) & nACK))
160 		return;
161 	if (sc->ppsparam.mode & PPS_ECHOASSERT)
162 		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
163 	if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
164 		timespecadd(&tc, &sc->ppsparam.assert_offset);
165 		if (tc.tv_nsec < 0) {
166 			tc.tv_sec--;
167 			tc.tv_nsec += 1000000000;
168 		}
169 	}
170 	sc->ppsinfo.assert_timestamp = tc;
171 	sc->ppsinfo.assert_sequence++;
172 #ifdef PPS_SYNC
173 	if (sc->ppsparam.mode & PPS_HARDPPSONASSERT) {
174 		tv.tv_sec = tc.tv_sec;
175 		tv.tv_usec = tc.tv_nsec / 1000;
176 		hardpps(&tv, tv.tv_usec);
177 	}
178 #endif /* PPS_SYNC */
179 	if (sc->ppsparam.mode & PPS_ECHOASSERT)
180 		ppb_wctr(&sc->pps_dev, IRQENABLE);
181 }
182 
183 static int
184 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
185 {
186 	struct pps_data *sc = softc[minor(dev)];
187 
188 	return (std_pps_ioctl(cmd, data, &sc->ppsparam, &sc->ppsinfo, ppscap));
189 }
190 
191 static pps_devsw_installed = 0;
192 
193 static void
194 pps_drvinit(void *unused)
195 {
196 	dev_t dev;
197 
198 	if( ! pps_devsw_installed ) {
199 		dev = makedev(CDEV_MAJOR, 0);
200 		cdevsw_add(&dev, &pps_cdevsw, NULL);
201 		pps_devsw_installed = 1;
202     	}
203 }
204 
205 SYSINIT(ppsdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,pps_drvinit,NULL)
206