pps.c revision 44666
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.13 1999/01/30 15:35:39 nsouch 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	struct	ppb_device pps_dev;
38	struct	pps_state pps;
39} *softc[NPPS];
40
41static int npps;
42static pps_devsw_installed = 0;
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);
51static void			pps_drvinit(void *unused);
52
53static struct ppb_driver ppsdriver = {
54    ppsprobe, ppsattach, PPS_NAME
55};
56
57DATA_SET(ppbdriver_set, ppsdriver);
58
59static	d_open_t	ppsopen;
60static	d_close_t	ppsclose;
61static	d_ioctl_t	ppsioctl;
62
63#define CDEV_MAJOR 89
64static struct cdevsw pps_cdevsw =
65	{ ppsopen,	ppsclose,	noread,		nowrite,
66	  ppsioctl,	nullstop,	nullreset,	nodevtotty,
67	  seltrue,	nommap,		nostrat,	PPS_NAME,
68	  NULL,		-1 };
69
70
71static struct ppb_device *
72ppsprobe(struct ppb_data *ppb)
73{
74	struct pps_data *sc;
75
76	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
77							M_TEMP, M_NOWAIT);
78	if (!sc) {
79		printf(PPS_NAME ": cannot malloc!\n");
80		return (0);
81	}
82	bzero(sc, sizeof(struct pps_data));
83
84	softc[npps] = sc;
85
86	sc->pps_unit = npps++;
87
88	sc->pps_dev.id_unit = sc->pps_unit;
89	sc->pps_dev.ppb = ppb;
90	sc->pps_dev.name = ppsdriver.name;
91	sc->pps_dev.intr = ppsintr;
92
93	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
94	pps_init(&sc->pps);
95	return (&sc->pps_dev);
96}
97
98static int
99ppsattach(struct ppb_device *dev)
100{
101	dev_t devt;
102
103	/*
104	 * Report ourselves
105	 */
106	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
107	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
108
109#ifdef DEVFS
110	devfs_add_devswf(&pps_cdevsw,
111		dev->id_unit, DV_CHR,
112		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
113#endif
114	if( ! pps_devsw_installed ) {
115		devt = makedev(CDEV_MAJOR, 0);
116		cdevsw_add(&devt, &pps_cdevsw, NULL);
117		pps_devsw_installed = 1;
118    	}
119	return (1);
120}
121
122static	int
123ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
124{
125	struct pps_data *sc;
126	u_int unit = minor(dev);
127
128	if ((unit >= npps))
129		return (ENXIO);
130
131	sc = softc[unit];
132
133	if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
134		return (EINTR);
135
136	ppb_wctr(&sc->pps_dev, 0);
137	ppb_wctr(&sc->pps_dev, IRQENABLE);
138
139	return(0);
140}
141
142static	int
143ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
144{
145	struct pps_data *sc = softc[minor(dev)];
146
147	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
148
149	ppb_wdtr(&sc->pps_dev, 0);
150	ppb_wctr(&sc->pps_dev, 0);
151
152	ppb_release_bus(&sc->pps_dev);
153	return(0);
154}
155
156static void
157ppsintr(int unit)
158{
159	struct pps_data *sc = softc[unit];
160	struct timecounter *tc;
161	unsigned count;
162
163	tc = timecounter;
164	count = timecounter->tc_get_timecount(tc);
165	if (!(ppb_rstr(&sc->pps_dev) & nACK))
166		return;
167	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
168		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
169	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
170	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
171		ppb_wctr(&sc->pps_dev, IRQENABLE);
172}
173
174static int
175ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
176{
177	struct pps_data *sc = softc[minor(dev)];
178
179	return (pps_ioctl(cmd, data, &sc->pps));
180}
181
182