xref: /freebsd/sys/dev/ppbus/pps.c (revision a1a4f1a0d87b594d3f17a97dc0127eec1417e6f6)
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$
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 
30 struct pps_data {
31 	int	pps_open;
32 	struct	ppb_device pps_dev;
33 	struct	pps_state pps;
34 };
35 
36 static int npps;
37 
38 /*
39  * Make ourselves visible as a ppbus driver
40  */
41 
42 static struct ppb_device	*ppsprobe(struct ppb_data *ppb);
43 static int			ppsattach(struct ppb_device *dev);
44 static void			ppsintr(struct ppb_device *ppd);
45 
46 static struct ppb_driver ppsdriver = {
47     ppsprobe, ppsattach, PPS_NAME
48 };
49 
50 DATA_SET(ppbdriver_set, ppsdriver);
51 
52 static	d_open_t	ppsopen;
53 static	d_close_t	ppsclose;
54 static	d_ioctl_t	ppsioctl;
55 
56 #define CDEV_MAJOR 89
57 static 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 
80 static struct ppb_device *
81 ppsprobe(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 
114 static int
115 ppsattach(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 
127 static	int
128 ppsopen(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 
150 static	int
151 ppsclose(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 
165 static void
166 ppsintr(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 
183 static int
184 ppsioctl(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