xref: /freebsd/sys/dev/ppbus/pps.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
1098ca2bdSWarner Losh /*-
264de3fddSPedro F. Giffuni  * SPDX-License-Identifier: Beerware
364de3fddSPedro F. Giffuni  *
4507e2e44SPoul-Henning Kamp  * ----------------------------------------------------------------------------
5507e2e44SPoul-Henning Kamp  * "THE BEER-WARE LICENSE" (Revision 42):
6507e2e44SPoul-Henning Kamp  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
7507e2e44SPoul-Henning Kamp  * can do whatever you want with this stuff. If we meet some day, and you think
8507e2e44SPoul-Henning Kamp  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
9507e2e44SPoul-Henning Kamp  * ----------------------------------------------------------------------------
10507e2e44SPoul-Henning Kamp  *
11389825d5SPoul-Henning Kamp  *
12389825d5SPoul-Henning Kamp  * This driver implements a draft-mogul-pps-api-02.txt PPS source.
13389825d5SPoul-Henning Kamp  *
14389825d5SPoul-Henning Kamp  * The input pin is pin#10
15389825d5SPoul-Henning Kamp  * The echo output pin is pin#14
16507e2e44SPoul-Henning Kamp  *
17507e2e44SPoul-Henning Kamp  */
18507e2e44SPoul-Henning Kamp 
19507e2e44SPoul-Henning Kamp #include <sys/param.h>
202067d312SJohn Baldwin #include <sys/lock.h>
21507e2e44SPoul-Henning Kamp #include <sys/kernel.h>
22507e2e44SPoul-Henning Kamp #include <sys/systm.h>
230f210c92SNicolas Souchu #include <sys/module.h>
242067d312SJohn Baldwin #include <sys/sx.h>
250f210c92SNicolas Souchu #include <sys/bus.h>
26507e2e44SPoul-Henning Kamp #include <sys/conf.h>
278afeddf0SPoul-Henning Kamp #include <sys/timepps.h>
280f210c92SNicolas Souchu #include <machine/bus.h>
290f210c92SNicolas Souchu #include <machine/resource.h>
300f210c92SNicolas Souchu #include <sys/rman.h>
31507e2e44SPoul-Henning Kamp 
32507e2e44SPoul-Henning Kamp #include <dev/ppbus/ppbconf.h>
330f210c92SNicolas Souchu #include "ppbus_if.h"
340f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h>
35507e2e44SPoul-Henning Kamp 
361a03ce6cSPoul-Henning Kamp #define PPS_NAME	"pps"		/* our official name */
37507e2e44SPoul-Henning Kamp 
38daefef7cSPeter Wemm #define PRVERBOSE(fmt, arg...)	if (bootverbose) printf(fmt, ##arg);
39d7e53105SWarner Losh 
401a03ce6cSPoul-Henning Kamp struct pps_data {
4195f0f58cSPoul-Henning Kamp 	struct	ppb_device pps_dev;
42bd61b8e8SPoul-Henning Kamp 	struct	pps_state pps[9];
4389c9c53dSPoul-Henning Kamp 	struct cdev *devs[9];
44bd61b8e8SPoul-Henning Kamp 	device_t ppsdev;
45bd61b8e8SPoul-Henning Kamp 	device_t ppbus;
46bd61b8e8SPoul-Henning Kamp 	int	busy;
472067d312SJohn Baldwin 	struct callout timeout;
48bd61b8e8SPoul-Henning Kamp 	int	lastdata;
490f210c92SNicolas Souchu 
502067d312SJohn Baldwin 	struct sx lock;
510f210c92SNicolas Souchu 	struct resource *intr_resource;	/* interrupt resource */
520f210c92SNicolas Souchu 	void *intr_cookie;		/* interrupt registration cookie */
531a03ce6cSPoul-Henning Kamp };
54507e2e44SPoul-Henning Kamp 
552067d312SJohn Baldwin static void	ppsintr(void *arg);
56bd61b8e8SPoul-Henning Kamp static void 	ppshcpoll(void *arg);
57507e2e44SPoul-Henning Kamp 
580f210c92SNicolas Souchu #define DEVTOSOFTC(dev) \
590f210c92SNicolas Souchu 	((struct pps_data *)device_get_softc(dev))
60507e2e44SPoul-Henning Kamp 
6195f0f58cSPoul-Henning Kamp static	d_open_t	ppsopen;
6295f0f58cSPoul-Henning Kamp static	d_close_t	ppsclose;
638afeddf0SPoul-Henning Kamp static	d_ioctl_t	ppsioctl;
64507e2e44SPoul-Henning Kamp 
654e2f199eSPoul-Henning Kamp static struct cdevsw pps_cdevsw = {
66dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
677ac40f5fSPoul-Henning Kamp 	.d_open =	ppsopen,
687ac40f5fSPoul-Henning Kamp 	.d_close =	ppsclose,
697ac40f5fSPoul-Henning Kamp 	.d_ioctl =	ppsioctl,
707ac40f5fSPoul-Henning Kamp 	.d_name =	PPS_NAME,
714e2f199eSPoul-Henning Kamp };
72507e2e44SPoul-Henning Kamp 
730f063508SPeter Wemm static void
740f063508SPeter Wemm ppsidentify(driver_t *driver, device_t parent)
750f063508SPeter Wemm {
760f063508SPeter Wemm 
77a5c7e3bbSGuido van Rooij 	device_t dev;
78a5c7e3bbSGuido van Rooij 
79ae6b868aSJohn Baldwin 	dev = device_find_child(parent, PPS_NAME, -1);
80a5c7e3bbSGuido van Rooij 	if (!dev)
81*a05a6804SWarner Losh 		BUS_ADD_CHILD(parent, 0, PPS_NAME, DEVICE_UNIT_ANY);
820f063508SPeter Wemm }
830f063508SPeter Wemm 
840f210c92SNicolas Souchu static int
85bd61b8e8SPoul-Henning Kamp ppstry(device_t ppbus, int send, int expect)
86bd61b8e8SPoul-Henning Kamp {
87bd61b8e8SPoul-Henning Kamp 	int i;
88bd61b8e8SPoul-Henning Kamp 
89bd61b8e8SPoul-Henning Kamp 	ppb_wdtr(ppbus, send);
90bd61b8e8SPoul-Henning Kamp 	i = ppb_rdtr(ppbus);
91d7e53105SWarner Losh 	PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
92bd61b8e8SPoul-Henning Kamp 	return (i != expect);
93bd61b8e8SPoul-Henning Kamp }
94bd61b8e8SPoul-Henning Kamp 
95bd61b8e8SPoul-Henning Kamp static int
960f210c92SNicolas Souchu ppsprobe(device_t ppsdev)
97507e2e44SPoul-Henning Kamp {
980f210c92SNicolas Souchu 	device_set_desc(ppsdev, "Pulse per second Timing Interface");
99507e2e44SPoul-Henning Kamp 
100d7e53105SWarner Losh 	return (0);
101d7e53105SWarner Losh }
102d7e53105SWarner Losh 
103d7e53105SWarner Losh static int
104d7e53105SWarner Losh ppsattach(device_t dev)
105d7e53105SWarner Losh {
106d7e53105SWarner Losh 	struct pps_data *sc = DEVTOSOFTC(dev);
107d7e53105SWarner Losh 	device_t ppbus = device_get_parent(dev);
10889c9c53dSPoul-Henning Kamp 	struct cdev *d;
1092067d312SJohn Baldwin 	int error, i, unit, rid = 0;
110d7e53105SWarner Losh 
111d7e53105SWarner Losh 	/* declare our interrupt handler */
112ca3d3795SJohn Baldwin 	sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
113ca3d3795SJohn Baldwin 	    RF_SHAREABLE);
114ca3d3795SJohn Baldwin 
115d7e53105SWarner Losh 	/* interrupts seem mandatory */
1162067d312SJohn Baldwin 	if (sc->intr_resource == NULL) {
1172067d312SJohn Baldwin 		device_printf(dev, "Unable to allocate interrupt resource\n");
118d7e53105SWarner Losh 		return (ENXIO);
1192067d312SJohn Baldwin 	}
120d7e53105SWarner Losh 
1212067d312SJohn Baldwin 	error = bus_setup_intr(dev, sc->intr_resource,
1222067d312SJohn Baldwin 	    INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr,
1232067d312SJohn Baldwin 	    sc, &sc->intr_cookie);
1242067d312SJohn Baldwin 	if (error) {
1252067d312SJohn Baldwin 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource);
1262067d312SJohn Baldwin 		device_printf(dev, "Unable to register interrupt handler\n");
1272067d312SJohn Baldwin 		return (error);
1282067d312SJohn Baldwin 	}
1292067d312SJohn Baldwin 
1302067d312SJohn Baldwin 	sx_init(&sc->lock, "pps");
1312067d312SJohn Baldwin 	ppb_init_callout(ppbus, &sc->timeout, 0);
132d7e53105SWarner Losh 	sc->ppsdev = dev;
133d7e53105SWarner Losh 	sc->ppbus = ppbus;
134bd61b8e8SPoul-Henning Kamp 	unit = device_get_unit(ppbus);
135d7e53105SWarner Losh 	d = make_dev(&pps_cdevsw, unit,
1362066d8e7SRobert Watson 	    UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit);
137d7e53105SWarner Losh 	sc->devs[0] = d;
138bd61b8e8SPoul-Henning Kamp 	sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
139c62aa65bSWarner Losh 	sc->pps[0].driver_abi = PPS_ABI_VERSION;
140c62aa65bSWarner Losh 	sc->pps[0].driver_mtx = ppb_get_lock(ppbus);
141d7e53105SWarner Losh 	d->si_drv1 = sc;
142d7e53105SWarner Losh 	d->si_drv2 = (void*)0;
143c62aa65bSWarner Losh 	pps_init_abi(&sc->pps[0]);
144bd61b8e8SPoul-Henning Kamp 
1452067d312SJohn Baldwin 	ppb_lock(ppbus);
1462067d312SJohn Baldwin 	if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) {
1472067d312SJohn Baldwin 		ppb_unlock(ppbus);
148bd61b8e8SPoul-Henning Kamp 		return (0);
1492067d312SJohn Baldwin 	}
150bd61b8e8SPoul-Henning Kamp 
151bd61b8e8SPoul-Henning Kamp 	do {
152bd61b8e8SPoul-Henning Kamp 		i = ppb_set_mode(sc->ppbus, PPB_EPP);
153d7e53105SWarner Losh 		PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
154bd61b8e8SPoul-Henning Kamp 		if (i == -1)
155bd61b8e8SPoul-Henning Kamp 			break;
156bd61b8e8SPoul-Henning Kamp 		i = 0;
157bd61b8e8SPoul-Henning Kamp 		ppb_wctr(ppbus, i);
158bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0x00, 0x00))
159bd61b8e8SPoul-Henning Kamp 			break;
160bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0x55, 0x55))
161bd61b8e8SPoul-Henning Kamp 			break;
162bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0xaa, 0xaa))
163bd61b8e8SPoul-Henning Kamp 			break;
164bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0xff, 0xff))
165bd61b8e8SPoul-Henning Kamp 			break;
166bd61b8e8SPoul-Henning Kamp 
167bd61b8e8SPoul-Henning Kamp 		i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
168bd61b8e8SPoul-Henning Kamp 		ppb_wctr(ppbus, i);
169d7e53105SWarner Losh 		PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
170bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0x00, 0x00))
171bd61b8e8SPoul-Henning Kamp 			break;
172bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0x55, 0x00))
173bd61b8e8SPoul-Henning Kamp 			break;
174bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0xaa, 0x00))
175bd61b8e8SPoul-Henning Kamp 			break;
176bd61b8e8SPoul-Henning Kamp 		if (ppstry(ppbus, 0xff, 0x00))
177bd61b8e8SPoul-Henning Kamp 			break;
178bd61b8e8SPoul-Henning Kamp 
179bd61b8e8SPoul-Henning Kamp 		i = IRQENABLE | PCD | nINIT | SELECTIN;
180bd61b8e8SPoul-Henning Kamp 		ppb_wctr(ppbus, i);
181d7e53105SWarner Losh 		PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
182bd61b8e8SPoul-Henning Kamp 		ppstry(ppbus, 0x00, 0xff);
183bd61b8e8SPoul-Henning Kamp 		ppstry(ppbus, 0x55, 0xff);
184bd61b8e8SPoul-Henning Kamp 		ppstry(ppbus, 0xaa, 0xff);
185bd61b8e8SPoul-Henning Kamp 		ppstry(ppbus, 0xff, 0xff);
1862067d312SJohn Baldwin 		ppb_unlock(ppbus);
187bd61b8e8SPoul-Henning Kamp 
188bd61b8e8SPoul-Henning Kamp 		for (i = 1; i < 9; i++) {
189d7e53105SWarner Losh 			d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
1902066d8e7SRobert Watson 			  UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1);
191d7e53105SWarner Losh 			sc->devs[i] = d;
192bd61b8e8SPoul-Henning Kamp 			sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
193c62aa65bSWarner Losh 			sc->pps[i].driver_abi = PPS_ABI_VERSION;
194c62aa65bSWarner Losh 			sc->pps[i].driver_mtx = ppb_get_lock(ppbus);
195d7e53105SWarner Losh 			d->si_drv1 = sc;
196a885bb60SJohn Baldwin 			d->si_drv2 = (void *)(intptr_t)i;
197c62aa65bSWarner Losh 			pps_init_abi(&sc->pps[i]);
198bd61b8e8SPoul-Henning Kamp 		}
1992067d312SJohn Baldwin 		ppb_lock(ppbus);
200bd61b8e8SPoul-Henning Kamp 	} while (0);
201bd61b8e8SPoul-Henning Kamp 	i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
202d7e53105SWarner Losh 	ppb_release_bus(ppbus, dev);
2032067d312SJohn Baldwin 	ppb_unlock(ppbus);
2040f210c92SNicolas Souchu 
2050f210c92SNicolas Souchu 	return (0);
206507e2e44SPoul-Henning Kamp }
207507e2e44SPoul-Henning Kamp 
208507e2e44SPoul-Henning Kamp static	int
20989c9c53dSPoul-Henning Kamp ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td)
210507e2e44SPoul-Henning Kamp {
211bd61b8e8SPoul-Henning Kamp 	struct pps_data *sc = dev->si_drv1;
2122067d312SJohn Baldwin 	device_t ppbus = sc->ppbus;
213a885bb60SJohn Baldwin 	int subdev = (intptr_t)dev->si_drv2;
2142067d312SJohn Baldwin 	int i;
215507e2e44SPoul-Henning Kamp 
2162067d312SJohn Baldwin 	/*
2172067d312SJohn Baldwin 	 * The sx lock is here solely to serialize open()'s to close
2182067d312SJohn Baldwin 	 * the race of concurrent open()'s when pps(4) doesn't own the
2192067d312SJohn Baldwin 	 * ppbus.
2202067d312SJohn Baldwin 	 */
2212067d312SJohn Baldwin 	sx_xlock(&sc->lock);
2222067d312SJohn Baldwin 	ppb_lock(ppbus);
223bd61b8e8SPoul-Henning Kamp 	if (!sc->busy) {
224bd61b8e8SPoul-Henning Kamp 		device_t ppsdev = sc->ppsdev;
225bd61b8e8SPoul-Henning Kamp 
2262067d312SJohn Baldwin 		if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) {
2272067d312SJohn Baldwin 			ppb_unlock(ppbus);
2282067d312SJohn Baldwin 			sx_xunlock(&sc->lock);
229507e2e44SPoul-Henning Kamp 			return (EINTR);
2300f210c92SNicolas Souchu 		}
2310f210c92SNicolas Souchu 
232bd61b8e8SPoul-Henning Kamp 		i = ppb_set_mode(sc->ppbus, PPB_PS2);
233d7e53105SWarner Losh 		PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
234507e2e44SPoul-Henning Kamp 
235bd61b8e8SPoul-Henning Kamp 		i = IRQENABLE | PCD | nINIT | SELECTIN;
236bd61b8e8SPoul-Henning Kamp 		ppb_wctr(ppbus, i);
237bd61b8e8SPoul-Henning Kamp 	}
238bd61b8e8SPoul-Henning Kamp 	if (subdev > 0 && !(sc->busy & ~1)) {
2392067d312SJohn Baldwin 		/* XXX: Timeout of 1?  hz/100 instead perhaps? */
2402067d312SJohn Baldwin 		callout_reset(&sc->timeout, 1, ppshcpoll, sc);
241bd61b8e8SPoul-Henning Kamp 		sc->lastdata = ppb_rdtr(sc->ppbus);
242bd61b8e8SPoul-Henning Kamp 	}
243bd61b8e8SPoul-Henning Kamp 	sc->busy |= (1 << subdev);
2442067d312SJohn Baldwin 	ppb_unlock(ppbus);
2452067d312SJohn Baldwin 	sx_xunlock(&sc->lock);
246507e2e44SPoul-Henning Kamp 	return(0);
247507e2e44SPoul-Henning Kamp }
248507e2e44SPoul-Henning Kamp 
249507e2e44SPoul-Henning Kamp static	int
25089c9c53dSPoul-Henning Kamp ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td)
251507e2e44SPoul-Henning Kamp {
252bd61b8e8SPoul-Henning Kamp 	struct pps_data *sc = dev->si_drv1;
253a885bb60SJohn Baldwin 	int subdev = (intptr_t)dev->si_drv2;
254507e2e44SPoul-Henning Kamp 
2552067d312SJohn Baldwin 	sx_xlock(&sc->lock);
256bd61b8e8SPoul-Henning Kamp 	sc->pps[subdev].ppsparam.mode = 0;	/* PHK ??? */
2572067d312SJohn Baldwin 	ppb_lock(sc->ppbus);
258bd61b8e8SPoul-Henning Kamp 	sc->busy &= ~(1 << subdev);
259bd61b8e8SPoul-Henning Kamp 	if (subdev > 0 && !(sc->busy & ~1))
2602067d312SJohn Baldwin 		callout_stop(&sc->timeout);
261bd61b8e8SPoul-Henning Kamp 	if (!sc->busy) {
262bd61b8e8SPoul-Henning Kamp 		device_t ppsdev = sc->ppsdev;
263bd61b8e8SPoul-Henning Kamp 		device_t ppbus = sc->ppbus;
26420240fa3SNicolas Souchu 
2650f210c92SNicolas Souchu 		ppb_wdtr(ppbus, 0);
2660f210c92SNicolas Souchu 		ppb_wctr(ppbus, 0);
26720240fa3SNicolas Souchu 
268bd61b8e8SPoul-Henning Kamp 		ppb_set_mode(ppbus, PPB_COMPATIBLE);
2690f210c92SNicolas Souchu 		ppb_release_bus(ppbus, ppsdev);
270bd61b8e8SPoul-Henning Kamp 	}
2712067d312SJohn Baldwin 	ppb_unlock(sc->ppbus);
2722067d312SJohn Baldwin 	sx_xunlock(&sc->lock);
273507e2e44SPoul-Henning Kamp 	return(0);
274507e2e44SPoul-Henning Kamp }
275507e2e44SPoul-Henning Kamp 
276507e2e44SPoul-Henning Kamp static void
277bd61b8e8SPoul-Henning Kamp ppshcpoll(void *arg)
278bd61b8e8SPoul-Henning Kamp {
279bd61b8e8SPoul-Henning Kamp 	struct pps_data *sc = arg;
280bd61b8e8SPoul-Henning Kamp 	int i, j, k, l;
281bd61b8e8SPoul-Henning Kamp 
2822067d312SJohn Baldwin 	KASSERT(sc->busy & ~1, ("pps polling w/o opened devices"));
283bd61b8e8SPoul-Henning Kamp 	i = ppb_rdtr(sc->ppbus);
284bd61b8e8SPoul-Henning Kamp 	if (i == sc->lastdata)
285bd61b8e8SPoul-Henning Kamp 		return;
286bd61b8e8SPoul-Henning Kamp 	l = sc->lastdata ^ i;
287bd61b8e8SPoul-Henning Kamp 	k = 1;
288bd61b8e8SPoul-Henning Kamp 	for (j = 1; j < 9; j ++) {
2897bf758bfSPoul-Henning Kamp 		if (l & k) {
2907bf758bfSPoul-Henning Kamp 			pps_capture(&sc->pps[j]);
2917bf758bfSPoul-Henning Kamp 			pps_event(&sc->pps[j],
2927bf758bfSPoul-Henning Kamp 			    i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
2937bf758bfSPoul-Henning Kamp 		}
294bd61b8e8SPoul-Henning Kamp 		k += k;
295bd61b8e8SPoul-Henning Kamp 	}
296bd61b8e8SPoul-Henning Kamp 	sc->lastdata = i;
2972067d312SJohn Baldwin 	callout_reset(&sc->timeout, 1, ppshcpoll, sc);
298bd61b8e8SPoul-Henning Kamp }
299bd61b8e8SPoul-Henning Kamp 
3002067d312SJohn Baldwin static void
3010f210c92SNicolas Souchu ppsintr(void *arg)
302507e2e44SPoul-Henning Kamp {
3033aabc159SWarner Losh 	struct pps_data *sc = (struct pps_data *)arg;
304507e2e44SPoul-Henning Kamp 
3052067d312SJohn Baldwin 	ppb_assert_locked(sc->ppbus);
3067bf758bfSPoul-Henning Kamp 	pps_capture(&sc->pps[0]);
3073aabc159SWarner Losh 	if (!(ppb_rstr(sc->ppbus) & nACK))
3082067d312SJohn Baldwin 		return;
3092067d312SJohn Baldwin 
310bd61b8e8SPoul-Henning Kamp 	if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
3113aabc159SWarner Losh 		ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED);
3127bf758bfSPoul-Henning Kamp 	pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
3133aabc159SWarner Losh 	if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
3143aabc159SWarner Losh 		ppb_wctr(sc->ppbus, IRQENABLE);
315507e2e44SPoul-Henning Kamp }
316507e2e44SPoul-Henning Kamp 
317507e2e44SPoul-Henning Kamp static int
31889c9c53dSPoul-Henning Kamp ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
319507e2e44SPoul-Henning Kamp {
320bd61b8e8SPoul-Henning Kamp 	struct pps_data *sc = dev->si_drv1;
321a885bb60SJohn Baldwin 	int subdev = (intptr_t)dev->si_drv2;
32276061701SWarner Losh 	int err;
323507e2e44SPoul-Henning Kamp 
3242067d312SJohn Baldwin 	ppb_lock(sc->ppbus);
32576061701SWarner Losh 	err = pps_ioctl(cmd, data, &sc->pps[subdev]);
3262067d312SJohn Baldwin 	ppb_unlock(sc->ppbus);
32776061701SWarner Losh 	return (err);
3288afeddf0SPoul-Henning Kamp }
329507e2e44SPoul-Henning Kamp 
3300f063508SPeter Wemm static device_method_t pps_methods[] = {
3310f063508SPeter Wemm 	/* device interface */
3320f063508SPeter Wemm 	DEVMETHOD(device_identify,	ppsidentify),
3330f063508SPeter Wemm 	DEVMETHOD(device_probe,		ppsprobe),
3340f063508SPeter Wemm 	DEVMETHOD(device_attach,	ppsattach),
3350f063508SPeter Wemm 	{ 0, 0 }
3360f063508SPeter Wemm };
3370f063508SPeter Wemm 
3380f063508SPeter Wemm static driver_t pps_driver = {
3390f063508SPeter Wemm 	PPS_NAME,
3400f063508SPeter Wemm 	pps_methods,
3410f063508SPeter Wemm 	sizeof(struct pps_data),
3420f063508SPeter Wemm };
3436d588090SJohn Baldwin 
3446d588090SJohn Baldwin DRIVER_MODULE(pps, ppbus, pps_driver, 0, 0);
345f5fd5611SRuslan Ermilov MODULE_DEPEND(pps, ppbus, 1, 1, 1);
346