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