xref: /freebsd/sys/dev/ppbus/pps.c (revision 98edb3e17869504d0ada58932efa96b71f899181)
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.18 1999/05/30 16:51:36 phk 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 
35 static 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 
42 static int npps;
43 
44 /*
45  * Make ourselves visible as a ppbus driver
46  */
47 
48 static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
49 static int			ppsattach(struct ppb_device *dev);
50 static void			ppsintr(int unit);
51 
52 static struct ppb_driver ppsdriver = {
53     ppsprobe, ppsattach, PPS_NAME
54 };
55 
56 DATA_SET(ppbdriver_set, ppsdriver);
57 
58 static	d_open_t	ppsopen;
59 static	d_close_t	ppsclose;
60 static	d_ioctl_t	ppsioctl;
61 
62 #define CDEV_MAJOR 89
63 static 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 
86 static struct ppb_device *
87 ppsprobe(struct ppb_data *ppb)
88 {
89 	struct pps_data *sc;
90 	static int once;
91 
92 	if (!once++)
93 		cdevsw_add(&pps_cdevsw);
94 
95 	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
96 							M_TEMP, M_NOWAIT);
97 	if (!sc) {
98 		printf(PPS_NAME ": cannot malloc!\n");
99 		return (0);
100 	}
101 	bzero(sc, sizeof(struct pps_data));
102 
103 	softc[npps] = sc;
104 
105 	sc->pps_unit = npps++;
106 
107 	sc->pps_dev.id_unit = sc->pps_unit;
108 	sc->pps_dev.ppb = ppb;
109 	sc->pps_dev.name = ppsdriver.name;
110 	sc->pps_dev.intr = ppsintr;
111 
112 	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
113 	pps_init(&sc->pps);
114 	return (&sc->pps_dev);
115 }
116 
117 static int
118 ppsattach(struct ppb_device *dev)
119 {
120 
121 	/*
122 	 * Report ourselves
123 	 */
124 	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
125 	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
126 
127 #ifdef DEVFS
128 	devfs_add_devswf(&pps_cdevsw,
129 		dev->id_unit, DV_CHR,
130 		UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", dev->id_unit);
131 #endif
132 	return (1);
133 }
134 
135 static	int
136 ppsopen(dev_t dev, int flags, int fmt, struct proc *p)
137 {
138 	struct pps_data *sc;
139 	u_int unit = minor(dev);
140 
141 	if ((unit >= npps))
142 		return (ENXIO);
143 
144 	sc = softc[unit];
145 
146 	if (!sc->pps_open) {
147 		if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
148 			return (EINTR);
149 
150 		ppb_wctr(&sc->pps_dev, 0);
151 		ppb_wctr(&sc->pps_dev, IRQENABLE);
152 		sc->pps_open = 1;
153 	}
154 
155 	return(0);
156 }
157 
158 static	int
159 ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
160 {
161 	struct pps_data *sc = softc[minor(dev)];
162 
163 	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
164 
165 	ppb_wdtr(&sc->pps_dev, 0);
166 	ppb_wctr(&sc->pps_dev, 0);
167 
168 	ppb_release_bus(&sc->pps_dev);
169 	sc->pps_open = 0;
170 	return(0);
171 }
172 
173 static void
174 ppsintr(int unit)
175 {
176 	struct pps_data *sc = softc[unit];
177 	struct timecounter *tc;
178 	unsigned count;
179 
180 	tc = timecounter;
181 	count = timecounter->tc_get_timecount(tc);
182 	if (!(ppb_rstr(&sc->pps_dev) & nACK))
183 		return;
184 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
185 		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
186 	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
187 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
188 		ppb_wctr(&sc->pps_dev, IRQENABLE);
189 }
190 
191 static int
192 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
193 {
194 	struct pps_data *sc = softc[minor(dev)];
195 
196 	return (pps_ioctl(cmd, data, &sc->pps));
197 }
198 
199