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