Deleted Added
full compact
pps.c (108322) pps.c (111815)
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 *
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: head/sys/dev/ppbus/pps.c 108322 2002-12-27 16:34:12Z rwatson $
9 * $FreeBSD: head/sys/dev/ppbus/pps.c 111815 2003-03-03 12:15:54Z phk $
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/module.h>
22#include <sys/bus.h>
23#include <sys/conf.h>
24#include <sys/timepps.h>
25#include <machine/bus.h>
26#include <machine/resource.h>
27#include <sys/rman.h>
28
29#include <dev/ppbus/ppbconf.h>
30#include "ppbus_if.h"
31#include <dev/ppbus/ppbio.h>
32
33#define PPS_NAME "pps" /* our official name */
34
35#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg);
36
37struct pps_data {
38 struct ppb_device pps_dev;
39 struct pps_state pps[9];
40 dev_t devs[9];
41 device_t ppsdev;
42 device_t ppbus;
43 int busy;
44 struct callout_handle timeout;
45 int lastdata;
46
47 struct resource *intr_resource; /* interrupt resource */
48 void *intr_cookie; /* interrupt registration cookie */
49};
50
51static void ppsintr(void *arg);
52static void ppshcpoll(void *arg);
53
54#define DEVTOSOFTC(dev) \
55 ((struct pps_data *)device_get_softc(dev))
56
57static devclass_t pps_devclass;
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 = {
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/module.h>
22#include <sys/bus.h>
23#include <sys/conf.h>
24#include <sys/timepps.h>
25#include <machine/bus.h>
26#include <machine/resource.h>
27#include <sys/rman.h>
28
29#include <dev/ppbus/ppbconf.h>
30#include "ppbus_if.h"
31#include <dev/ppbus/ppbio.h>
32
33#define PPS_NAME "pps" /* our official name */
34
35#define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg);
36
37struct pps_data {
38 struct ppb_device pps_dev;
39 struct pps_state pps[9];
40 dev_t devs[9];
41 device_t ppsdev;
42 device_t ppbus;
43 int busy;
44 struct callout_handle timeout;
45 int lastdata;
46
47 struct resource *intr_resource; /* interrupt resource */
48 void *intr_cookie; /* interrupt registration cookie */
49};
50
51static void ppsintr(void *arg);
52static void ppshcpoll(void *arg);
53
54#define DEVTOSOFTC(dev) \
55 ((struct pps_data *)device_get_softc(dev))
56
57static devclass_t pps_devclass;
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 /* open */ ppsopen,
66 /* close */ ppsclose,
67 /* read */ noread,
68 /* write */ nowrite,
69 /* ioctl */ ppsioctl,
70 /* poll */ nopoll,
71 /* mmap */ nommap,
72 /* strategy */ nostrategy,
73 /* name */ PPS_NAME,
74 /* maj */ CDEV_MAJOR,
75 /* dump */ nodump,
76 /* psize */ nopsize,
77 /* flags */ 0,
65 .d_open = ppsopen,
66 .d_close = ppsclose,
67 .d_ioctl = ppsioctl,
68 .d_name = PPS_NAME,
69 .d_maj = CDEV_MAJOR,
78};
79
80static void
81ppsidentify(driver_t *driver, device_t parent)
82{
83
84 BUS_ADD_CHILD(parent, 0, PPS_NAME, -1);
85}
86
87static int
88ppstry(device_t ppbus, int send, int expect)
89{
90 int i;
91
92 ppb_wdtr(ppbus, send);
93 i = ppb_rdtr(ppbus);
94 PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
95 return (i != expect);
96}
97
98static int
99ppsprobe(device_t ppsdev)
100{
101 device_set_desc(ppsdev, "Pulse per second Timing Interface");
102
103 return (0);
104}
105
106static int
107ppsattach(device_t dev)
108{
109 struct pps_data *sc = DEVTOSOFTC(dev);
110 device_t ppbus = device_get_parent(dev);
111 dev_t d;
112 intptr_t irq;
113 int i, unit, zero = 0;
114
115 bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */
116
117 /* retrieve the ppbus irq */
118 BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
119
120 if (irq > 0) {
121 /* declare our interrupt handler */
122 sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
123 &zero, irq, irq, 1, RF_SHAREABLE);
124 }
125 /* interrupts seem mandatory */
126 if (sc->intr_resource == NULL)
127 return (ENXIO);
128
129 sc->ppsdev = dev;
130 sc->ppbus = ppbus;
131 unit = device_get_unit(ppbus);
132 d = make_dev(&pps_cdevsw, unit,
133 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit);
134 sc->devs[0] = d;
135 sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
136 d->si_drv1 = sc;
137 d->si_drv2 = (void*)0;
138 pps_init(&sc->pps[0]);
139
140 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT))
141 return (0);
142
143 do {
144 i = ppb_set_mode(sc->ppbus, PPB_EPP);
145 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
146 if (i == -1)
147 break;
148 i = 0;
149 ppb_wctr(ppbus, i);
150 if (ppstry(ppbus, 0x00, 0x00))
151 break;
152 if (ppstry(ppbus, 0x55, 0x55))
153 break;
154 if (ppstry(ppbus, 0xaa, 0xaa))
155 break;
156 if (ppstry(ppbus, 0xff, 0xff))
157 break;
158
159 i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
160 ppb_wctr(ppbus, i);
161 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
162 if (ppstry(ppbus, 0x00, 0x00))
163 break;
164 if (ppstry(ppbus, 0x55, 0x00))
165 break;
166 if (ppstry(ppbus, 0xaa, 0x00))
167 break;
168 if (ppstry(ppbus, 0xff, 0x00))
169 break;
170
171 i = IRQENABLE | PCD | nINIT | SELECTIN;
172 ppb_wctr(ppbus, i);
173 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
174 ppstry(ppbus, 0x00, 0xff);
175 ppstry(ppbus, 0x55, 0xff);
176 ppstry(ppbus, 0xaa, 0xff);
177 ppstry(ppbus, 0xff, 0xff);
178
179 for (i = 1; i < 9; i++) {
180 d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
181 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1);
182 sc->devs[i] = d;
183 sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
184 d->si_drv1 = sc;
185 d->si_drv2 = (void *)(intptr_t)i;
186 pps_init(&sc->pps[i]);
187 }
188 } while (0);
189 i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
190 ppb_release_bus(ppbus, dev);
191
192 return (0);
193}
194
195static int
196ppsopen(dev_t dev, int flags, int fmt, struct thread *td)
197{
198 struct pps_data *sc = dev->si_drv1;
199 int subdev = (intptr_t)dev->si_drv2;
200 int error, i;
201
202 if (!sc->busy) {
203 device_t ppsdev = sc->ppsdev;
204 device_t ppbus = sc->ppbus;
205
206 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
207 return (EINTR);
208
209 /* attach the interrupt handler */
210 if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource,
211 INTR_TYPE_TTY, ppsintr, ppsdev,
212 &sc->intr_cookie))) {
213 ppb_release_bus(ppbus, ppsdev);
214 return (error);
215 }
216
217 i = ppb_set_mode(sc->ppbus, PPB_PS2);
218 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
219
220 i = IRQENABLE | PCD | nINIT | SELECTIN;
221 ppb_wctr(ppbus, i);
222 }
223 if (subdev > 0 && !(sc->busy & ~1)) {
224 sc->timeout = timeout(ppshcpoll, sc, 1);
225 sc->lastdata = ppb_rdtr(sc->ppbus);
226 }
227 sc->busy |= (1 << subdev);
228 return(0);
229}
230
231static int
232ppsclose(dev_t dev, int flags, int fmt, struct thread *td)
233{
234 struct pps_data *sc = dev->si_drv1;
235 int subdev = (intptr_t)dev->si_drv2;
236
237 sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */
238 sc->busy &= ~(1 << subdev);
239 if (subdev > 0 && !(sc->busy & ~1))
240 untimeout(ppshcpoll, sc, sc->timeout);
241 if (!sc->busy) {
242 device_t ppsdev = sc->ppsdev;
243 device_t ppbus = sc->ppbus;
244
245 ppb_wdtr(ppbus, 0);
246 ppb_wctr(ppbus, 0);
247
248 /* Note: the interrupt handler is automatically detached */
249 ppb_set_mode(ppbus, PPB_COMPATIBLE);
250 ppb_release_bus(ppbus, ppsdev);
251 }
252 return(0);
253}
254
255static void
256ppshcpoll(void *arg)
257{
258 struct pps_data *sc = arg;
259 int i, j, k, l;
260
261 if (!(sc->busy & ~1))
262 return;
263 sc->timeout = timeout(ppshcpoll, sc, 1);
264 i = ppb_rdtr(sc->ppbus);
265 if (i == sc->lastdata)
266 return;
267 l = sc->lastdata ^ i;
268 k = 1;
269 for (j = 1; j < 9; j ++) {
270 if (l & k) {
271 pps_capture(&sc->pps[j]);
272 pps_event(&sc->pps[j],
273 i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
274 }
275 k += k;
276 }
277 sc->lastdata = i;
278}
279
280static void
281ppsintr(void *arg)
282{
283 device_t ppsdev = (device_t)arg;
284 struct pps_data *sc = DEVTOSOFTC(ppsdev);
285 device_t ppbus = sc->ppbus;
286
287 pps_capture(&sc->pps[0]);
288 if (!(ppb_rstr(ppbus) & nACK))
289 return;
290 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
291 ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
292 pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
293 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
294 ppb_wctr(ppbus, IRQENABLE);
295}
296
297static int
298ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
299{
300 struct pps_data *sc = dev->si_drv1;
301 int subdev = (intptr_t)dev->si_drv2;
302
303 return (pps_ioctl(cmd, data, &sc->pps[subdev]));
304}
305
306static device_method_t pps_methods[] = {
307 /* device interface */
308 DEVMETHOD(device_identify, ppsidentify),
309 DEVMETHOD(device_probe, ppsprobe),
310 DEVMETHOD(device_attach, ppsattach),
311
312 { 0, 0 }
313};
314
315static driver_t pps_driver = {
316 PPS_NAME,
317 pps_methods,
318 sizeof(struct pps_data),
319};
320DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);
70};
71
72static void
73ppsidentify(driver_t *driver, device_t parent)
74{
75
76 BUS_ADD_CHILD(parent, 0, PPS_NAME, -1);
77}
78
79static int
80ppstry(device_t ppbus, int send, int expect)
81{
82 int i;
83
84 ppb_wdtr(ppbus, send);
85 i = ppb_rdtr(ppbus);
86 PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i);
87 return (i != expect);
88}
89
90static int
91ppsprobe(device_t ppsdev)
92{
93 device_set_desc(ppsdev, "Pulse per second Timing Interface");
94
95 return (0);
96}
97
98static int
99ppsattach(device_t dev)
100{
101 struct pps_data *sc = DEVTOSOFTC(dev);
102 device_t ppbus = device_get_parent(dev);
103 dev_t d;
104 intptr_t irq;
105 int i, unit, zero = 0;
106
107 bzero(sc, sizeof(struct pps_data)); /* XXX doesn't newbus do this? */
108
109 /* retrieve the ppbus irq */
110 BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq);
111
112 if (irq > 0) {
113 /* declare our interrupt handler */
114 sc->intr_resource = bus_alloc_resource(dev, SYS_RES_IRQ,
115 &zero, irq, irq, 1, RF_SHAREABLE);
116 }
117 /* interrupts seem mandatory */
118 if (sc->intr_resource == NULL)
119 return (ENXIO);
120
121 sc->ppsdev = dev;
122 sc->ppbus = ppbus;
123 unit = device_get_unit(ppbus);
124 d = make_dev(&pps_cdevsw, unit,
125 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit);
126 sc->devs[0] = d;
127 sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
128 d->si_drv1 = sc;
129 d->si_drv2 = (void*)0;
130 pps_init(&sc->pps[0]);
131
132 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT))
133 return (0);
134
135 do {
136 i = ppb_set_mode(sc->ppbus, PPB_EPP);
137 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
138 if (i == -1)
139 break;
140 i = 0;
141 ppb_wctr(ppbus, i);
142 if (ppstry(ppbus, 0x00, 0x00))
143 break;
144 if (ppstry(ppbus, 0x55, 0x55))
145 break;
146 if (ppstry(ppbus, 0xaa, 0xaa))
147 break;
148 if (ppstry(ppbus, 0xff, 0xff))
149 break;
150
151 i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN;
152 ppb_wctr(ppbus, i);
153 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
154 if (ppstry(ppbus, 0x00, 0x00))
155 break;
156 if (ppstry(ppbus, 0x55, 0x00))
157 break;
158 if (ppstry(ppbus, 0xaa, 0x00))
159 break;
160 if (ppstry(ppbus, 0xff, 0x00))
161 break;
162
163 i = IRQENABLE | PCD | nINIT | SELECTIN;
164 ppb_wctr(ppbus, i);
165 PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i);
166 ppstry(ppbus, 0x00, 0xff);
167 ppstry(ppbus, 0x55, 0xff);
168 ppstry(ppbus, 0xaa, 0xff);
169 ppstry(ppbus, 0xff, 0xff);
170
171 for (i = 1; i < 9; i++) {
172 d = make_dev(&pps_cdevsw, unit + 0x10000 * i,
173 UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1);
174 sc->devs[i] = d;
175 sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
176 d->si_drv1 = sc;
177 d->si_drv2 = (void *)(intptr_t)i;
178 pps_init(&sc->pps[i]);
179 }
180 } while (0);
181 i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE);
182 ppb_release_bus(ppbus, dev);
183
184 return (0);
185}
186
187static int
188ppsopen(dev_t dev, int flags, int fmt, struct thread *td)
189{
190 struct pps_data *sc = dev->si_drv1;
191 int subdev = (intptr_t)dev->si_drv2;
192 int error, i;
193
194 if (!sc->busy) {
195 device_t ppsdev = sc->ppsdev;
196 device_t ppbus = sc->ppbus;
197
198 if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR))
199 return (EINTR);
200
201 /* attach the interrupt handler */
202 if ((error = BUS_SETUP_INTR(ppbus, ppsdev, sc->intr_resource,
203 INTR_TYPE_TTY, ppsintr, ppsdev,
204 &sc->intr_cookie))) {
205 ppb_release_bus(ppbus, ppsdev);
206 return (error);
207 }
208
209 i = ppb_set_mode(sc->ppbus, PPB_PS2);
210 PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus));
211
212 i = IRQENABLE | PCD | nINIT | SELECTIN;
213 ppb_wctr(ppbus, i);
214 }
215 if (subdev > 0 && !(sc->busy & ~1)) {
216 sc->timeout = timeout(ppshcpoll, sc, 1);
217 sc->lastdata = ppb_rdtr(sc->ppbus);
218 }
219 sc->busy |= (1 << subdev);
220 return(0);
221}
222
223static int
224ppsclose(dev_t dev, int flags, int fmt, struct thread *td)
225{
226 struct pps_data *sc = dev->si_drv1;
227 int subdev = (intptr_t)dev->si_drv2;
228
229 sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */
230 sc->busy &= ~(1 << subdev);
231 if (subdev > 0 && !(sc->busy & ~1))
232 untimeout(ppshcpoll, sc, sc->timeout);
233 if (!sc->busy) {
234 device_t ppsdev = sc->ppsdev;
235 device_t ppbus = sc->ppbus;
236
237 ppb_wdtr(ppbus, 0);
238 ppb_wctr(ppbus, 0);
239
240 /* Note: the interrupt handler is automatically detached */
241 ppb_set_mode(ppbus, PPB_COMPATIBLE);
242 ppb_release_bus(ppbus, ppsdev);
243 }
244 return(0);
245}
246
247static void
248ppshcpoll(void *arg)
249{
250 struct pps_data *sc = arg;
251 int i, j, k, l;
252
253 if (!(sc->busy & ~1))
254 return;
255 sc->timeout = timeout(ppshcpoll, sc, 1);
256 i = ppb_rdtr(sc->ppbus);
257 if (i == sc->lastdata)
258 return;
259 l = sc->lastdata ^ i;
260 k = 1;
261 for (j = 1; j < 9; j ++) {
262 if (l & k) {
263 pps_capture(&sc->pps[j]);
264 pps_event(&sc->pps[j],
265 i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
266 }
267 k += k;
268 }
269 sc->lastdata = i;
270}
271
272static void
273ppsintr(void *arg)
274{
275 device_t ppsdev = (device_t)arg;
276 struct pps_data *sc = DEVTOSOFTC(ppsdev);
277 device_t ppbus = sc->ppbus;
278
279 pps_capture(&sc->pps[0]);
280 if (!(ppb_rstr(ppbus) & nACK))
281 return;
282 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
283 ppb_wctr(ppbus, IRQENABLE | AUTOFEED);
284 pps_event(&sc->pps[0], PPS_CAPTUREASSERT);
285 if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT)
286 ppb_wctr(ppbus, IRQENABLE);
287}
288
289static int
290ppsioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
291{
292 struct pps_data *sc = dev->si_drv1;
293 int subdev = (intptr_t)dev->si_drv2;
294
295 return (pps_ioctl(cmd, data, &sc->pps[subdev]));
296}
297
298static device_method_t pps_methods[] = {
299 /* device interface */
300 DEVMETHOD(device_identify, ppsidentify),
301 DEVMETHOD(device_probe, ppsprobe),
302 DEVMETHOD(device_attach, ppsattach),
303
304 { 0, 0 }
305};
306
307static driver_t pps_driver = {
308 PPS_NAME,
309 pps_methods,
310 sizeof(struct pps_data),
311};
312DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0);