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 * ----------------------------------------------------------------------------
11 * This device-driver helps the userland controlprogram for a LORAN-C
12 * receiver avoid monopolizing the CPU.
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.
18 * Details can be found at:
19 * ftp://ftp.eecis.udel.edu/pub/ntp/loran.tar.Z
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/sysctl.h>
28 #include <sys/kernel.h>
30 #include <sys/malloc.h>
32 #include <i386/isa/isa_device.h>
35 typedef TAILQ_HEAD(, datapoint) dphead_t;
38 /* Fields used by kernel */
50 TAILQ_ENTRY(datapoint) list;
59 /* Fields used only in userland */
60 void (*proc)(struct datapoint *);
66 /* Fields used only in userland */
75 * Mode register (PAR) hardware definitions
77 #define INTEG 0x03 /* integrator mask */
78 #define INTEG_1000us 0
82 #define GATE 0x0C /* gate source mask */
87 #define MSB 0x10 /* load dac high-order bits */
88 #define IEN 0x20 /* enable interrupt bit */
89 #define EN5 0x40 /* enable counter 5 bit */
90 #define ENG 0x80 /* enable gri bit */
92 #define VCO_SHIFT 8 /* bits of fraction on VCO value */
93 #define VCO (2048 << VCO_SHIFT) /* initial vco dac (0 V)*/
96 #define PGUARD 990 /* program guard time (cycle) (990!) */
100 #define NLORAN 10 /* Allow ten minor devices */
102 #define NDUMMY 4 /* How many idlers we want */
104 #define PORT 0x0300 /* controller port address */
107 #define GRI 800 /* pulse-group gate (cycle) */
110 * Analog/digital converter (ADC) hardware definitions
112 #define ADC PORT+2 /* adc buffer (r)/address (w) */
113 #define ADCGO PORT+3 /* adc status (r)/adc start (w) */
114 #define ADC_START 0x01 /* converter start bit (w) */
115 #define ADC_BUSY 0x01 /* converter busy bit (r) */
116 #define ADC_DONE 0x80 /* converter done bit (r) */
117 #define ADC_I 0 /* i channel (phase) */
118 #define ADC_Q 1 /* q channel (amplitude) */
119 #define ADC_S 2 /* s channel (agc) */
122 * Digital/analog converter (DAC) hardware definitions
123 * Note: output voltage increases with value programmed; the buffer
124 * is loaded in two 8-bit bytes, the lsb 8 bits with the MSB bit off in
125 * the PAR register, the msb 4 bits with the MSB on.
127 #define DACA PORT+4 /* vco (dac a) buffer (w) */
128 #define DACB PORT+5 /* agc (dac b) buffer (w) */
130 #define LOAD_DAC(dac, val) if (0) { } else { \
131 par &= ~MSB; outb(PAR, par); outb((dac), (val) & 0xff); \
132 par |= MSB; outb(PAR, par); outb((dac), ((val) >> 8) & 0xff); \
136 * Pulse-code generator (CODE) hardware definitions
137 * Note: bits are shifted out from the lsb first
139 #define CODE PORT+6 /* pulse-code buffer (w) */
140 #define MPCA 0xCA /* LORAN-C master pulse code group a */
141 #define MPCB 0x9F /* LORAN-C master pulse code group b */
142 #define SPCA 0xF9 /* LORAN-C slave pulse code group a */
143 #define SPCB 0xAC /* LORAN-C slave pulse code group b */
146 * Mode register (PAR) hardware definitions
148 #define PAR PORT+7 /* parameter buffer (w) */
150 #define TGC PORT+0 /* stc control port (r/w) */
151 #define TGD PORT+1 /* stc data port (r/w) */
154 * Timing generator (STC) hardware commands
156 /* argument sssss = counter numbers 5-1 */
157 #define TG_LOADDP 0x00 /* load data pointer */
158 /* argument ee = element (all groups except ggg = 000 or 111) */
159 #define MODEREG 0x00 /* mode register */
160 #define LOADREG 0x08 /* load register */
161 #define HOLDREG 0x10 /* hold register */
162 #define HOLDINC 0x18 /* hold register (hold cycle increm) */
163 /* argument ee = element (group ggg = 111) */
164 #define ALARM1 0x07 /* alarm register 1 */
165 #define ALARM2 0x0F /* alarm register 2 */
166 #define MASTER 0x17 /* master mode register */
167 #define STATUS 0x1F /* status register */
168 #define ARM 0x20 /* arm counters */
169 #define LOAD 0x40 /* load counters */
170 #define TG_LOADARM 0x60 /* load and arm counters */
171 #define DISSAVE 0x80 /* disarm and save counters */
172 #define TG_SAVE 0xA0 /* save counters */
173 #define DISARM 0xC0 /* disarm counters */
174 /* argument nnn = counter number */
175 #define SETTOG 0xE8 /* set toggle output HIGH for counter */
176 #define CLRTOG 0xE0 /* set toggle output LOW for counter */
177 #define STEP 0xF0 /* step counter */
178 /* argument eeggg, where ee = element, ggg - counter group */
180 #define ENABDPS 0xE0 /* enable data pointer sequencing */
181 #define ENABFOUT 0xE6 /* enable fout */
182 #define ENAB8 0xE7 /* enable 8-bit data bus */
183 #define DSABDPS 0xE8 /* disable data pointer sequencing */
184 #define ENAB16 0xEF /* enable 16-bit data bus */
185 #define DSABFOUT 0xEE /* disable fout */
186 #define ENABPFW 0xF8 /* enable prefetch for write */
187 #define DSABPFW 0xF9 /* disable prefetch for write */
188 #define TG_RESET 0xFF /* master reset */
190 #define LOAD_9513(index, val) if (0) {} else { \
191 outb(TGC, TG_LOADDP + (index)); \
192 outb(TGD, (val) & 0xff); \
193 outb(TGD, ((val) >> 8) & 0xff); \
196 #define NENV 40 /* size of envelope filter */
197 #define CLOCK 50 /* clock period (clock) */
198 #define CYCLE 10 /* carrier period (us) */
199 #define PCX (NENV * CLOCK) /* envelope gate (clock) */
200 #define STROBE 50 /* strobe gate (clock) */
202 /**********************************************************************/
204 extern struct cdevsw loran_cdevsw;
206 static dphead_t minors[NLORAN + 1], working;
208 static struct datapoint dummy[NDUMMY], *first, *second;
210 static u_int64_t ticker;
214 static MALLOC_DEFINE(M_LORAN, "Loran", "Loran datapoints");
216 static int loranerror;
217 static char lorantext[160];
220 static u_int vco_should;
221 static u_int vco_want;
222 static u_int64_t vco_when;
223 static int64_t vco_error;
225 /**********************************************************************/
227 static int loranprobe (struct isa_device *dvp);
228 static void init_tgc (void);
229 static int loranattach (struct isa_device *isdp);
230 static void loranenqueue (struct datapoint *);
231 static d_open_t loranopen;
232 static d_close_t loranclose;
233 static d_read_t loranread;
234 static d_write_t loranwrite;
235 static ointhand2_t loranintr;
236 extern struct timecounter loran_timecounter;
238 /**********************************************************************/
241 loranprobe(struct isa_device *dvp)
246 cdevsw_add(&loran_cdevsw);
247 /* We need to be a "fast-intr" */
248 dvp->id_ri_flags |= RI_FAST;
250 dvp->id_iobase = PORT;
254 static u_short tg_init[] = { /* stc initialization vector */
255 0x0562, 12, 13, /* counter 1 (p0) Mode J */
256 0x0262, PGUARD, GRI, /* counter 2 (gri) Mode J */
257 0x8562, PCX, 5000 - PCX, /* counter 3 (pcx) */
258 0xc562, 0, STROBE, /* counter 4 (stb) Mode L */
259 0x052a, 0, 0 /* counter 5 (out) */
267 /* Initialize the 9513A */
268 outb(TGC, TG_RESET); outb(TGC, LOAD+0x1f); /* reset STC chip */
269 LOAD_9513(MASTER, 0x8af0);
270 outb(TGC, TG_LOADDP+1);
271 tg_init[4] = 7499 - GRI;
272 for (i = 0; i < 5*3; i++) {
273 outb(TGD, tg_init[i]);
274 outb(TGD, tg_init[i] >> 8);
276 outb(TGC, TG_LOADARM+0x1f); /* let the good times roll */
280 loranattach(struct isa_device *isdp)
284 isdp->id_ointr = loranintr;
286 /* We need to be a "fast-intr" */
287 isdp->id_ri_flags |= RI_FAST;
289 printf("loran0: LORAN-C Receiver\n");
291 vco_want = vco_should = VCO;
292 vco_is = vco_should >> VCO_SHIFT;
293 LOAD_DAC(DACA, vco_is);
297 init_timecounter(&loran_timecounter);
299 TAILQ_INIT(&working);
300 for (i = 0; i < NLORAN + 1; i++) {
301 TAILQ_INIT(&minors[i]);
305 for (i = 0; i < NDUMMY; i++) {
307 dummy[i].code = 0xac;
308 dummy[i].fri = PGUARD;
309 dummy[i].scheduled = PGUARD * 2 * i;
312 dummy[i].priority = NLORAN * 256;
313 dummy[i].home = &minors[NLORAN];
319 TAILQ_INSERT_TAIL(&working, &dummy[i], list);
322 inb(ADC); /* Flush any old result */
332 loranopen (dev_t dev, int flags, int fmt, struct proc *p)
344 loranclose(dev_t dev, int flags, int fmt, struct proc *p)
350 loranread(dev_t dev, struct uio * uio, int ioflag)
353 struct datapoint *this;
360 printf("Loran0: %s", lorantext);
364 if (TAILQ_EMPTY(&minors[idx]))
365 tsleep ((caddr_t)&minors[idx], (PZERO + 8) |PCATCH, "loranrd", hz*2);
366 if (TAILQ_EMPTY(&minors[idx]))
368 this = TAILQ_FIRST(&minors[idx]);
371 TAILQ_REMOVE(&minors[idx], this, list);
374 c = imin(uio->uio_resid, (int)sizeof *this);
375 err = uiomove((caddr_t)this, c, uio);
381 loranenqueue(struct datapoint *dp)
383 struct datapoint *dpp;
385 TAILQ_FOREACH(dpp, &working, list) {
386 if (dpp->priority <= dp->priority)
388 TAILQ_INSERT_BEFORE(dpp, dp, list);
391 TAILQ_INSERT_TAIL(&working, dp, list);
395 loranwrite(dev_t dev, struct uio * uio, int ioflag)
399 struct datapoint *this;
406 MALLOC(this, struct datapoint *, sizeof *this, M_LORAN, M_WAITOK);
407 c = imin(uio->uio_resid, (int)sizeof *this);
408 err = uiomove((caddr_t)this, c, uio);
413 if (this->fri == 0) {
417 this->par &= INTEG|GATE;
418 /* XXX more checks needed! */
419 this->home = &minors[idx];
420 this->priority &= 0xff;
421 this->priority += idx * 256;
423 when = second->scheduled + PGUARD;
424 if (when > this->scheduled) {
425 dt = when - this->scheduled;
426 dt -= dt % this->fri;
427 this->scheduled += dt;
434 vco_want = this->vco;
448 struct timespec there, then;
449 struct datapoint *dp, *done;
455 * Pick up the measurement which just completed, and setup
456 * the next measurement. We have 1100 microseconds for this
457 * of which some eaten by the A/D of the S channel and the
458 * interrupt to get us here.
464 done->ssig = inb(ADC);
470 outb(ADCGO, ADC_START);
472 /* Interlude: while we wait: setup the next measurement */
473 LOAD_DAC(DACB, second->agc);
474 outb(CODE, second->code);
475 par &= ~(INTEG|GATE);
479 while (!(inb(ADCGO) & ADC_DONE))
481 done->isig = inb(ADC);
484 outb(ADCGO, ADC_START);
485 /* Interlude: while we wait: setup the next measurement */
487 * We need to load this from the opposite register due to some
488 * weirdness which you can read about in in the 9513 manual on
489 * page 1-26 under "LOAD"
491 LOAD_9513(0x0c, second->phase);
492 LOAD_9513(0x14, second->phase);
493 outb(TGC, TG_LOADARM + 0x08);
494 LOAD_9513(0x14, second->width);
495 while (!(inb(ADCGO) & ADC_DONE))
497 done->qsig = inb(ADC);
504 * End of VERY time critical stuff, we have 8 msec to find
505 * the next measurement and program the delay.
512 when = first->scheduled + PGUARD;
513 TAILQ_FOREACH(dp, &working, list) {
514 while (dp->scheduled < when)
515 dp->scheduled += dp->fri;
516 if (second && dp->scheduled + PGUARD >= second->scheduled)
521 delay = (second->scheduled - first->scheduled) - GRI;
523 LOAD_9513(0x0a, delay);
525 /* Done, the rest is leisure work */
527 vco_error += ((vco_is << VCO_SHIFT) - vco_should) *
529 vco_should = vco_want;
530 i = vco_should >> VCO_SHIFT;
540 /* Check if we overran */
545 outb(TGC, TG_SAVE + 2); /* save counter #2 */
546 outb(TGC, TG_LOADDP + 0x12); /* hold counter #2 */
548 count |= inb(TGD) << 8;
554 printf( "Missed: %02x %d first:%p second:%p %.09ld\n",
555 status, delay, first, second,
556 then.tv_nsec - there.tv_nsec);
560 TAILQ_REMOVE(&working, second, list);
566 done->epoch = ticker;
569 TAILQ_INSERT_TAIL(done->home, done, list);
570 wakeup((caddr_t)done->home);
573 ticker = first->scheduled;
575 while ((dp = TAILQ_FIRST(&minors[NLORAN])) != NULL) {
576 TAILQ_REMOVE(&minors[NLORAN], dp, list);
577 TAILQ_INSERT_TAIL(&working, dp, list);
580 when = second->scheduled + PGUARD;
582 TAILQ_FOREACH(dp, &working, list) {
583 while (dp->scheduled < when)
584 dp->scheduled += dp->fri;
589 /**********************************************************************/
592 loran_get_timecount(struct timecounter *tc)
600 outb(TGC, TG_SAVE + 0x10); /* save counter #5 */
601 outb(TGC, TG_LOADDP +0x15); /* hold counter #5 */
603 count |= inb(TGD) << 8;
609 static struct timecounter loran_timecounter = {
610 loran_get_timecount, /* get_timecount */
612 0xffff, /* counter_mask */
613 5000000, /* frequency */
617 SYSCTL_OPAQUE(_debug, OID_AUTO, loran_timecounter, CTLFLAG_RD,
618 &loran_timecounter, sizeof(loran_timecounter), "S,timecounter", "");
621 /**********************************************************************/
623 struct isa_driver lorandriver = {
624 loranprobe, loranattach, "loran"
627 #define CDEV_MAJOR 94
628 static struct cdevsw loran_cdevsw = {
629 /* open */ loranopen,
630 /* close */ loranclose,
631 /* read */ loranread,
632 /* write */ loranwrite,
636 /* strategy */ nostrategy,
638 /* maj */ CDEV_MAJOR,