xref: /netbsd-src/sys/dev/ppbus/pps_ppbus.c (revision f9228f42259a421502f04b1ddb2df91c2ecbc519)
1*f9228f42Sdholland /* $NetBSD: pps_ppbus.c,v 1.16 2014/07/25 08:10:38 dholland Exp $ */
239abe92eSdrochner 
339abe92eSdrochner /*
4de4337abSkardel  * ported to timecounters by Frank Kardel 2006
5de4337abSkardel  *
639abe92eSdrochner  * Copyright (c) 2004
739abe92eSdrochner  * 	Matthias Drochner.  All rights reserved.
839abe92eSdrochner  *
939abe92eSdrochner  * Redistribution and use in source and binary forms, with or without
1039abe92eSdrochner  * modification, are permitted provided that the following conditions
1139abe92eSdrochner  * are met:
1239abe92eSdrochner  * 1. Redistributions of source code must retain the above copyright
1339abe92eSdrochner  *    notice, this list of conditions, and the following disclaimer.
1439abe92eSdrochner  * 2. Redistributions in binary form must reproduce the above copyright
1539abe92eSdrochner  *    notice, this list of conditions and the following disclaimer in the
1639abe92eSdrochner  *    documentation and/or other materials provided with the distribution.
1739abe92eSdrochner  *
1839abe92eSdrochner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1939abe92eSdrochner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2039abe92eSdrochner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2139abe92eSdrochner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2239abe92eSdrochner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2339abe92eSdrochner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2439abe92eSdrochner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2539abe92eSdrochner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2639abe92eSdrochner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2739abe92eSdrochner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2839abe92eSdrochner  * SUCH DAMAGE.
2939abe92eSdrochner  */
3039abe92eSdrochner 
3139abe92eSdrochner #include <sys/cdefs.h>
32*f9228f42Sdholland __KERNEL_RCSID(0, "$NetBSD: pps_ppbus.c,v 1.16 2014/07/25 08:10:38 dholland Exp $");
3339abe92eSdrochner 
3439abe92eSdrochner #include "opt_ntp.h"
3539abe92eSdrochner 
3639abe92eSdrochner #include <sys/param.h>
3739abe92eSdrochner #include <sys/systm.h>
3839abe92eSdrochner #include <sys/conf.h>
3939abe92eSdrochner #include <sys/proc.h>
4039abe92eSdrochner #include <sys/ioctl.h>
4139abe92eSdrochner #include <sys/timepps.h>
4239abe92eSdrochner 
4339abe92eSdrochner #include <dev/ppbus/ppbus_base.h>
4439abe92eSdrochner #include <dev/ppbus/ppbus_device.h>
4539abe92eSdrochner #include <dev/ppbus/ppbus_io.h>
4639abe92eSdrochner #include <dev/ppbus/ppbus_var.h>
4739abe92eSdrochner 
4839abe92eSdrochner struct pps_softc {
4939abe92eSdrochner 	struct ppbus_device_softc pps_dev;
50376411d2Scegger 	device_t ppbus;
5139abe92eSdrochner 	int busy;
52de4337abSkardel 	struct pps_state pps_state;	/* pps state */
5339abe92eSdrochner };
5439abe92eSdrochner 
55376411d2Scegger static int pps_probe(device_t, cfdata_t, void *);
56376411d2Scegger static void pps_attach(device_t, device_t, void *);
57b849cd90Scegger CFATTACH_DECL_NEW(pps, sizeof(struct pps_softc), pps_probe, pps_attach,
5839abe92eSdrochner 	NULL, NULL);
5939abe92eSdrochner extern struct cfdriver pps_cd;
6039abe92eSdrochner 
6139abe92eSdrochner static dev_type_open(ppsopen);
6239abe92eSdrochner static dev_type_close(ppsclose);
6339abe92eSdrochner static dev_type_ioctl(ppsioctl);
6439abe92eSdrochner const struct cdevsw pps_cdevsw = {
65a68f9396Sdholland 	.d_open = ppsopen,
66a68f9396Sdholland 	.d_close = ppsclose,
67a68f9396Sdholland 	.d_read = noread,
68a68f9396Sdholland 	.d_write = nowrite,
69a68f9396Sdholland 	.d_ioctl = ppsioctl,
70a68f9396Sdholland 	.d_stop = nostop,
71a68f9396Sdholland 	.d_tty = notty,
72a68f9396Sdholland 	.d_poll = nopoll,
73a68f9396Sdholland 	.d_mmap = nommap,
74a68f9396Sdholland 	.d_kqfilter = nokqfilter,
75*f9228f42Sdholland 	.d_discard = nodiscard,
76a68f9396Sdholland 	.d_flag = D_OTHER
7739abe92eSdrochner };
7839abe92eSdrochner 
7939abe92eSdrochner static void ppsintr(void *arg);
8039abe92eSdrochner 
8139abe92eSdrochner static int
pps_probe(device_t parent,cfdata_t match,void * aux)82376411d2Scegger pps_probe(device_t parent, cfdata_t match, void *aux)
8339abe92eSdrochner {
8439abe92eSdrochner 	struct ppbus_attach_args *args = aux;
8539abe92eSdrochner 
8639abe92eSdrochner 	/* we need an interrupt */
8739abe92eSdrochner 	if (!(args->capabilities & PPBUS_HAS_INTR))
8839abe92eSdrochner 		return 0;
8939abe92eSdrochner 
9039abe92eSdrochner 	return 1;
9139abe92eSdrochner }
9239abe92eSdrochner 
9339abe92eSdrochner static void
pps_attach(device_t parent,device_t self,void * aux)94376411d2Scegger pps_attach(device_t parent, device_t self, void *aux)
9539abe92eSdrochner {
96ec03de0cSthorpej 	struct pps_softc *sc = device_private(self);
9739abe92eSdrochner 
9839abe92eSdrochner 	sc->ppbus = parent;
99376411d2Scegger 	sc->pps_dev.sc_dev = self;
10039abe92eSdrochner 
10139abe92eSdrochner 	printf("\n");
10239abe92eSdrochner }
10339abe92eSdrochner 
10439abe92eSdrochner static int
ppsopen(dev_t dev,int flags,int fmt,struct lwp * l)105e868ae92Srpaulo ppsopen(dev_t dev, int flags, int fmt, struct lwp *l)
10639abe92eSdrochner {
10739abe92eSdrochner 	struct pps_softc *sc;
108fa6adb2aSdrochner 	int res, weg = 0;
10939abe92eSdrochner 
1106a2e2c8cScegger 	sc = device_lookup_private(&pps_cd, minor(dev));
11139abe92eSdrochner 	if (!sc)
11239abe92eSdrochner 		return (ENXIO);
11339abe92eSdrochner 
11439abe92eSdrochner 	if (sc->busy)
11539abe92eSdrochner 		return (0);
11639abe92eSdrochner 
117b849cd90Scegger 	if (ppbus_request_bus(sc->ppbus, sc->pps_dev.sc_dev,
11839abe92eSdrochner 			      PPBUS_WAIT|PPBUS_INTR, 0))
11939abe92eSdrochner 		return (EINTR);
12039abe92eSdrochner 
121fa6adb2aSdrochner 	ppbus_write_ivar(sc->ppbus, PPBUS_IVAR_IEEE, &weg);
122fa6adb2aSdrochner 
12339abe92eSdrochner 	/* attach the interrupt handler */
12439abe92eSdrochner 	/* XXX priority should be set here */
12539abe92eSdrochner 	res = ppbus_add_handler(sc->ppbus, ppsintr, sc);
12639abe92eSdrochner 	if (res) {
127b849cd90Scegger 		ppbus_release_bus(sc->ppbus, sc->pps_dev.sc_dev,
12839abe92eSdrochner 				  PPBUS_WAIT, 0);
12939abe92eSdrochner 		return (res);
13039abe92eSdrochner 	}
13139abe92eSdrochner 
13239abe92eSdrochner 	ppbus_set_mode(sc->ppbus, PPBUS_PS2, 0);
13339abe92eSdrochner 	ppbus_wctr(sc->ppbus, IRQENABLE | PCD | nINIT | SELECTIN);
13439abe92eSdrochner 
135a2249ef7Sad 	mutex_spin_enter(&timecounter_lock);
136de4337abSkardel 	memset((void *)&sc->pps_state, 0, sizeof(sc->pps_state));
137de4337abSkardel 	sc->pps_state.ppscap = PPS_CAPTUREASSERT;
138de4337abSkardel 	pps_init(&sc->pps_state);
139a2249ef7Sad 	mutex_spin_exit(&timecounter_lock);
140de4337abSkardel 
14139abe92eSdrochner 	sc->busy = 1;
142a2249ef7Sad 
14339abe92eSdrochner 	return (0);
14439abe92eSdrochner }
14539abe92eSdrochner 
14639abe92eSdrochner static int
ppsclose(dev_t dev,int flags,int fmt,struct lwp * l)147e868ae92Srpaulo ppsclose(dev_t dev, int flags, int fmt, struct lwp *l)
14839abe92eSdrochner {
1496a2e2c8cScegger 	struct pps_softc *sc = device_lookup_private(&pps_cd, minor(dev));
150376411d2Scegger 	device_t ppbus = sc->ppbus;
15139abe92eSdrochner 
15239abe92eSdrochner 	sc->busy = 0;
153a2249ef7Sad 	mutex_spin_enter(&timecounter_lock);
154de4337abSkardel 	sc->pps_state.ppsparam.mode = 0;
155a2249ef7Sad 	mutex_spin_exit(&timecounter_lock);
156de4337abSkardel 
15739abe92eSdrochner 	ppbus_wdtr(ppbus, 0);
15839abe92eSdrochner 	ppbus_wctr(ppbus, 0);
15939abe92eSdrochner 
16039abe92eSdrochner 	ppbus_remove_handler(ppbus, ppsintr);
16139abe92eSdrochner 	ppbus_set_mode(ppbus, PPBUS_COMPATIBLE, 0);
162b849cd90Scegger 	ppbus_release_bus(ppbus, sc->pps_dev.sc_dev, PPBUS_WAIT, 0);
16339abe92eSdrochner 	return (0);
16439abe92eSdrochner }
16539abe92eSdrochner 
16639abe92eSdrochner static void
ppsintr(void * arg)16739abe92eSdrochner ppsintr(void *arg)
16839abe92eSdrochner {
16939abe92eSdrochner 	struct pps_softc *sc = arg;
170376411d2Scegger 	device_t ppbus = sc->ppbus;
17139abe92eSdrochner 
172a2249ef7Sad 	mutex_spin_enter(&timecounter_lock);
173de4337abSkardel 	pps_capture(&sc->pps_state);
174a2249ef7Sad 	if (!(ppbus_rstr(ppbus) & nACK)) {
175a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
17639abe92eSdrochner 		return;
177a2249ef7Sad 	}
178de4337abSkardel 	if (sc->pps_state.ppsparam.mode & PPS_ECHOASSERT)
179de4337abSkardel 		ppbus_wctr(ppbus, IRQENABLE | AUTOFEED);
180de4337abSkardel 	pps_event(&sc->pps_state, PPS_CAPTUREASSERT);
181de4337abSkardel 	if (sc->pps_state.ppsparam.mode & PPS_ECHOASSERT)
182de4337abSkardel 		ppbus_wctr(ppbus, IRQENABLE);
183a2249ef7Sad 	mutex_spin_exit(&timecounter_lock);
18439abe92eSdrochner }
18539abe92eSdrochner 
18639abe92eSdrochner static int
ppsioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)18753524e44Schristos ppsioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
18839abe92eSdrochner {
1896a2e2c8cScegger 	struct pps_softc *sc = device_lookup_private(&pps_cd, minor(dev));
19039abe92eSdrochner 	int error = 0;
19139abe92eSdrochner 
19239abe92eSdrochner 	switch (cmd) {
193de4337abSkardel 	case PPS_IOC_CREATE:
194de4337abSkardel 	case PPS_IOC_DESTROY:
195de4337abSkardel 	case PPS_IOC_GETPARAMS:
196de4337abSkardel 	case PPS_IOC_SETPARAMS:
197de4337abSkardel 	case PPS_IOC_GETCAP:
198de4337abSkardel 	case PPS_IOC_FETCH:
199de4337abSkardel #ifdef PPS_SYNC
200de4337abSkardel 	case PPS_IOC_KCBIND:
201de4337abSkardel #endif
202a2249ef7Sad 		mutex_spin_enter(&timecounter_lock);
203de4337abSkardel 		error = pps_ioctl(cmd, data, &sc->pps_state);
204a2249ef7Sad 		mutex_spin_exit(&timecounter_lock);
205de4337abSkardel 		break;
20639abe92eSdrochner 
20739abe92eSdrochner 	default:
20839abe92eSdrochner 		error = EPASSTHROUGH;
20939abe92eSdrochner 		break;
21039abe92eSdrochner 	}
21139abe92eSdrochner 	return (error);
21239abe92eSdrochner }
213