pps.c revision 47625
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 * $Id: pps.c,v 1.17 1999/05/06 22:03:14 peter Exp $
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 "opt_devfs.h"
19
20#include <sys/param.h>
21#include <sys/kernel.h>
22#include <sys/systm.h>
23#include <sys/conf.h>
24#include <sys/timepps.h>
25#ifdef DEVFS
26#include <sys/devfsext.h>
27#endif
28#include <sys/malloc.h>
29
30#include <dev/ppbus/ppbconf.h>
31#include "pps.h"
32
33#define PPS_NAME	"lppps"		/* our official name */
34
35static struct pps_data {
36	int	pps_unit;
37	int	pps_open;
38	struct	ppb_device pps_dev;
39	struct	pps_state pps;
40} *softc[NPPS];
41
42static int npps;
43
44/*
45 * Make ourselves visible as a ppbus driver
46 */
47
48static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
49static int			ppsattach(struct ppb_device *dev);
50static void			ppsintr(int unit);
51
52static struct ppb_driver ppsdriver = {
53    ppsprobe, ppsattach, PPS_NAME
54};
55
56DATA_SET(ppbdriver_set, ppsdriver);
57
58static	d_open_t	ppsopen;
59static	d_close_t	ppsclose;
60static	d_ioctl_t	ppsioctl;
61
62#define CDEV_MAJOR 89
63static struct cdevsw pps_cdevsw = {
64	/* open */	ppsopen,
65	/* close */	ppsclose,
66	/* read */	noread,
67	/* write */	nowrite,
68	/* ioctl */	ppsioctl,
69	/* stop */	nostop,
70	/* reset */	noreset,
71	/* devtotty */	nodevtotty,
72	/* poll */	nopoll,
73	/* mmap */	nommap,
74	/* strategy */	nostrategy,
75	/* name */	PPS_NAME,
76	/* parms */	noparms,
77	/* maj */	CDEV_MAJOR,
78	/* dump */	nodump,
79	/* psize */	nopsize,
80	/* flags */	0,
81	/* maxio */	0,
82	/* bmaj */	-1
83};
84
85
86static struct ppb_device *
87ppsprobe(struct ppb_data *ppb)
88{
89	struct pps_data *sc;
90
91	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
92							M_TEMP, M_NOWAIT);
93	if (!sc) {
94		printf(PPS_NAME ": cannot malloc!\n");
95		return (0);
96	}
97	bzero(sc, sizeof(struct pps_data));
98
99	softc[npps] = sc;
100
101	sc->pps_unit = npps++;
102
103	sc->pps_dev.id_unit = sc->pps_unit;
104	sc->pps_dev.ppb = ppb;
105	sc->pps_dev.name = ppsdriver.name;
106	sc->pps_dev.intr = ppsintr;
107
108	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
109	pps_init(&sc->pps);
110	return (&sc->pps_dev);
111}
112
113static int
114ppsattach(struct ppb_device *dev)
115{
116	dev_t devt;
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#ifdef DEVFS
125	devfs_add_devswf(&pps_cdevsw,
126		dev->id_unit, DV_CHR,
127		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
128#endif
129	devt = makedev(CDEV_MAJOR, 0);
130	cdevsw_add(&devt, &pps_cdevsw, NULL);
131	return (1);
132}
133
134static	int
135ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
136{
137	struct pps_data *sc;
138	u_int unit = minor(dev);
139
140	if ((unit >= npps))
141		return (ENXIO);
142
143	sc = softc[unit];
144
145	if (!sc->pps_open) {
146		if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
147			return (EINTR);
148
149		ppb_wctr(&sc->pps_dev, 0);
150		ppb_wctr(&sc->pps_dev, IRQENABLE);
151		sc->pps_open = 1;
152	}
153
154	return(0);
155}
156
157static	int
158ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
159{
160	struct pps_data *sc = softc[minor(dev)];
161
162	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
163
164	ppb_wdtr(&sc->pps_dev, 0);
165	ppb_wctr(&sc->pps_dev, 0);
166
167	ppb_release_bus(&sc->pps_dev);
168	sc->pps_open = 0;
169	return(0);
170}
171
172static void
173ppsintr(int unit)
174{
175	struct pps_data *sc = softc[unit];
176	struct timecounter *tc;
177	unsigned count;
178
179	tc = timecounter;
180	count = timecounter->tc_get_timecount(tc);
181	if (!(ppb_rstr(&sc->pps_dev) & nACK))
182		return;
183	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
184		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
185	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
186	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
187		ppb_wctr(&sc->pps_dev, IRQENABLE);
188}
189
190static int
191ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
192{
193	struct pps_data *sc = softc[minor(dev)];
194
195	return (pps_ioctl(cmd, data, &sc->pps));
196}
197
198