Commit | Line | Data |
---|---|---|
ab05af3a WJ |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * William Jolitz. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | * @(#)isa.c 7.2 (Berkeley) 5/13/91 | |
37 | */ | |
38 | static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/01/21 14:34:23 william Exp Locker: root $"; | |
39 | ||
40 | /* | |
41 | * code to manage AT bus | |
42 | */ | |
43 | ||
44 | #include "param.h" | |
45 | #include "systm.h" | |
46 | #include "conf.h" | |
47 | #include "file.h" | |
48 | #include "buf.h" | |
49 | #include "uio.h" | |
50 | #include "syslog.h" | |
51 | #include "malloc.h" | |
52 | #include "rlist.h" | |
53 | #include "machine/segments.h" | |
54 | #include "vm/vm.h" | |
55 | #include "i386/isa/isa_device.h" | |
56 | #include "i386/isa/isa.h" | |
57 | #include "i386/isa/icu.h" | |
58 | #include "i386/isa/ic/i8237.h" | |
59 | #include "i386/isa/ic/i8042.h" | |
60 | ||
61 | int config_isadev(struct isa_device *, u_short *); | |
62 | #ifdef notyet | |
63 | struct rlist *isa_iomem; | |
64 | ||
65 | /* | |
66 | * Configure all ISA devices | |
67 | */ | |
68 | isa_configure() { | |
69 | struct isa_device *dvp; | |
70 | struct isa_driver *dp; | |
71 | ||
72 | splhigh(); | |
73 | INTREN(IRQ_SLAVE); | |
74 | /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/ | |
75 | for (dvp = isa_devtab_tty; dvp; dvp++) | |
76 | (void) config_isadev(dvp, &ttymask); | |
77 | for (dvp = isa_devtab_bio; dvp; dvp++) | |
78 | (void) config_isadev(dvp, &biomask); | |
79 | for (dvp = isa_devtab_net; dvp; dvp++) | |
80 | (void) config_isadev(dvp, &netmask); | |
81 | for (dvp = isa_devtab_null; dvp; dvp++) | |
82 | (void) config_isadev(dvp, 0); | |
83 | #include "sl.h" | |
84 | #if NSL > 0 | |
85 | netmask |= ttymask; | |
86 | ttymask |= netmask; | |
87 | #endif | |
88 | /* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ | |
89 | splnone(); | |
90 | } | |
91 | ||
92 | /* | |
93 | * Configure an ISA device. | |
94 | */ | |
95 | config_isadev(isdp, mp) | |
96 | struct isa_device *isdp; | |
97 | u_short *mp; | |
98 | { | |
99 | struct isa_driver *dp; | |
100 | static short drqseen, irqseen; | |
101 | ||
102 | if (dp = isdp->id_driver) { | |
103 | /* if a device with i/o memory, convert to virtual address */ | |
104 | if (isdp->id_maddr) { | |
105 | extern unsigned int atdevbase; | |
106 | ||
107 | isdp->id_maddr -= IOM_BEGIN; | |
108 | isdp->id_maddr += atdevbase; | |
109 | } | |
110 | isdp->id_alive = (*dp->probe)(isdp); | |
111 | if (isdp->id_alive) { | |
112 | ||
113 | printf("%s%d at port 0x%x ", dp->name, | |
114 | isdp->id_unit, isdp->id_iobase); | |
115 | ||
116 | /* check for conflicts */ | |
117 | if (irqseen & isdp->id_irq) { | |
118 | printf("INTERRUPT CONFLICT - irq%d\n", | |
119 | ffs(isdp->id_irq) - 1); | |
120 | return (0); | |
121 | } | |
122 | if (isdp->id_drq != -1 | |
123 | && (drqseen & (1<<isdp->id_drq))) { | |
124 | printf("DMA CONFLICT - drq%d\n", isdp->id_drq); | |
125 | return (0); | |
126 | } | |
127 | /* NEED TO CHECK IOMEM CONFLICT HERE */ | |
128 | ||
129 | /* allocate and wire in device */ | |
130 | if(isdp->id_irq) { | |
131 | int intrno; | |
132 | ||
133 | intrno = ffs(isdp->id_irq)-1; | |
134 | printf("irq %d ", intrno); | |
135 | INTREN(isdp->id_irq); | |
136 | if(mp)INTRMASK(*mp,isdp->id_irq); | |
137 | setidt(NRSVIDT + intrno, isdp->id_intr, | |
138 | SDT_SYS386IGT, SEL_KPL); | |
139 | irqseen |= isdp->id_irq; | |
140 | } | |
141 | if (isdp->id_drq != -1) { | |
142 | printf("drq %d ", isdp->id_drq); | |
143 | drqseen |= 1 << isdp->id_drq; | |
144 | } | |
145 | ||
146 | (*dp->attach)(isdp); | |
147 | ||
148 | printf("on isa\n"); | |
149 | } | |
150 | return (1); | |
151 | } else return(0); | |
152 | } | |
153 | #else | |
154 | /* | |
155 | * Configure all ISA devices | |
156 | */ | |
157 | isa_configure() { | |
158 | struct isa_device *dvp; | |
159 | struct isa_driver *dp; | |
160 | ||
161 | splhigh(); | |
162 | INTREN(IRQ_SLAVE); | |
163 | for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++); | |
164 | for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++); | |
165 | for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++); | |
166 | for (dvp = isa_devtab_null; config_isadev(dvp,0); dvp++); | |
167 | #include "sl.h" | |
168 | #if NSL > 0 | |
169 | netmask |= ttymask; | |
170 | ttymask |= netmask; | |
171 | #endif | |
172 | /* biomask |= ttymask ; can some tty devices use buffers? */ | |
173 | /* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ | |
174 | splnone(); | |
175 | } | |
176 | ||
177 | /* | |
178 | * Configure an ISA device. | |
179 | */ | |
180 | config_isadev(isdp, mp) | |
181 | struct isa_device *isdp; | |
182 | u_short *mp; | |
183 | { | |
184 | struct isa_driver *dp; | |
185 | ||
186 | if (dp = isdp->id_driver) { | |
187 | if (isdp->id_maddr) { | |
188 | extern u_int atdevbase; | |
189 | ||
190 | isdp->id_maddr -= 0xa0000; | |
191 | isdp->id_maddr += atdevbase; | |
192 | } | |
193 | isdp->id_alive = (*dp->probe)(isdp); | |
194 | if (isdp->id_alive) { | |
195 | printf("%s%d", dp->name, isdp->id_unit); | |
196 | (*dp->attach)(isdp); | |
197 | printf(" at 0x%x ", isdp->id_iobase); | |
198 | if(isdp->id_irq) { | |
199 | int intrno; | |
200 | ||
201 | intrno = ffs(isdp->id_irq)-1; | |
202 | printf("irq %d ", intrno); | |
203 | INTREN(isdp->id_irq); | |
204 | if(mp)INTRMASK(*mp,isdp->id_irq); | |
205 | setidt(ICU_OFFSET+intrno, isdp->id_intr, | |
206 | SDT_SYS386IGT, SEL_KPL); | |
207 | } | |
208 | if (isdp->id_drq != -1) printf("drq %d ", isdp->id_drq); | |
209 | printf("on isa\n"); | |
210 | } | |
211 | return (1); | |
212 | } else return(0); | |
213 | } | |
214 | #endif | |
215 | ||
216 | #define IDTVEC(name) __CONCAT(X,name) | |
217 | /* default interrupt vector table entries */ | |
218 | extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3), | |
219 | IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7), | |
220 | IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), | |
221 | IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); | |
222 | ||
223 | static *defvec[16] = { | |
224 | &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), | |
225 | &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), | |
226 | &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), | |
227 | &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; | |
228 | ||
229 | /* out of range default interrupt vector gate entry */ | |
230 | extern IDTVEC(intrdefault); | |
231 | ||
232 | /* | |
233 | * Fill in default interrupt table (in case of spuruious interrupt | |
234 | * during configuration of kernel, setup interrupt control unit | |
235 | */ | |
236 | isa_defaultirq() { | |
237 | int i; | |
238 | ||
239 | /* icu vectors */ | |
240 | for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) | |
241 | setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); | |
242 | ||
243 | /* out of range vectors */ | |
244 | for (i = NRSVIDT; i < NIDT; i++) | |
245 | setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); | |
246 | ||
247 | /* clear npx intr latch */ | |
248 | outb(0xf1,0); | |
249 | ||
250 | /* initialize 8259's */ | |
251 | outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ | |
252 | outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ | |
253 | outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ | |
254 | outb(IO_ICU1+1, 1); /* 8086 mode */ | |
255 | outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ | |
256 | outb(IO_ICU1, 2); /* default to ISR on read */ | |
257 | ||
258 | outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ | |
259 | outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ | |
260 | outb(IO_ICU2+1,2); /* my slave id is 2 */ | |
261 | outb(IO_ICU2+1,1); /* 8086 mode */ | |
262 | outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ | |
263 | outb(IO_ICU2, 2); /* default to ISR on read */ | |
264 | } | |
265 | ||
266 | /* region of physical memory known to be contiguous */ | |
267 | vm_offset_t isaphysmem; | |
268 | static caddr_t dma_bounce[8]; /* XXX */ | |
269 | static char bounced[8]; /* XXX */ | |
270 | #define MAXDMASZ 512 /* XXX */ | |
271 | ||
272 | /* high byte of address is stored in this port for i-th dma channel */ | |
273 | static short dmapageport[8] = | |
274 | { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; | |
275 | ||
276 | /* | |
277 | * isa_dmacascade(): program 8237 DMA controller channel to accept | |
278 | * external dma control by a board. | |
279 | */ | |
280 | void isa_dmacascade(unsigned chan) | |
281 | { int modeport; | |
282 | ||
283 | if (chan > 7) | |
284 | panic("isa_dmacascade: impossible request"); | |
285 | ||
286 | /* set dma channel mode, and set dma channel mode */ | |
287 | if ((chan & 4) == 0) | |
288 | modeport = IO_DMA1 + 0xb; | |
289 | else | |
290 | modeport = IO_DMA2 + 0x16; | |
291 | outb(modeport, DMA37MD_CASCADE | (chan & 3)); | |
292 | if ((chan & 4) == 0) | |
293 | outb(modeport - 1, chan & 3); | |
294 | else | |
295 | outb(modeport - 2, chan & 3); | |
296 | } | |
297 | ||
298 | /* | |
299 | * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment | |
300 | * problems by using a bounce buffer. | |
301 | */ | |
302 | void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan) | |
303 | { vm_offset_t phys; | |
304 | int modeport, waport, mskport; | |
305 | caddr_t newaddr; | |
306 | ||
307 | if (chan > 7 || nbytes > (1<<16)) | |
308 | panic("isa_dmastart: impossible request"); | |
309 | ||
310 | if (isa_dmarangecheck(addr, nbytes)) { | |
311 | if (dma_bounce[chan] == 0) | |
312 | dma_bounce[chan] = | |
313 | /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ | |
314 | (caddr_t) isaphysmem + NBPG*chan; | |
315 | bounced[chan] = 1; | |
316 | newaddr = dma_bounce[chan]; | |
317 | *(int *) newaddr = 0; /* XXX */ | |
318 | ||
319 | /* copy bounce buffer on write */ | |
320 | if (!(flags & B_READ)) | |
321 | bcopy(addr, newaddr, nbytes); | |
322 | addr = newaddr; | |
323 | } | |
324 | ||
325 | /* translate to physical */ | |
326 | phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); | |
327 | ||
328 | /* set dma channel mode, and reset address ff */ | |
329 | if ((chan & 4) == 0) | |
330 | modeport = IO_DMA1 + 0xb; | |
331 | else | |
332 | modeport = IO_DMA2 + 0x16; | |
333 | if (flags & B_READ) | |
334 | outb(modeport, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); | |
335 | else | |
336 | outb(modeport, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); | |
337 | if ((chan & 4) == 0) | |
338 | outb(modeport + 1, 0); | |
339 | else | |
340 | outb(modeport + 2, 0); | |
341 | ||
342 | /* send start address */ | |
343 | if ((chan & 4) == 0) { | |
344 | waport = IO_DMA1 + (chan<<1); | |
345 | outb(waport, phys); | |
346 | outb(waport, phys>>8); | |
347 | } else { | |
348 | waport = IO_DMA2 + ((chan - 4)<<2); | |
349 | outb(waport, phys>>1); | |
350 | outb(waport, phys>>9); | |
351 | } | |
352 | outb(dmapageport[chan], phys>>16); | |
353 | ||
354 | /* send count */ | |
355 | if ((chan & 4) == 0) { | |
356 | outb(waport + 1, --nbytes); | |
357 | outb(waport + 1, nbytes>>8); | |
358 | } else { | |
359 | nbytes <<= 1; | |
360 | outb(waport + 2, --nbytes); | |
361 | outb(waport + 2, nbytes>>8); | |
362 | } | |
363 | ||
364 | /* unmask channel */ | |
365 | if ((chan & 4) == 0) | |
366 | mskport = IO_DMA1 + 0x0a; | |
367 | else | |
368 | mskport = IO_DMA2 + 0x14; | |
369 | outb(mskport, chan & 3); | |
370 | } | |
371 | ||
372 | void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) | |
373 | { | |
374 | ||
375 | /* copy bounce buffer on read */ | |
376 | /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/ | |
377 | if (bounced[chan]) { | |
378 | bcopy(dma_bounce[chan], addr, nbytes); | |
379 | bounced[chan] = 0; | |
380 | } | |
381 | } | |
382 | ||
383 | /* | |
384 | * Check for problems with the address range of a DMA transfer | |
385 | * (non-contiguous physical pages, outside of bus address space). | |
386 | * Return true if special handling needed. | |
387 | */ | |
388 | ||
389 | isa_dmarangecheck(caddr_t va, unsigned length) { | |
390 | vm_offset_t phys, priorpage, endva; | |
391 | ||
392 | endva = (vm_offset_t)round_page(va + length); | |
393 | for (; va < (caddr_t) endva ; va += NBPG) { | |
394 | phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); | |
395 | #define ISARAM_END RAM_END | |
396 | if (phys == 0) | |
397 | panic("isa_dmacheck: no physical page present"); | |
398 | if (phys > ISARAM_END) | |
399 | return (1); | |
400 | if (priorpage && priorpage + NBPG != phys) | |
401 | return (1); | |
402 | priorpage = phys; | |
403 | } | |
404 | return (0); | |
405 | } | |
406 | ||
407 | /* head of queue waiting for physmem to become available */ | |
408 | struct buf isa_physmemq; | |
409 | ||
410 | /* blocked waiting for resource to become free for exclusive use */ | |
411 | static isaphysmemflag; | |
412 | /* if waited for and call requested when free (B_CALL) */ | |
413 | static void (*isaphysmemunblock)(); /* needs to be a list */ | |
414 | ||
415 | /* | |
416 | * Allocate contiguous physical memory for transfer, returning | |
417 | * a *virtual* address to region. May block waiting for resource. | |
418 | * (assumed to be called at splbio()) | |
419 | */ | |
420 | caddr_t | |
421 | isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { | |
422 | ||
423 | isaphysmemunblock = func; | |
424 | while (isaphysmemflag & B_BUSY) { | |
425 | isaphysmemflag |= B_WANTED; | |
426 | sleep(&isaphysmemflag, PRIBIO); | |
427 | } | |
428 | isaphysmemflag |= B_BUSY; | |
429 | ||
430 | return((caddr_t)isaphysmem); | |
431 | } | |
432 | ||
433 | /* | |
434 | * Free contiguous physical memory used for transfer. | |
435 | * (assumed to be called at splbio()) | |
436 | */ | |
437 | void | |
438 | isa_freephysmem(caddr_t va, unsigned length) { | |
439 | ||
440 | isaphysmemflag &= ~B_BUSY; | |
441 | if (isaphysmemflag & B_WANTED) { | |
442 | isaphysmemflag &= B_WANTED; | |
443 | wakeup(&isaphysmemflag); | |
444 | if (isaphysmemunblock) | |
445 | (*isaphysmemunblock)(); | |
446 | } | |
447 | } | |
448 | ||
449 | /* | |
450 | * Handle a NMI, possibly a machine check. | |
451 | * return true to panic system, false to ignore. | |
452 | */ | |
453 | isa_nmi(cd) { | |
454 | ||
455 | log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); | |
456 | return(0); | |
457 | } | |
458 | ||
459 | /* | |
460 | * Caught a stray interrupt, notify | |
461 | */ | |
462 | isa_strayintr(d) { | |
463 | ||
464 | #ifdef notdef | |
465 | /* DON'T BOTHER FOR NOW! */ | |
466 | /* for some reason, we get bursts of intr #7, even if not enabled! */ | |
467 | log(LOG_ERR,"ISA strayintr %x", d); | |
468 | #endif | |
469 | } | |
470 | ||
471 | /* | |
472 | * Wait "n" microseconds. Relies on timer 0 to have 1Mhz clock, regardless | |
473 | * of processor board speed. Note: timer had better have been programmed | |
474 | * before this is first used! | |
475 | */ | |
476 | DELAY(n) { | |
477 | int tick = getit(0,0) & 1; | |
478 | ||
479 | while (n--) { | |
480 | /* wait approximately 1 micro second */ | |
481 | while (tick == getit(0,0) & 1) ; | |
482 | ||
483 | tick = getit(0,0) & 1; | |
484 | } | |
485 | } | |
486 | ||
487 | getit(unit, timer) { | |
488 | int port = (unit ? IO_TIMER2 : IO_TIMER1) + timer, val; | |
489 | ||
490 | val = inb(port); | |
491 | val = (inb(port) << 8) + val; | |
492 | return (val); | |
493 | } | |
494 | ||
495 | extern int hz; | |
496 | ||
497 | static beeping; | |
498 | static | |
499 | sysbeepstop(f) | |
500 | { | |
501 | /* disable counter 2 */ | |
502 | outb(0x61, inb(0x61) & 0xFC); | |
503 | if (f) | |
504 | timeout(sysbeepstop, 0, f); | |
505 | else | |
506 | beeping = 0; | |
507 | } | |
508 | ||
509 | void sysbeep(int pitch, int period) | |
510 | { | |
511 | ||
512 | outb(0x61, inb(0x61) | 3); /* enable counter 2 */ | |
513 | outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ | |
514 | ||
515 | outb(0x42, pitch); | |
516 | outb(0x42, (pitch>>8)); | |
517 | ||
518 | if (!beeping) { | |
519 | beeping = period; | |
520 | timeout(sysbeepstop, period/2, period); | |
521 | } | |
522 | } | |
523 | ||
524 | /* | |
525 | * Pass command to keyboard controller (8042) | |
526 | */ | |
527 | unsigned kbc_8042cmd(val) { | |
528 | ||
529 | while (inb(KBSTATP)&KBS_IBF); | |
530 | if (val) outb(KBCMDP, val); | |
531 | while (inb(KBSTATP)&KBS_IBF); | |
532 | return (inb(KBDATAP)); | |
533 | } |