FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/loran.c
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 *
10 * This device-driver helps the userland controlprogram for a LORAN-C
11 * receiver avoid monopolizing the CPU.
12 *
13 * This is clearly a candidate for the "most weird hardware support in
14 * FreeBSD" prize. At this time only two copies of the receiver are
15 * known to exist in the entire world.
16 *
17 * Details can be found at:
18 * ftp://ftp.eecis.udel.edu/pub/ntp/loran.tar.Z
19 */
20
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD: releng/5.2/sys/i386/isa/loran.c 115703 2003-06-02 16:32:55Z obrien $");
23
24 #ifdef _KERNEL
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/sysctl.h>
28 #include <sys/conf.h>
29 #include <sys/kernel.h>
30 #include <sys/uio.h>
31 #include <sys/bus.h>
32 #include <sys/malloc.h>
33 #include <sys/timetc.h>
34
35 #include <i386/isa/isa_device.h>
36 #endif /* _KERNEL */
37
38 typedef TAILQ_HEAD(, datapoint) dphead_t;
39
40 struct datapoint {
41 /* Fields used by kernel */
42 u_int64_t scheduled;
43 u_int code;
44 u_int fri;
45 u_int agc;
46 u_int phase;
47 u_int width;
48 u_int par;
49 u_int isig;
50 u_int qsig;
51 u_int ssig;
52 u_int64_t epoch;
53 TAILQ_ENTRY(datapoint) list;
54 int vco;
55 int bounce;
56 pid_t pid;
57 struct timespec when;
58
59 int priority;
60 dphead_t *home;
61
62 /* Fields used only in userland */
63 void (*proc)(struct datapoint *);
64 void *ident;
65 int index;
66 char *name;
67
68
69 /* Fields used only in userland */
70 double ival;
71 double qval;
72 double sval;
73 double mval;
74
75 };
76
77 /*
78 * Mode register (PAR) hardware definitions
79 */
80 #define INTEG 0x03 /* integrator mask */
81 #define INTEG_1000us 0
82 #define INTEG_264us 1
83 #define INTEG_36us 2
84 #define INTEG_SHORT 3
85 #define GATE 0x0C /* gate source mask */
86 #define GATE_OPEN 0x0
87 #define GATE_GRI 0x4
88 #define GATE_PCI 0x8
89 #define GATE_STB 0xc
90 #define MSB 0x10 /* load dac high-order bits */
91 #define IEN 0x20 /* enable interrupt bit */
92 #define EN5 0x40 /* enable counter 5 bit */
93 #define ENG 0x80 /* enable gri bit */
94
95 #define VCO_SHIFT 8 /* bits of fraction on VCO value */
96 #define VCO (2048 << VCO_SHIFT) /* initial vco dac (0 V)*/
97
98
99 #define PGUARD 990 /* program guard time (cycle) (990!) */
100
101 #ifdef _KERNEL
102
103 #define NLORAN 10 /* Allow ten minor devices */
104
105 #define NDUMMY 4 /* How many idlers we want */
106
107 #define PORT 0x0300 /* controller port address */
108
109
110 #define GRI 800 /* pulse-group gate (cycle) */
111
112 /*
113 * Analog/digital converter (ADC) hardware definitions
114 */
115 #define ADC PORT+2 /* adc buffer (r)/address (w) */
116 #define ADCGO PORT+3 /* adc status (r)/adc start (w) */
117 #define ADC_START 0x01 /* converter start bit (w) */
118 #define ADC_BUSY 0x01 /* converter busy bit (r) */
119 #define ADC_DONE 0x80 /* converter done bit (r) */
120 #define ADC_I 0 /* i channel (phase) */
121 #define ADC_Q 1 /* q channel (amplitude) */
122 #define ADC_S 2 /* s channel (agc) */
123
124 /*
125 * Digital/analog converter (DAC) hardware definitions
126 * Note: output voltage increases with value programmed; the buffer
127 * is loaded in two 8-bit bytes, the lsb 8 bits with the MSB bit off in
128 * the PAR register, the msb 4 bits with the MSB on.
129 */
130 #define DACA PORT+4 /* vco (dac a) buffer (w) */
131 #define DACB PORT+5 /* agc (dac b) buffer (w) */
132
133 #define LOAD_DAC(dac, val) if (0) { } else { \
134 par &= ~MSB; outb(PAR, par); outb((dac), (val) & 0xff); \
135 par |= MSB; outb(PAR, par); outb((dac), ((val) >> 8) & 0xff); \
136 }
137
138 /*
139 * Pulse-code generator (CODE) hardware definitions
140 * Note: bits are shifted out from the lsb first
141 */
142 #define CODE PORT+6 /* pulse-code buffer (w) */
143 #define MPCA 0xCA /* LORAN-C master pulse code group a */
144 #define MPCB 0x9F /* LORAN-C master pulse code group b */
145 #define SPCA 0xF9 /* LORAN-C slave pulse code group a */
146 #define SPCB 0xAC /* LORAN-C slave pulse code group b */
147
148 /*
149 * Mode register (PAR) hardware definitions
150 */
151 #define PAR PORT+7 /* parameter buffer (w) */
152
153 #define TGC PORT+0 /* stc control port (r/w) */
154 #define TGD PORT+1 /* stc data port (r/w) */
155
156 /*
157 * Timing generator (STC) hardware commands
158 */
159 /* argument sssss = counter numbers 5-1 */
160 #define TG_LOADDP 0x00 /* load data pointer */
161 /* argument ee = element (all groups except ggg = 000 or 111) */
162 #define MODEREG 0x00 /* mode register */
163 #define LOADREG 0x08 /* load register */
164 #define HOLDREG 0x10 /* hold register */
165 #define HOLDINC 0x18 /* hold register (hold cycle increm) */
166 /* argument ee = element (group ggg = 111) */
167 #define ALARM1 0x07 /* alarm register 1 */
168 #define ALARM2 0x0F /* alarm register 2 */
169 #define MASTER 0x17 /* master mode register */
170 #define STATUS 0x1F /* status register */
171 #define ARM 0x20 /* arm counters */
172 #define LOAD 0x40 /* load counters */
173 #define TG_LOADARM 0x60 /* load and arm counters */
174 #define DISSAVE 0x80 /* disarm and save counters */
175 #define TG_SAVE 0xA0 /* save counters */
176 #define DISARM 0xC0 /* disarm counters */
177 /* argument nnn = counter number */
178 #define SETTOG 0xE8 /* set toggle output HIGH for counter */
179 #define CLRTOG 0xE0 /* set toggle output LOW for counter */
180 #define STEP 0xF0 /* step counter */
181 /* argument eeggg, where ee = element, ggg - counter group */
182 /* no arguments */
183 #define ENABDPS 0xE0 /* enable data pointer sequencing */
184 #define ENABFOUT 0xE6 /* enable fout */
185 #define ENAB8 0xE7 /* enable 8-bit data bus */
186 #define DSABDPS 0xE8 /* disable data pointer sequencing */
187 #define ENAB16 0xEF /* enable 16-bit data bus */
188 #define DSABFOUT 0xEE /* disable fout */
189 #define ENABPFW 0xF8 /* enable prefetch for write */
190 #define DSABPFW 0xF9 /* disable prefetch for write */
191 #define TG_RESET 0xFF /* master reset */
192
193 #define LOAD_9513(index, val) if (0) {} else { \
194 outb(TGC, TG_LOADDP + (index)); \
195 outb(TGD, (val) & 0xff); \
196 outb(TGD, ((val) >> 8) & 0xff); \
197 }
198
199 #define NENV 40 /* size of envelope filter */
200 #define CLOCK 50 /* clock period (clock) */
201 #define CYCLE 10 /* carrier period (us) */
202 #define PCX (NENV * CLOCK) /* envelope gate (clock) */
203 #define STROBE 50 /* strobe gate (clock) */
204
205 /**********************************************************************/
206
207 extern struct cdevsw loran_cdevsw;
208
209 static dphead_t minors[NLORAN + 1], working;
210
211 static struct datapoint dummy[NDUMMY], *first, *second;
212
213 static u_int64_t ticker;
214
215 static u_char par;
216
217 static MALLOC_DEFINE(M_LORAN, "Loran", "Loran datapoints");
218
219 static int loranerror;
220 static char lorantext[160];
221
222 static u_int vco_is;
223 static u_int vco_should;
224 static u_int vco_want;
225 static u_int64_t vco_when;
226 static int64_t vco_error;
227
228 /**********************************************************************/
229
230 static int loranprobe (struct isa_device *dvp);
231 static void init_tgc (void);
232 static int loranattach (struct isa_device *isdp);
233 static void loranenqueue (struct datapoint *);
234 static d_open_t loranopen;
235 static d_close_t loranclose;
236 static d_read_t loranread;
237 static d_write_t loranwrite;
238 static ointhand2_t loranintr;
239 extern struct timecounter loran_timecounter;
240
241 /**********************************************************************/
242
243 static int
244 loranprobe(struct isa_device *dvp)
245 {
246
247 dvp->id_iobase = PORT;
248 return (8);
249 }
250
251 static u_short tg_init[] = { /* stc initialization vector */
252 0x0562, 12, 13, /* counter 1 (p0) Mode J */
253 0x0262, PGUARD, GRI, /* counter 2 (gri) Mode J */
254 0x8562, PCX, 5000 - PCX, /* counter 3 (pcx) */
255 0xc562, 0, STROBE, /* counter 4 (stb) Mode L */
256 0x052a, 0, 0 /* counter 5 (out) */
257 };
258
259 static void
260 init_tgc(void)
261 {
262 int i;
263
264 /* Initialize the 9513A */
265 outb(TGC, TG_RESET); outb(TGC, LOAD+0x1f); /* reset STC chip */
266 LOAD_9513(MASTER, 0x8af0);
267 outb(TGC, TG_LOADDP+1);
268 tg_init[4] = 7499 - GRI;
269 for (i = 0; i < 5*3; i++) {
270 outb(TGD, tg_init[i]);
271 outb(TGD, tg_init[i] >> 8);
272 }
273 outb(TGC, TG_LOADARM+0x1f); /* let the good times roll */
274 }
275
276 static int
277 loranattach(struct isa_device *isdp)
278 {
279 int i;
280
281 isdp->id_ointr = loranintr;
282
283 /* We need to be a "fast-intr" */
284 /* isdp->id_ri_flags |= RI_FAST; XXX unimplemented - use newbus! */
285
286 printf("loran0: LORAN-C Receiver\n");
287
288 vco_want = vco_should = VCO;
289 vco_is = vco_should >> VCO_SHIFT;
290 LOAD_DAC(DACA, vco_is);
291
292 init_tgc();
293
294 tc_init(&loran_timecounter);
295
296 TAILQ_INIT(&working);
297 for (i = 0; i < NLORAN + 1; i++) {
298 TAILQ_INIT(&minors[i]);
299 make_dev(&loran_cdevsw, i, UID_ROOT, GID_WHEEL, 0600, "loran%d", i);
300 }
301
302 for (i = 0; i < NDUMMY; i++) {
303 dummy[i].agc = 4095;
304 dummy[i].code = 0xac;
305 dummy[i].fri = PGUARD;
306 dummy[i].scheduled = PGUARD * 2 * i;
307 dummy[i].phase = 50;
308 dummy[i].width = 50;
309 dummy[i].priority = NLORAN * 256;
310 dummy[i].home = &minors[NLORAN];
311 if (i == 0)
312 first = &dummy[i];
313 else if (i == 1)
314 second = &dummy[i];
315 else
316 TAILQ_INSERT_TAIL(&working, &dummy[i], list);
317 }
318
319 inb(ADC); /* Flush any old result */
320 outb(ADC, ADC_S);
321
322 par = ENG|IEN;
323 outb(PAR, par);
324
325 return (1);
326 }
327
328 static int
329 loranopen (dev_t dev, int flags, int fmt, struct thread *td)
330 {
331 int idx;
332
333 idx = minor(dev);
334 if (idx >= NLORAN)
335 return (ENODEV);
336
337 return(0);
338 }
339
340 static int
341 loranclose(dev_t dev, int flags, int fmt, struct thread *td)
342 {
343 return(0);
344 }
345
346 static int
347 loranread(dev_t dev, struct uio * uio, int ioflag)
348 {
349 u_long ef;
350 struct datapoint *this;
351 int err, c;
352 int idx;
353
354 idx = minor(dev);
355
356 if (loranerror) {
357 printf("Loran0: %s", lorantext);
358 loranerror = 0;
359 return(EIO);
360 }
361 if (TAILQ_EMPTY(&minors[idx]))
362 tsleep (&minors[idx], (PZERO + 8) |PCATCH, "loranrd", hz*2);
363 if (TAILQ_EMPTY(&minors[idx]))
364 return(0);
365 this = TAILQ_FIRST(&minors[idx]);
366 ef = read_eflags();
367 disable_intr();
368 TAILQ_REMOVE(&minors[idx], this, list);
369 write_eflags(ef);
370
371 c = imin(uio->uio_resid, (int)sizeof *this);
372 err = uiomove((caddr_t)this, c, uio);
373 FREE(this, M_LORAN);
374 return(err);
375 }
376
377 static void
378 loranenqueue(struct datapoint *dp)
379 {
380 struct datapoint *dpp;
381
382 TAILQ_FOREACH(dpp, &working, list) {
383 if (dpp->priority <= dp->priority)
384 continue;
385 TAILQ_INSERT_BEFORE(dpp, dp, list);
386 return;
387 }
388 TAILQ_INSERT_TAIL(&working, dp, list);
389 }
390
391 static int
392 loranwrite(dev_t dev, struct uio * uio, int ioflag)
393 {
394 u_long ef;
395 int err = 0, c;
396 struct datapoint *this;
397 int idx;
398 u_int64_t dt;
399 u_int64_t when;
400
401 idx = minor(dev);
402
403 MALLOC(this, struct datapoint *, sizeof *this, M_LORAN, M_WAITOK);
404 c = imin(uio->uio_resid, (int)sizeof *this);
405 err = uiomove((caddr_t)this, c, uio);
406 if (err) {
407 FREE(this, M_LORAN);
408 return (err);
409 }
410 if (this->fri == 0) {
411 FREE(this, M_LORAN);
412 return (EINVAL);
413 }
414 this->par &= INTEG|GATE;
415 /* XXX more checks needed! */
416 this->home = &minors[idx];
417 this->priority &= 0xff;
418 this->priority += idx * 256;
419 this->bounce = 0;
420 when = second->scheduled + PGUARD;
421 if (when > this->scheduled) {
422 dt = when - this->scheduled;
423 dt -= dt % this->fri;
424 this->scheduled += dt;
425 }
426 ef = read_eflags();
427 disable_intr();
428 loranenqueue(this);
429 write_eflags(ef);
430 if (this->vco >= 0)
431 vco_want = this->vco;
432 return(err);
433 }
434
435 static void
436 loranintr(int unit)
437 {
438 u_long ef;
439 int status = 0, i;
440 #if 0
441 int count = 0;
442 #endif
443 int delay;
444 u_int64_t when;
445 struct timespec there, then;
446 struct datapoint *dp, *done;
447
448 ef = read_eflags();
449 disable_intr();
450
451 /*
452 * Pick up the measurement which just completed, and setup
453 * the next measurement. We have 1100 microseconds for this
454 * of which some eaten by the A/D of the S channel and the
455 * interrupt to get us here.
456 */
457
458 done = first;
459
460 nanotime(&there);
461 done->ssig = inb(ADC);
462
463 par &= ~(ENG | IEN);
464 outb(PAR, par);
465
466 outb(ADC, ADC_I);
467 outb(ADCGO, ADC_START);
468
469 /* Interlude: while we wait: setup the next measurement */
470 LOAD_DAC(DACB, second->agc);
471 outb(CODE, second->code);
472 par &= ~(INTEG|GATE);
473 par |= second->par;
474 par |= ENG | IEN;
475
476 while (!(inb(ADCGO) & ADC_DONE))
477 continue;
478 done->isig = inb(ADC);
479
480 outb(ADC, ADC_Q);
481 outb(ADCGO, ADC_START);
482 /* Interlude: while we wait: setup the next measurement */
483 /*
484 * We need to load this from the opposite register due to some
485 * weirdness which you can read about in in the 9513 manual on
486 * page 1-26 under "LOAD"
487 */
488 LOAD_9513(0x0c, second->phase);
489 LOAD_9513(0x14, second->phase);
490 outb(TGC, TG_LOADARM + 0x08);
491 LOAD_9513(0x14, second->width);
492 while (!(inb(ADCGO) & ADC_DONE))
493 continue;
494 done->qsig = inb(ADC);
495
496 outb(ADC, ADC_S);
497
498 outb(PAR, par);
499
500 /*
501 * End of VERY time critical stuff, we have 8 msec to find
502 * the next measurement and program the delay.
503 */
504 status = inb(TGC);
505 nanotime(&then);
506
507 first = second;
508 second = 0;
509 when = first->scheduled + PGUARD;
510 TAILQ_FOREACH(dp, &working, list) {
511 while (dp->scheduled < when)
512 dp->scheduled += dp->fri;
513 if (second && dp->scheduled + PGUARD >= second->scheduled)
514 continue;
515 second = dp;
516 }
517
518 delay = (second->scheduled - first->scheduled) - GRI;
519
520 LOAD_9513(0x0a, delay);
521
522 /* Done, the rest is leisure work */
523
524 vco_error += ((vco_is << VCO_SHIFT) - vco_should) *
525 (ticker - vco_when);
526 vco_should = vco_want;
527 i = vco_should >> VCO_SHIFT;
528 if (vco_error < 0)
529 i++;
530
531 if (vco_is != i) {
532 LOAD_DAC(DACA, i);
533 vco_is = i;
534 }
535 vco_when = ticker;
536
537 /* Check if we overran */
538 status &= 0x0c;
539 #if 0
540
541 if (status) {
542 outb(TGC, TG_SAVE + 2); /* save counter #2 */
543 outb(TGC, TG_LOADDP + 0x12); /* hold counter #2 */
544 count = inb(TGD);
545 count |= inb(TGD) << 8;
546 LOAD_9513(0x12, GRI)
547 }
548 #endif
549
550 if (status) {
551 printf( "Missed: %02x %d first:%p second:%p %.09ld\n",
552 status, delay, first, second,
553 then.tv_nsec - there.tv_nsec);
554 first->bounce++;
555 }
556
557 TAILQ_REMOVE(&working, second, list);
558
559 if (done->bounce) {
560 done->bounce = 0;
561 loranenqueue(done);
562 } else {
563 done->epoch = ticker;
564 done->vco = vco_is;
565 done->when = there;
566 TAILQ_INSERT_TAIL(done->home, done, list);
567 wakeup(done->home);
568 }
569
570 ticker = first->scheduled;
571
572 while ((dp = TAILQ_FIRST(&minors[NLORAN])) != NULL) {
573 TAILQ_REMOVE(&minors[NLORAN], dp, list);
574 TAILQ_INSERT_TAIL(&working, dp, list);
575 }
576
577 when = second->scheduled + PGUARD;
578
579 TAILQ_FOREACH(dp, &working, list) {
580 while (dp->scheduled < when)
581 dp->scheduled += dp->fri;
582 }
583 write_eflags(ef);
584 }
585
586 /**********************************************************************/
587
588 static unsigned
589 loran_get_timecount(struct timecounter *tc)
590 {
591 unsigned count;
592 u_long ef;
593
594 ef = read_eflags();
595 disable_intr();
596
597 outb(TGC, TG_SAVE + 0x10); /* save counter #5 */
598 outb(TGC, TG_LOADDP +0x15); /* hold counter #5 */
599 count = inb(TGD);
600 count |= inb(TGD) << 8;
601
602 write_eflags(ef);
603 return (count);
604 }
605
606 static struct timecounter loran_timecounter = {
607 loran_get_timecount, /* get_timecount */
608 0, /* no pps_poll */
609 0xffff, /* counter_mask */
610 5000000, /* frequency */
611 "loran" /* name */
612 };
613
614
615 /**********************************************************************/
616
617 struct isa_driver lorandriver = {
618 INTR_TYPE_TTY | INTR_FAST,
619 loranprobe,
620 loranattach,
621 "loran"
622 };
623 COMPAT_ISA_DRIVER(loran, lorandriver);
624
625 static struct cdevsw loran_cdevsw = {
626 .d_open = loranopen,
627 .d_close = loranclose,
628 .d_read = loranread,
629 .d_write = loranwrite,
630 .d_name = "loran",
631 };
632
633 #endif /* _KERNEL */
Cache object: cb194ae8a191cc2251b265f34aa062ce
|