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