xref: /freebsd/sys/dev/ppbus/pps.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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 	/* poll */	nopoll,
64 	/* mmap */	nommap,
65 	/* strategy */	nostrategy,
66 	/* name */	PPS_NAME,
67 	/* maj */	CDEV_MAJOR,
68 	/* dump */	nodump,
69 	/* psize */	nopsize,
70 	/* flags */	0,
71 	/* bmaj */	-1
72 };
73 
74 
75 static struct ppb_device *
76 ppsprobe(struct ppb_data *ppb)
77 {
78 	struct pps_data *sc;
79 	static int once;
80 	dev_t dev;
81 
82 	if (!once++)
83 		cdevsw_add(&pps_cdevsw);
84 
85 	sc = (struct pps_data *) malloc(sizeof(struct pps_data),
86 							M_TEMP, M_NOWAIT);
87 	if (!sc) {
88 		printf(PPS_NAME ": cannot malloc!\n");
89 		return (0);
90 	}
91 	bzero(sc, sizeof(struct pps_data));
92 
93 	dev = make_dev(&pps_cdevsw, npps,
94 	    UID_ROOT, GID_WHEEL, 0644, PPS_NAME "%d", npps);
95 
96 	dev->si_drv1 = sc;
97 
98 	sc->pps_dev.id_unit = npps++;
99 	sc->pps_dev.ppb = ppb;
100 	sc->pps_dev.name = ppsdriver.name;
101 	sc->pps_dev.bintr = ppsintr;
102 	sc->pps_dev.drv1 = sc;
103 
104 	sc->pps.ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
105 	pps_init(&sc->pps);
106 	return (&sc->pps_dev);
107 }
108 
109 static int
110 ppsattach(struct ppb_device *dev)
111 {
112 
113 	/*
114 	 * Report ourselves
115 	 */
116 	printf(PPS_NAME "%d: <Pulse per second Timing Interface> on ppbus %d\n",
117 	       dev->id_unit, dev->ppb->ppb_link->adapter_unit);
118 
119 	return (1);
120 }
121 
122 static	int
123 ppsopen(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 = dev->si_drv1;
132 
133 	if (!sc->pps_open) {
134 		if (ppb_request_bus(&sc->pps_dev, PPB_WAIT|PPB_INTR))
135 			return (EINTR);
136 
137 		ppb_wctr(&sc->pps_dev, 0);
138 		ppb_wctr(&sc->pps_dev, IRQENABLE);
139 		sc->pps_open = 1;
140 	}
141 
142 	return(0);
143 }
144 
145 static	int
146 ppsclose(dev_t dev, int flags, int fmt, struct proc *p)
147 {
148 	struct pps_data *sc = dev->si_drv1;
149 
150 	sc->pps.ppsparam.mode = 0;	/* PHK ??? */
151 
152 	ppb_wdtr(&sc->pps_dev, 0);
153 	ppb_wctr(&sc->pps_dev, 0);
154 
155 	ppb_release_bus(&sc->pps_dev);
156 	sc->pps_open = 0;
157 	return(0);
158 }
159 
160 static void
161 ppsintr(struct ppb_device *ppd)
162 {
163 	struct pps_data *sc = ppd->drv1;
164 	struct timecounter *tc;
165 	unsigned count;
166 
167 	tc = timecounter;
168 	count = timecounter->tc_get_timecount(tc);
169 	if (!(ppb_rstr(&sc->pps_dev) & nACK))
170 		return;
171 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
172 		ppb_wctr(&sc->pps_dev, IRQENABLE | AUTOFEED);
173 	pps_event(&sc->pps, tc, count, PPS_CAPTUREASSERT);
174 	if (sc->pps.ppsparam.mode & PPS_ECHOASSERT)
175 		ppb_wctr(&sc->pps_dev, IRQENABLE);
176 }
177 
178 static int
179 ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
180 {
181 	struct pps_data *sc = dev->si_drv1;
182 
183 	return (pps_ioctl(cmd, data, &sc->pps));
184 }
185 
186