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