pps_ppbus.c revision 1.8.4.1 1 /* $NetBSD: pps_ppbus.c,v 1.8.4.1 2007/03/12 05:56:47 rmind Exp $ */
2
3 /*
4 * ported to timecounters by Frank Kardel 2006
5 *
6 * Copyright (c) 2004
7 * Matthias Drochner. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions, and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: pps_ppbus.c,v 1.8.4.1 2007/03/12 05:56:47 rmind Exp $");
33
34 #include "opt_ntp.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/proc.h>
40 #include <sys/ioctl.h>
41 #include <sys/timepps.h>
42
43 #include <dev/ppbus/ppbus_base.h>
44 #include <dev/ppbus/ppbus_device.h>
45 #include <dev/ppbus/ppbus_io.h>
46 #include <dev/ppbus/ppbus_var.h>
47
48 struct pps_softc {
49 struct ppbus_device_softc pps_dev;
50 struct device *ppbus;
51 int busy;
52 #ifdef __HAVE_TIMECOUNTER
53 struct pps_state pps_state; /* pps state */
54 #else /* !__HAVE_TIMECOUNTER */
55 pps_info_t ppsinfo;
56 pps_params_t ppsparam;
57 #ifdef PPS_SYNC
58 int hardpps;
59 #endif
60 #endif /* !__HAVE_TIMECOUNTER */
61 };
62
63 static int pps_probe(struct device *, struct cfdata *, void *);
64 static void pps_attach(struct device *, struct device *, void *);
65 CFATTACH_DECL(pps, sizeof(struct pps_softc), pps_probe, pps_attach,
66 NULL, NULL);
67 extern struct cfdriver pps_cd;
68
69 static dev_type_open(ppsopen);
70 static dev_type_close(ppsclose);
71 static dev_type_ioctl(ppsioctl);
72 const struct cdevsw pps_cdevsw = {
73 ppsopen, ppsclose, noread, nowrite, ppsioctl,
74 nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
75 };
76
77 static void ppsintr(void *arg);
78
79 #ifndef __HAVE_TIMECOUNTER
80 static int ppscap = PPS_TSFMT_TSPEC | PPS_CAPTUREASSERT | PPS_OFFSETASSERT;
81 #endif
82
83 static int
84 pps_probe(struct device *parent, struct cfdata *match, void *aux)
85 {
86 struct ppbus_attach_args *args = aux;
87
88 /* we need an interrupt */
89 if (!(args->capabilities & PPBUS_HAS_INTR))
90 return 0;
91
92 return 1;
93 }
94
95 static void
96 pps_attach(struct device *parent, struct device *self, void *aux)
97 {
98 struct pps_softc *sc = device_private(self);
99
100 sc->ppbus = parent;
101
102 printf("\n");
103 }
104
105 static int
106 ppsopen(dev_t dev, int flags, int fmt, struct lwp *l)
107 {
108 struct pps_softc *sc;
109 int res, weg = 0;
110
111 sc = device_lookup(&pps_cd, minor(dev));
112 if (!sc)
113 return (ENXIO);
114
115 if (sc->busy)
116 return (0);
117
118 if (ppbus_request_bus(sc->ppbus, &sc->pps_dev.sc_dev,
119 PPBUS_WAIT|PPBUS_INTR, 0))
120 return (EINTR);
121
122 ppbus_write_ivar(sc->ppbus, PPBUS_IVAR_IEEE, &weg);
123
124 /* attach the interrupt handler */
125 /* XXX priority should be set here */
126 res = ppbus_add_handler(sc->ppbus, ppsintr, sc);
127 if (res) {
128 ppbus_release_bus(sc->ppbus, &sc->pps_dev.sc_dev,
129 PPBUS_WAIT, 0);
130 return (res);
131 }
132
133 ppbus_set_mode(sc->ppbus, PPBUS_PS2, 0);
134 ppbus_wctr(sc->ppbus, IRQENABLE | PCD | nINIT | SELECTIN);
135
136 #ifdef __HAVE_TIMECOUNTER
137 memset((void *)&sc->pps_state, 0, sizeof(sc->pps_state));
138 sc->pps_state.ppscap = PPS_CAPTUREASSERT;
139 pps_init(&sc->pps_state);
140 #endif /* __HAVE_TIMECOUNTER */
141
142 sc->busy = 1;
143 return (0);
144 }
145
146 static int
147 ppsclose(dev_t dev, int flags, int fmt, struct lwp *l)
148 {
149 struct pps_softc *sc = device_lookup(&pps_cd, minor(dev));
150 struct device *ppbus = sc->ppbus;
151
152 sc->busy = 0;
153 #ifdef __HAVE_TIMECOUNTER
154 sc->pps_state.ppsparam.mode = 0;
155 #else /* !__HAVE_TIMECOUNTER */
156 sc->ppsparam.mode = 0;
157 #ifdef PPS_SYNC
158 sc->hardpps = 0;
159 #endif
160 #endif /* __HAVE_TIMECOUNTER */
161
162 ppbus_wdtr(ppbus, 0);
163 ppbus_wctr(ppbus, 0);
164
165 ppbus_remove_handler(ppbus, ppsintr);
166 ppbus_set_mode(ppbus, PPBUS_COMPATIBLE, 0);
167 ppbus_release_bus(ppbus, &sc->pps_dev.sc_dev, PPBUS_WAIT, 0);
168 return (0);
169 }
170
171 static void
172 ppsintr(void *arg)
173 {
174 struct pps_softc *sc = arg;
175 struct device *ppbus = sc->ppbus;
176 #ifndef __HAVE_TIMECOUNTER
177 struct timeval tv;
178
179 #else /* __HAVE_TIMECOUNTER */
180 pps_capture(&sc->pps_state);
181 #endif /* __HAVE_TIMECOUNTER */
182
183 if (!(ppbus_rstr(ppbus) & nACK))
184 return;
185
186 #ifdef __HAVE_TIMECOUNTER
187 if (sc->pps_state.ppsparam.mode & PPS_ECHOASSERT)
188 ppbus_wctr(ppbus, IRQENABLE | AUTOFEED);
189
190 pps_event(&sc->pps_state, PPS_CAPTUREASSERT);
191
192 if (sc->pps_state.ppsparam.mode & PPS_ECHOASSERT)
193 ppbus_wctr(ppbus, IRQENABLE);
194 #else /* !__HAVE_TIMECOUNTER */
195 microtime(&tv);
196 TIMEVAL_TO_TIMESPEC(&tv, &sc->ppsinfo.assert_timestamp);
197 if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
198 timespecadd(&sc->ppsinfo.assert_timestamp,
199 &sc->ppsparam.assert_offset,
200 &sc->ppsinfo.assert_timestamp);
201 }
202 #ifdef PPS_SYNC
203 if (sc->hardpps)
204 hardpps(&tv, tv.tv_usec);
205 #endif
206 sc->ppsinfo.assert_sequence++;
207 sc->ppsinfo.current_mode = sc->ppsparam.mode;
208 #endif /* !__HAVE_TIMECOUNTER */
209 }
210
211 static int
212 ppsioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
213 {
214 struct pps_softc *sc = device_lookup(&pps_cd, minor(dev));
215 int error = 0;
216
217 switch (cmd) {
218 #ifdef __HAVE_TIMECOUNTER
219 case PPS_IOC_CREATE:
220 case PPS_IOC_DESTROY:
221 case PPS_IOC_GETPARAMS:
222 case PPS_IOC_SETPARAMS:
223 case PPS_IOC_GETCAP:
224 case PPS_IOC_FETCH:
225 #ifdef PPS_SYNC
226 case PPS_IOC_KCBIND:
227 #endif
228 error = pps_ioctl(cmd, data, &sc->pps_state);
229 break;
230 #else /* !__HAVE_TIMECOUNTER */
231 case PPS_IOC_CREATE:
232 break;
233
234 case PPS_IOC_DESTROY:
235 break;
236
237 case PPS_IOC_GETPARAMS: {
238 pps_params_t *pp;
239 pp = (pps_params_t *)data;
240 *pp = sc->ppsparam;
241 break;
242 }
243
244 case PPS_IOC_SETPARAMS: {
245 pps_params_t *pp;
246 pp = (pps_params_t *)data;
247 if (pp->mode & ~(ppscap)) {
248 error = EINVAL;
249 break;
250 }
251 sc->ppsparam = *pp;
252 break;
253 }
254
255 case PPS_IOC_GETCAP:
256 *(int*)data = ppscap;
257 break;
258
259 case PPS_IOC_FETCH: {
260 pps_info_t *pi;
261 pi = (pps_info_t *)data;
262 *pi = sc->ppsinfo;
263 break;
264 }
265
266 #ifdef PPS_SYNC
267 case PPS_IOC_KCBIND:
268 if (*(int *)data & PPS_CAPTUREASSERT)
269 sc->hardpps = 1;
270 else
271 sc->hardpps = 0;
272 break;
273 #endif
274 #endif /* !__HAVE_TIMECOUNTER */
275
276 default:
277 error = EPASSTHROUGH;
278 break;
279 }
280 return (error);
281 }
282