1 /*
2 * Copyright (c) 1989, 1990, 1991, 1992, 1994 William F. Jolitz, TeleMuse
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This software is a component of "386BSD" developed by
16 * William F. Jolitz, TeleMuse.
17 * 4. Neither the name of the developer nor the name "386BSD"
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 * 5. Non-commercial distribution of this complete file in either source and/or
21 * binary form at no charge to the user (such as from an official Internet
22 * archive site) is permitted.
23 * 6. Commercial distribution and sale of this complete file in either source
24 * and/or binary form on any media, including that of floppies, tape, or
25 * CD-ROM, or through a per-charge download such as that of a BBS, is not
26 * permitted without specific prior written permission.
27 * 7. Non-commercial and/or commercial distribution of an incomplete, altered,
28 * or otherwise modified file in either source and/or binary form is not
29 * permitted.
30 *
31 * THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
32 * AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
33 * SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
34 * THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
35 * NOT MAKE USE OF THIS WORK.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND
38 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE
41 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * SUCH DAMAGE.
48 *
49 * $Id: isa.c,v 1.1 94/06/24 18:49:31 bill Exp Locker: bill $
50 *
51 * ISA Bus adapter.
52 */
53 static char *isa_config =
54 "isa 32. # bus device (/dev/isa) $Revision$";
55
56 #include "sys/param.h"
57 #include "sys/errno.h"
58 #include "sys/time.h"
59 #include "sys/file.h"
60 #include "sys/syslog.h"
61 #include "kernel.h" /* hz */
62 #include "proc.h"
63 #include "buf.h"
64 #include "uio.h"
65 #include "malloc.h"
66 #include "rlist.h"
67 #include "modconfig.h"
68 #include "prototypes.h"
69
70 #include "vm.h"
71
72 #include "machine/cpu.h"
73 #include "machine/pcb.h"
74 #include "machine/psl.h"
75
76
77 #include "isa_driver.h"
78 #include "isa_stdports.h"
79 #include "isa_irq.h"
80 #include "isa_mem.h"
81 #include "machine/icu.h"
82 #include "i8237.h"
83 #include "i8042.h"
84
85
86 #include "machine/inline/io.h"
87
88 int config_isadev(struct isa_device *);
89 u_short getit(int unit, int timer);
90
91 #define IDTVEC(name) __CONCAT(X,name)
92 /* assignable interrupt vector table entries */
93 extern IDTVEC(irq0), IDTVEC(irq1), IDTVEC(irq2), IDTVEC(irq3),
94 IDTVEC(irq4), IDTVEC(irq5), IDTVEC(irq6), IDTVEC(irq7),
95 IDTVEC(irq8), IDTVEC(irq9), IDTVEC(irq10), IDTVEC(irq11),
96 IDTVEC(irq12), IDTVEC(irq13), IDTVEC(irq14), IDTVEC(irq15);
97
98 static *irqvec[16] = {
99 &IDTVEC(irq0), &IDTVEC(irq1), &IDTVEC(irq2), &IDTVEC(irq3),
100 &IDTVEC(irq4), &IDTVEC(irq5), &IDTVEC(irq6), &IDTVEC(irq7),
101 &IDTVEC(irq8), &IDTVEC(irq9), &IDTVEC(irq10), &IDTVEC(irq11),
102 &IDTVEC(irq12), &IDTVEC(irq13), &IDTVEC(irq14), &IDTVEC(irq15) };
103
104 struct isa_driver *driver_for_intr[16];
105 u_short isa_mask[16];
106 void (*isa_vec[16])();
107 int isa_unit[16];
108 extern char *intrnames[16];
109
110 unsigned volatile it_ticks;
111 unsigned it_ticksperintr;
112 int loops_per_usec;
113
114 /*
115 * Unfinished:
116 *
117 * Bus discovery causes motherboard low-level configuration. Drivers
118 * are discovered by kernel, which do depth-first resource discovery and
119 * low-level configuration, signalling changes to the higher levels. On
120 * arrival to higher-level kernel layer, message is discovered and
121 * configuration is reconciled (top down) via symbolic call to registered
122 * bus configuration function(s).
123 *
124 * Bus drivers provide services via registered interfaces to the kernel,
125 * and to drivers via symbolic calls only. Bus resources must be recorded
126 * as being assigned to the arc linking the bus driver to a ordinary driver
127 * so that if either is unloaded/reloaded, resources can be reclaimed/reconciled
128 * in a stateless manner (e.g. if still referenced, the attempt to unload
129 * is followed by an immediate reload of the same driver -- resources are
130 * independant of the driver instance/implementation itself).
131 */
132
BUS_MODCONFIG()133 BUS_MODCONFIG() {
134
135 /*printf("isa: "); */
136 /*isa_configure(init++) */
137 }
138
139 /*
140 * Configure all ISA devices
141 */
isa_configure()142 isa_configure()
143 {
144 struct isa_device *dvp;
145 struct isa_driver *dp;
146 register cnt;
147 int tick, x;
148
149 /*
150 * Configure bus interrupts with processor
151 */
152 isa_defaultirq();
153 splhigh(); /* XXX */
154
155 /*
156 * Configure motherboard
157 */
158
159 /* initialize 8253 clock */
160 it_ticksperintr = 1193182/hz;
161 outb (IO_TIMER1+3, 0x34);
162 outb (IO_TIMER1, it_ticksperintr);
163 outb (IO_TIMER1, it_ticksperintr/256);
164
165 /*
166 * Find out how many loops we need to DELAY() a microsecond
167 */
168
169 /* wait till overflow to insure no wrap around */
170 do
171 tick = getit(0,0);
172 while (tick < getit(0,0));
173
174 /* time a while loop */
175 cnt = 1000;
176 tick = getit(0,0);
177 while (cnt-- > 0)
178 ;
179 tick -= getit(0,0);
180
181 /* scale to microseconds per 1000 "loops" */
182 tick *= 1000000;
183 tick /= 1193182;
184 loops_per_usec = (10000/tick + 5) / 10;
185
186 /*
187 * Configure ISA devices. XXX
188 */
189 splhigh();
190 INTREN(IRQ_SLAVE);
191 modscaninit(MODT_DRIVER);
192
193 /*
194 * Finish configuration of ISA devices.
195 */
196 ttymask |= biomask + netmask;
197 netmask |= biomask;
198 /* biomask |= ttymask ; can some tty devices use buffers? */
199 /*printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */
200
201 /* splat masks into place */
202 for (x = 0; x < 16; x++)
203 {
204 if (driver_for_intr[x] && driver_for_intr[x]->mask)
205 isa_mask[x] = *(driver_for_intr[x]->mask);
206 if (intrnames[x] == 0)
207 intrnames[x] = "** unassigned **";
208 }
209 splnone();
210 }
211
212 /* parse and evaluate an ISA device */
cfg_isadev(char ** ptr,char * modname,struct isa_device * idp)213 cfg_isadev(char **ptr, char *modname, struct isa_device *idp) {
214 char *lp = *ptr;
215 int val;
216
217
218 /* default fields */
219 idp->id_iobase = 0;
220 idp->id_irq = 0;
221 idp->id_drq = -1;
222 idp->id_maddr= (caddr_t) 0;
223 idp->id_msize = 0;
224 idp->id_unit = '?';
225 idp->id_flags = 0;
226
227 /* list of values or '?' */
228 if (cfg_char(&lp, '(') && cfg_number(&lp, &val)) {
229 /* specify all or partial list (if device can guess rest) */
230 idp->id_iobase = val;
231
232 if (cfg_number(&lp, &val)) { /* XXX use "," to spec multiples */
233 if (val >= 0)
234 idp->id_irq = 1 << val;
235 }
236
237 if (cfg_number(&lp, &val))
238 idp->id_drq = val;
239
240 if (cfg_number(&lp, &val))
241 idp->id_maddr = (caddr_t)val;
242
243 if (cfg_number(&lp, &val))
244 idp->id_msize = val;
245
246 if (cfg_number(&lp, &val))
247 idp->id_unit = val;
248
249 if (cfg_number(&lp, &val))
250 idp->id_flags = val;
251
252 if (cfg_char(&lp, ')')) {
253 *ptr = lp;
254 return (1);
255 }
256 goto nope;
257
258 }
259 else if (cfg_char(&lp, '?')) {
260 /* driver attempts to do everything for device */
261 idp->id_iobase = '?';
262 *ptr = lp;
263 return (1);
264 }
265 nope:
266 if (modname)
267 printf("module %s: cannot find ISA port spec\n", modname);
268
269 return (0);
270 }
271
272 #ifdef notyet
273 /* print a isa dev entry */
274 void
print_isadev(struct isa_device * idp)275 print_isadev(struct isa_device *idp) {
276
277 if (idp->id_iobase) {
278 printf ("port: ");
279 if (idp->id_iobase == '?')
280 printf("? ");
281 else
282 printf("%x ", idp->id_iobase);
283 }
284 if (idp->id_irq) /* XXX iterate over bitfield */
285 printf ("irq%d ", ffs(idp->id_irq)-1);
286 if (idp->id_drq != -1)
287 printf ("drq%d ", idp->id_drq);
288 if (idp->id_maddr)
289 printf ("maddr: %x + %d ",
290 kmem_phys(idp->id_maddr), idp->id_msize);
291 printf ("unit: ");
292 if (idp->id_unit == '?')
293 printf("? ");
294 else
295 printf("%d ", idp->id_unit);
296
297 /* if (idp->id_flags)
298 printf("flags: %x ", idp->id_flags);*/
299 }
300 #endif
301
302 void
new_isa_configure(char ** lp,struct isa_driver * dp)303 new_isa_configure(char **lp, struct isa_driver *dp) {
304 struct isa_device adev ;
305
306 adev.id_driver = dp;
307 for(;;) {
308 if (cfg_isadev(lp, 0, &adev)) {
309 /*if ( */ (void)config_isadev(&adev) /* )
310 print_isadev(&adev) */ ;
311 /* attach */
312 } else break;
313 }
314
315 /*printf("\n");*/
316 }
317
318 /*
319 * Configure an ISA device.
320 */
321 config_isadev(isdp)
322 struct isa_device *isdp;
323 {
324 struct isa_driver *dp;
325 int rv;
326
327 if (dp = isdp->id_driver) {
328 if (isdp->id_maddr) {
329 extern u_int atdevbase;
330
331 isdp->id_maddr -= 0xa0000;
332 isdp->id_maddr += atdevbase;
333 }
334
335 printf("probing for %s port %x: ", dp->name, isdp->id_iobase);
336 isdp->id_alive = (*dp->probe)(isdp);
337 if (isdp->id_alive) {
338 printf("\r \r");
339 printf("%s: ", dp->name);
340 (*dp->attach)(isdp);
341 printf(" ");
342 if (isdp->id_iobase)
343 printf("port %x ", isdp->id_iobase);
344
345 /* XXX handle multiple interrupts */
346 if(isdp->id_irq) {
347 int intrno;
348
349 intrno = ffs(isdp->id_irq)-1;
350 printf("irq%d ", intrno);
351 INTREN(isdp->id_irq);
352
353 if (dp->mask)
354 INTRMASK(*(dp->mask), isdp->id_irq);
355
356 setirq(ICU_OFFSET + intrno, irqvec [intrno]);
357 isa_vec[intrno] = dp->intr;
358 isa_unit[intrno] = isdp->id_unit;
359
360 driver_for_intr[intrno] = dp;
361 intrnames[intrno] = dp->name; /* XXX - allocate seperate string with unit number */
362 }
363
364 if (isdp->id_drq != -1)
365 printf("drq%d ", isdp->id_drq);
366 printf("\n");
367 }
368 rv = 1;
369 } else {
370 DELAY(5000);
371 rv = 0;
372 }
373 printf("\r \r");
374 return(rv);
375 }
376
377 #define IDTVEC(name) __CONCAT(X,name)
378 /* default interrupt vector table entries */
379 extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3),
380 IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7),
381 IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11),
382 IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15);
383
384 static *defvec[16] = {
385 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
386 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
387 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
388 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) };
389
390 /* out of range default interrupt vector gate entry */
391 extern IDTVEC(intrdefault);
392
393 /*
394 * Fill in default interrupt table (in case of spuruious interrupt
395 * during configuration of kernel, setup interrupt control unit
396 */
isa_defaultirq()397 isa_defaultirq() {
398 int i;
399
400 /* icu vectors */
401 #define NIDT 256
402 #define NRSVIDT 32 /* reserved entries for cpu exceptions */
403 for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++)
404 setirq(i, defvec[i]);
405
406 /* out of range vectors */
407 for (i = NRSVIDT; i < NIDT; i++)
408 setirq(i, &IDTVEC(intrdefault));
409
410 /* clear npx intr latch */
411 outb(0xf1,0);
412
413 /* initialize 8259's */
414 __outb(IO_ICU1, 0x11); /* reset; program device, four bytes */
415 __outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */
416 __outb(IO_ICU1+1, 1<<2); /* slave on line 2 */
417 __outb(IO_ICU1+1, 1); /* 8086 mode */
418 __outb(IO_ICU1+1, 0xff); /* leave interrupts masked */
419 __outb(IO_ICU1, /*0x60+*/8+1); /* default to ISR on read */
420
421 __outb(IO_ICU2, 0x11); /* reset; program device, four bytes */
422 __outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */
423 __outb(IO_ICU2+1,2); /* my slave id is 2 */
424 __outb(IO_ICU2+1,1); /* 8086 mode */
425 __outb(IO_ICU2+1, 0xff); /* leave interrupts masked */
426 __outb(IO_ICU2, /*0x60+*/8+1); /* default to ISR on read */
427 }
428
429
430 /*
431 * XXX unfinished, missing new channel code and pvc_alloc()
432 */
433
434 /* region of physical memory known to be contiguous */
435 vm_offset_t isaphysmem;
436 static caddr_t dma_bounce[8]; /* XXX */
437 static char bounced[8]; /* XXX */
438 int dma_active;
439 #define MAXDMASZ 512 /* XXX */
440
441 /* high byte of address is stored in this port for i-th dma channel */
442 static short dmapageport[8] =
443 { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
444
445 /*
446 * isa_dmacascade(): program 8237 DMA controller channel to accept
447 * external dma control by a board.
448 * N.B. drivers must manage "dma_active" manually.
449 */
isa_dmacascade(unsigned chan)450 void isa_dmacascade(unsigned chan)
451 { int modeport;
452
453 if (chan > 7)
454 panic("isa_dmacascade: impossible request");
455
456 /* set dma channel mode, and set dma channel mode */
457 if ((chan & 4) == 0)
458 modeport = IO_DMA1 + 0xb;
459 else
460 modeport = IO_DMA2 + 0x16;
461 outb(modeport, DMA37MD_CASCADE | (chan & 3));
462 if ((chan & 4) == 0)
463 outb(modeport - 1, chan & 3);
464 else
465 outb(modeport - 2, chan & 3);
466 }
467
468 /*
469 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
470 * problems by using a bounce buffer.
471 */
isa_dmastart(int flags,caddr_t addr,unsigned nbytes,unsigned chan)472 void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan)
473 { vm_offset_t phys;
474 int modeport, waport, mskport;
475 caddr_t newaddr;
476
477 if (chan > 7 || nbytes > (1<<16))
478 panic("isa_dmastart: impossible request");
479
480 if (isa_dmarangecheck(addr, nbytes)) {
481 if (dma_bounce[chan] == 0)
482 dma_bounce[chan] =
483 /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
484 (caddr_t) isaphysmem + NBPG*chan;
485 bounced[chan] = 1;
486 newaddr = dma_bounce[chan];
487 *(int *) newaddr = 0; /* XXX */
488
489 /* copy bounce buffer on write */
490 if (!(flags & B_READ))
491 memcpy(newaddr, addr, nbytes);
492 addr = newaddr;
493 }
494
495 /* translate to physical */
496 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
497
498 /* set dma channel mode, and reset address ff */
499 if ((chan & 4) == 0)
500 modeport = IO_DMA1 + 0xb;
501 else
502 modeport = IO_DMA2 + 0x16;
503 if (flags & B_READ)
504 outb(modeport, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
505 else
506 outb(modeport, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
507 if ((chan & 4) == 0)
508 outb(modeport + 1, 0);
509 else
510 outb(modeport + 2, 0);
511
512 /* send start address */
513 if ((chan & 4) == 0) {
514 waport = IO_DMA1 + (chan<<1);
515 outb(waport, phys);
516 outb(waport, phys>>8);
517 } else {
518 waport = IO_DMA2 + ((chan - 4)<<2);
519 outb(waport, phys>>1);
520 outb(waport, phys>>9);
521 }
522 outb(dmapageport[chan], phys>>16);
523
524 /* send count */
525 if ((chan & 4) == 0) {
526 outb(waport + 1, --nbytes);
527 outb(waport + 1, nbytes>>8);
528 } else {
529 nbytes <<= 1;
530 outb(waport + 2, --nbytes);
531 outb(waport + 2, nbytes>>8);
532 }
533
534 /* unmask channel */
535 if ((chan & 4) == 0)
536 mskport = IO_DMA1 + 0x0a;
537 else
538 mskport = IO_DMA2 + 0x14;
539 outb(mskport, chan & 3);
540 dma_active |= 1 << chan;
541 }
542
isa_dmadone(int flags,caddr_t addr,int nbytes,int chan)543 void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
544 {
545
546 /* copy bounce buffer on read */
547 /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/
548 if (bounced[chan]) {
549 memcpy(addr, dma_bounce[chan], nbytes);
550 bounced[chan] = 0;
551 }
552 dma_active &= ~(1 << chan);
553 }
554
555 /*
556 * Check for problems with the address range of a DMA transfer
557 * (non-contiguous physical pages, outside of bus address space).
558 * Return true if special handling needed.
559 */
560
isa_dmarangecheck(caddr_t va,unsigned length)561 isa_dmarangecheck(caddr_t va, unsigned length) {
562 vm_offset_t phys, priorpage, endva;
563
564 endva = (vm_offset_t)round_page(va + length);
565 priorpage = 0;
566 for (; va < (caddr_t) endva ; va += NBPG) {
567 phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
568 if (phys == 0)
569 panic("isa_dmacheck: no physical page present");
570 if (phys > ISA_RAM_END)
571 return (1);
572 if (priorpage && priorpage + NBPG != phys)
573 return (1);
574 priorpage = phys;
575 }
576 return (0);
577 }
578
579 /* head of queue waiting for physmem to become available */
580 struct buf isa_physmemq;
581
582 /* blocked waiting for resource to become free for exclusive use */
583 static isaphysmemflag;
584 /* if waited for and call requested when free (B_CALL) */
585 static void (*isaphysmemunblock)(); /* needs to be a list */
586
587 /*
588 * Allocate contiguous physical memory for transfer, returning
589 * a *virtual* address to region. May block waiting for resource.
590 * (assumed to be called at splbio())
591 */
592 caddr_t
isa_allocphysmem(caddr_t va,unsigned length,void (* func)())593 isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
594
595 isaphysmemunblock = func;
596 while (isaphysmemflag & B_BUSY) {
597 isaphysmemflag |= B_WANTED;
598 tsleep((caddr_t)&isaphysmemflag, PRIBIO, "isaphys", 0);
599 }
600 isaphysmemflag |= B_BUSY;
601
602 return((caddr_t)isaphysmem);
603 }
604
605 /*
606 * Free contiguous physical memory used for transfer.
607 * (assumed to be called at splbio())
608 */
609 void
isa_freephysmem(caddr_t va,unsigned length)610 isa_freephysmem(caddr_t va, unsigned length) {
611
612 isaphysmemflag &= ~B_BUSY;
613 if (isaphysmemflag & B_WANTED) {
614 isaphysmemflag &= B_WANTED;
615 wakeup((caddr_t)&isaphysmemflag);
616 if (isaphysmemunblock)
617 (*isaphysmemunblock)();
618 }
619 }
620
621 /*
622 * Handle a NMI, possibly a machine check.
623 * return true to panic system, false to ignore.
624 */
isa_nmi(cd)625 isa_nmi(cd) {
626
627 log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70));
628 return(0);
629 }
630
631 /*
632 * Caught a stray interrupt, notify
633 */
isa_strayintr(d)634 isa_strayintr(d) {
635
636 #ifdef notdef
637 /* DON'T BOTHER FOR NOW! */
638 /* for some reason, we get bursts of intr #7, even if not enabled! */
639 log(LOG_ERR,"ISA strayintr %x", d);
640 #endif
641 }
642
643 u_short
getit(int unit,int timer)644 getit(int unit, int timer) {
645 int port = (unit ? IO_TIMER2 : IO_TIMER1), val;
646
647 outb(port+ 3, timer<<6); /* emit latch command */
648 val = inb(port + timer);
649 val = (inb(port + timer) << 8) + val;
650 return (val);
651 }
652
653 void
setit(unit,timer,mode,count)654 setit(unit, timer, mode, count) {
655 int port = (unit ? IO_TIMER2 : IO_TIMER1);
656
657 outb(port+ 3, (timer << 6) + mode); /* emit latch command */
658 outb(port + timer, count);
659 outb(port + timer, count >> 8);
660 }
661
662 /*
663 * get time in absolute it_ticks for tracing.
664 */
getticks()665 getticks() {
666 register unsigned val;
667
668 /* stop interrupts, emit latch command for timer 0, unit 0 */
669 asm (" xorl %eax, %eax ; cli ; outb %al, $0x43 ");
670
671 asm (" inb $0x40, %%al ; xchgb %%al, %%ah ; inb $0x40, %%al ; xchg %%al, %%ah " : "=a" (val));
672 /* val = inb(IO_TIMER1);
673 val += inb(IO_TIMER1) << 8; */
674
675 if (val >= it_ticksperintr - 1)
676 return (it_ticks + it_ticksperintr);
677 else
678 return (it_ticks + it_ticksperintr - val);
679 }
680
681 extern int hz;
682
683 /*
684 * get fraction of tick in units of microseconds
685 * (must be called at splclock)
686 * This routine cannot use get_it() because of underflow
687 * processing.
688 */
689
690 void
microtime(tvp)691 microtime(tvp)
692 register struct timeval *tvp;
693 {
694 extern unsigned it_ticksperintr;
695 register unsigned val;
696
697 /* minimize clock sampling skew by latching counter immediately after blocking interrupts */
698 /* stop interrupts, emit latch command for timer 0, unit 0 */
699 asm (" xorl %eax, %eax ; cli ; outb %al, $0x43 ");
700
701 /* obtain frozen time - optimize overhead */
702 asm (" inb $0x84, %%al ; inb $0x84, %%al ; inb $0x40, %%al ; xchgb %%al, %%ah ; inb $0x84, %%al ; inb $0x84, %%al ; inb $0x40, %%al ; xchgb %%al, %%ah " : "=a" (val));
703 *tvp = time;
704
705 /* if overflowed, account for missing clock interrupt */
706 /* (clock can be skewed by as much as 3 ticks following interrupt due
707 to bus overhead in processing interrupt request (the counter drops
708 to value 1, signals interrupt, 8259 just misses sampling window
709 (~ 1 I/O bus cycle or 625ns, counter now reset to N, program emits
710 freeze command (another bus cycle) .... */
711 if (val < it_ticksperintr - 2)
712 /* clock interrupt not pending */
713 tvp->tv_usec += (1000000 / hz) * (it_ticksperintr - val)
714 / it_ticksperintr;
715 else
716 /* clock race; interrupt pending, just add missing tick */
717 tvp->tv_usec += 1000000 / hz;
718
719 /* if overflowing microsecond time, carry to seconds time */
720 if (tvp->tv_usec > 1000000) {
721 tvp->tv_sec++;
722 tvp->tv_usec -= 1000000;
723 }
724
725 /* re-engage interrupts, and take clock interrupt if overflowed */
726 asm("sti");
727 }
728
729 static beeping;
730 static
sysbeepstop(f)731 sysbeepstop(f)
732 {
733 /* disable counter 2 */
734 outb(0x61, inb(0x61) & 0xFC);
735 if (f)
736 timeout(sysbeepstop, 0, f);
737 else
738 beeping = 0;
739 }
740
sysbeep(int pitch,int period)741 void sysbeep(int pitch, int period)
742 {
743
744 outb(0x61, inb(0x61) | 3); /* enable counter 2 */
745 outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */
746
747 outb(0x42, pitch);
748 outb(0x42, (pitch>>8));
749
750 if (!beeping) {
751 beeping = period;
752 timeout(sysbeepstop, (caddr_t)(period/2), period);
753 }
754 }
755
756 /*
757 * Pass command to keyboard controller (8042)
758 */
759 void
kbd_cmd(unsigned val)760 kbd_cmd(unsigned val) {
761 u_char r;
762
763 /* see if we can issue a command. clear data buffer if something present */
764 while ((r = inb(KBSTATP)) & (KBS_IBF|KBS_DIB))
765 if (r & KBS_DIB) {
766 DELAY(7);
767 printf("kc: %x", inb(KBDATAP));
768 }
769 DELAY(7);
770 outb(KBCMDP, val);
771
772 /* wait till command is taken */
773 while (inb(KBSTATP) & KBS_IBF)
774 DELAY(7);
775 }
776
777 /*
778 * Pass command thru keyboard controller to keyboard itself
779 */
780 void
kbd_wr(unsigned val)781 kbd_wr(unsigned val) {
782 u_char r;
783
784 while (inb(KBSTATP) & KBS_IBF)
785 DELAY(7);
786
787 while ((r = inb(KBSTATP)) & (KBS_IBF|KBS_DIB))
788 if (r & KBS_DIB) {
789 DELAY(7);
790 printf("kw: %x", inb(KBDATAP));
791 }
792
793 DELAY(7);
794 outb(KBOUTP, val);
795
796 /* wait till data is taken */
797 while (inb(KBSTATP) & KBS_IBF)
798 DELAY(7);
799 }
800
801 /*
802 * Read a character from keyboard controller
803 */
804 u_char
kbd_rd()805 kbd_rd() {
806 int sts;
807
808 while (inb(KBSTATP) & KBS_IBF)
809 DELAY(7);
810 DELAY(7);
811 while (((sts = inb(KBSTATP)) & KBS_DIB) == 0)
812 DELAY(7);
813 DELAY(7);
814 return (inb(KBDATAP));
815 }
816
kbd_drain()817 kbd_drain() {
818 int sts;
819
820 /* do { */
821 DELAY(1000);
822 if ((sts = inb(KBSTATP)) & KBS_DIB)
823 printf("kd: %x", kbd_rd());
824 /* } while (sts & KBS_DIB); */
825 }
826
827 /*
828 * Send the keyboard a command, wait for and return status
829 */
830 u_char
key_cmd(unsigned val)831 key_cmd(unsigned val) {
832
833 kbd_wr(val);
834 return(kbd_rd());
835 }
836
837 /*
838 * Send the aux port a command, wait for and return status
839 */
840 u_char
aux_cmd(unsigned val)841 aux_cmd(unsigned val) {
842 u_char r;
843
844 kbd_cmd(K_AUXOUT);
845 kbd_wr(val);
846 return(kbd_rd());
847 }
848
849 /*
850 * Execute a keyboard controller command that passes a parameter
851 */
852 void
kbd_cmd_write_param(unsigned cmd,unsigned val)853 kbd_cmd_write_param(unsigned cmd, unsigned val) {
854 u_char r;
855
856 kbd_cmd(cmd);
857 kbd_wr(val);
858 }
859
860 /*
861 * Execute a keyboard controller command that returns a parameter
862 */
863 u_char
kbd_cmd_read_param(unsigned cmd)864 kbd_cmd_read_param(unsigned cmd) {
865 u_char r;
866
867 kbd_cmd(cmd);
868 return(kbd_rd());
869 }
870
871 #define CW54BCD 0x01 /* BCD instead of binary count down mode */
872 #define CW54RATE 0x04 /* rate operating mode */
873 #define CW54SQUARE 0x06 /* square wave output mode */
874 #define CW54LSBMSB 0x30 /* pass counter values in little endian order */
875
876 #define KBC42FAILDIS 0x04 /* disable failsafe counter NMI */
877 #define KBC42IOCHKDIS 0x08 /* disable IOCHK NMI */
878 #define KBC42FAILNMI 0x40 /* NMI from failsafe timer */
879 #define KBC42IOCHKNMI 0x80 /* NMI from IOCHK */
880
881 /*
882 * Enable an NMI failsafe timer for a millisecond
883 */
884 int allownmi;
failsafe_start()885 failsafe_start() {
886 if(allownmi) {
887 setit(1, 0, CW54LSBMSB|CW54SQUARE, 1193000/100);
888 outb(0x61, inb(0x61) & ~KBC42FAILDIS);
889 outb(0x70, 0x00);
890 }
891 }
892
893 /*
894 * Disable the NMI failsafe timer, cancelling the timeout.
895 */
failsafe_cancel()896 failsafe_cancel() {
897 outb(0x61, inb(0x61) | KBC42FAILDIS);
898 outb(0x70, 0x80);
899 }
900
901 /*
902 * Read RTC atomically
903 */
904 u_char
rtcin(u_char adr)905 rtcin(u_char adr)
906 {
907 int x;
908
909 /*
910 * Some RTC's (dallas smart watch) attempt to foil unintentioned
911 * operation of the RTC by requiring back-to-back i/o instructions
912 * to reference the RTC; any interviening i/o operations cancel
913 * the reference to the clock (e.g. the NOP).
914 */
915 x = splhigh();
916 asm volatile ("out%z0 %b0, %2 ; xorl %0, %0 ; in%z0 %3, %b0"
917 : "=a"(adr) : "0"(adr), "i"(IO_RTC), "i"(IO_RTC + 1));
918 splx(x);
919 return (adr);
920 }
921
922 /*
923 * Write RTC atomically.
924 */
925 void
rtcout(u_char adr,u_char val)926 rtcout(u_char adr, u_char val)
927 {
928 int x;
929
930 /*
931 * Some RTC's (dallas smart watch) attempt to foil unintentioned
932 * operation of the RTC by requiring back-to-back i/o instructions
933 * to reference the RTC; any interviening i/o operations cancel
934 * the reference to the clock (e.g. the NOP).
935 */
936 x = splhigh();
937 asm volatile ("out%z0 %b0, %2 ; movb %1, %b0 ; out%z0 %b0, %3"
938 :: "a"(adr), "g"(val), "i"(IO_RTC), "i"(IO_RTC+1));
939 splx(x);
940 }
941
942 /* XXX: framework only, unfinished */
943 static int
isaopen(dev_t dev,int flag,int mode,struct proc * p)944 isaopen(dev_t dev, int flag, int mode, struct proc *p) {
945 struct syscframe *fp = (struct syscframe *)p->p_md.md_regs;
946
947 fp->sf_eflags |= PSL_IOPL;
948 return (0);
949 }
950
951 static int
isaclose(dev_t dev,int flag,int mode,struct proc * p)952 isaclose(dev_t dev, int flag, int mode, struct proc *p) {
953 struct syscframe *fp = (struct syscframe *)p->p_md.md_regs;
954
955 fp->sf_eflags &= ~PSL_IOPL;
956 return (0);
957 }
958
959 static int
isaioctl(dev_t dev,int cmd,caddr_t addr,int flag,struct proc * p)960 isaioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) {
961
962 /* What's missing: enable selection on irq's, controls on
963 [e]isa features, statistics, ... */
964
965 return (ENODEV);
966 }
967
968 static int
isaselect(dev_t dev,int rw,struct proc * p)969 isaselect(dev_t dev, int rw, struct proc *p) {
970
971 return (ENODEV);
972 }
973
974 static int
isammap(dev_t dev,int offset,int nprot)975 isammap(dev_t dev, int offset, int nprot)
976 {
977 if (offset < ISA_RAM_BEGIN || offset > ISA_RAM_END)
978 return -1;
979
980 return i386_btop(offset);
981 }
982
983 static struct devif isa_devif =
984 {
985 {0}, -1, -2, 0, 0, 0, 0, 0, 0,
986 isaopen, isaclose, isaioctl, 0, 0, isaselect, isammap,
987 0, 0, 0, 0,
988 0, 0,
989 };
990
DRIVER_MODCONFIG()991 DRIVER_MODCONFIG() {
992 char *cfg_string = isa_config;
993
994 if (devif_config(&cfg_string, &isa_devif) == 0)
995 return;
996
997 }
998