pps.c revision 50477
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sys/dev/ppbus/pps.c 50477 1999-08-28 01:08:13Z peter $
10 *
11 * This driver implements a draft-mogul-pps-api-02.txt PPS source.
12 *
13 * The input pin is pin#10
14 * The echo output pin is pin#14
15 *
16 */
17
18#include <sys/param.h>
19#include <sys/kernel.h>
20#include <sys/systm.h>
21#include <sys/conf.h>
22#include <sys/timepps.h>
23#include <sys/malloc.h>
24
25#include <dev/ppbus/ppbconf.h>
26#include "pps.h"
27
28#define PPS_NAME	"pps"		/* our official name */
29
30struct pps_data {
31	int	pps_open;
32	struct	ppb_device pps_dev;
33	struct	pps_state pps;
34};
35
36static int npps;
37
38/*
39 * Make ourselves visible as a ppbus driver
40 */
41
42static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
43static int			ppsattach(struct ppb_device *dev);
44static void			ppsintr(struct ppb_device *ppd);
45
46static struct ppb_driver ppsdriver = {
47    ppsprobe, ppsattach, PPS_NAME
48};
49
50DATA_SET(ppbdriver_set, ppsdriver);
51
52static	d_open_t	ppsopen;
53static	d_close_t	ppsclose;
54static	d_ioctl_t	ppsioctl;
55
56#define CDEV_MAJOR 89
57static struct cdevsw pps_cdevsw = {
58	/* open */	ppsopen,
59	/* close */	ppsclose,
60	/* read */	noread,
61	/* write */	nowrite,
62	/* ioctl */	ppsioctl,
63	/* stop */	nostop,
64	/* reset */	noreset,
65	/* devtotty */	nodevtotty,
66	/* poll */	nopoll,
67	/* mmap */	nommap,
68	/* strategy */	nostrategy,
69	/* name */	PPS_NAME,
70	/* parms */	noparms,
71	/* maj */	CDEV_MAJOR,
72	/* dump */	nodump,
73	/* psize */	nopsize,
74	/* flags */	0,
75	/* maxio */	0,
76	/* bmaj */	-1
77};
78
79
80static struct ppb_device *
81ppsprobe(struct ppb_data *ppb)
82{
83	struct pps_data *sc;
84	static int once;
85	dev_t dev;
86
87	if (!once++)
88		cdevsw_add(&pps_cdevsw);
89
90	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
91							M_TEMP, M_NOWAIT);
92	if (!sc) {
93		printf(PPS_NAME ": cannot malloc!\n");
94		return (0);
95	}
96	bzero(sc, sizeof(struct pps_data));
97
98	dev = make_dev(&pps_cdevsw, npps,
99	    UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", npps);
100
101	dev->si_drv1 = sc;
102
103	sc->pps_dev.id_unit = npps++;
104	sc->pps_dev.ppb = ppb;
105	sc->pps_dev.name = ppsdriver.name;
106	sc->pps_dev.bintr = ppsintr;
107	sc->pps_dev.drv1 = sc;
108
109	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
110	pps_init(&sc->pps);
111	return (&sc->pps_dev);
112}
113
114static int
115ppsattach(struct ppb_device *dev)
116{
117
118	/*
119	 * Report ourselves
120	 */
121	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
122	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
123
124	return (1);
125}
126
127static	int
128ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
129{
130	struct pps_data *sc;
131	u_int unit = minor(dev);
132
133	if ((unit >= npps))
134		return (ENXIO);
135
136	sc = dev->si_drv1;
137
138	if (!sc->pps_open) {
139		if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
140			return (EINTR);
141
142		ppb_wctr(&sc->pps_dev, 0);
143		ppb_wctr(&sc->pps_dev, IRQENABLE);
144		sc->pps_open = 1;
145	}
146
147	return(0);
148}
149
150static	int
151ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
152{
153	struct pps_data *sc = dev->si_drv1;
154
155	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
156
157	ppb_wdtr(&sc->pps_dev, 0);
158	ppb_wctr(&sc->pps_dev, 0);
159
160	ppb_release_bus(&sc->pps_dev);
161	sc->pps_open = 0;
162	return(0);
163}
164
165static void
166ppsintr(struct ppb_device *ppd)
167{
168	struct pps_data *sc = ppd->drv1;
169	struct timecounter *tc;
170	unsigned count;
171
172	tc = timecounter;
173	count = timecounter->tc_get_timecount(tc);
174	if (!(ppb_rstr(&sc->pps_dev) & nACK))
175		return;
176	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
177		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
178	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
179	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
180		ppb_wctr(&sc->pps_dev, IRQENABLE);
181}
182
183static int
184ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
185{
186	struct pps_data *sc = dev->si_drv1;
187
188	return (pps_ioctl(cmd, data, &sc->pps));
189}
190
191