]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/isa/loran.c
Catch up to the new swi API.
[FreeBSD/FreeBSD.git] / 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/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 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 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 proc *p)
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 proc *p)
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 SYSCTL_OPAQUE(_debug, OID_AUTO, loran_timecounter, CTLFLAG_RD, 
617         &loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
618
619
620 /**********************************************************************/
621
622 struct  isa_driver lorandriver = {
623         INTR_TYPE_TTY | INTR_FAST,
624         loranprobe,
625         loranattach,
626         "loran"
627 };
628 COMPAT_ISA_DRIVER(loran, lorandriver);
629
630 #define CDEV_MAJOR 94
631 static struct cdevsw loran_cdevsw = {
632         /* open */      loranopen,
633         /* close */     loranclose,
634         /* read */      loranread,
635         /* write */     loranwrite,
636         /* ioctl */     noioctl,
637         /* poll */      nopoll,
638         /* mmap */      nommap,
639         /* strategy */  nostrategy,
640         /* name */      "loran",
641         /* maj */       CDEV_MAJOR,
642         /* dump */      nodump,
643         /* psize */     nopsize,
644         /* flags */     0,
645         /* bmaj */      -1
646 };
647
648 #endif /* _KERNEL */