isa.c revision 1.49 1 /*-
2 * Copyright (c) 1993, 1994 Charles Hannum.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * William Jolitz.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91
38 * $Id: isa.c,v 1.49 1994/04/07 06:50:54 mycroft Exp $
39 */
40
41 /*
42 * Code to manage AT bus
43 */
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/conf.h>
49 #include <sys/file.h>
50 #include <sys/buf.h>
51 #include <sys/uio.h>
52 #include <sys/syslog.h>
53 #include <sys/malloc.h>
54 #include <sys/device.h>
55
56 #include <vm/vm.h>
57
58 #include <machine/pio.h>
59 #include <machine/cpufunc.h>
60
61 #include <i386/isa/isa.h>
62 #include <i386/isa/isa_device.h>
63 #include <i386/isa/isavar.h>
64 #include <i386/isa/ic/i8237.h>
65 #include <i386/isa/ic/i8042.h>
66 #include <i386/isa/timerreg.h>
67 #include <i386/isa/spkr_reg.h>
68
69 /* sorry, has to be here, no place else really suitable */
70 #include <machine/pc/display.h>
71 u_short *Crtat = (u_short *)MONO_BUF;
72
73 /*
74 * Register definitions for DMA controller 1 (channels 0..3):
75 */
76 #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */
77 #define DMA1_SR (IO_DMA1 + 1*8) /* status register */
78 #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */
79 #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */
80 #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */
81
82 /*
83 * Register definitions for DMA controller 2 (channels 4..7):
84 */
85 #define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */
86 #define DMA2_SR (IO_DMA2 + 2*8) /* status register */
87 #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */
88 #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */
89 #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */
90
91 static void sysbeepstop(int);
92
93 /*
94 * Configure all ISA devices
95 *
96 * XXX This code is a hack. It wants to be new config, but can't be until the
97 * interrupt system is redone. For now, we do some gross hacks to make it look
98 * 99% like new config.
99 */
100 static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
101
102 struct cfdata *
103 config_search(fn, parent, aux)
104 cfmatch_t fn;
105 struct device *parent;
106 void *aux;
107 {
108 struct cfdata *cf = 0;
109 struct device *dv = 0;
110 size_t devsize;
111 struct cfdriver *cd;
112 struct isa_device *id,
113 *idp = parent ? (void *)parent->dv_cfdata->cf_loc : 0;
114
115 for (id = isa_devtab; id->id_driver; id++) {
116 if (id->id_state == FSTATE_FOUND)
117 continue;
118 if (id->id_parent != idp)
119 continue;
120 cd = id->id_driver;
121 if (id->id_unit < cd->cd_ndevs) {
122 if (cd->cd_devs[id->id_unit] != 0)
123 continue;
124 } else {
125 int old = cd->cd_ndevs, new;
126 void **nsp;
127
128 if (old == 0) {
129 nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_NOWAIT);
130 if (!nsp)
131 panic("config_search: creating dev array");
132 bzero(nsp, MINALLOCSIZE);
133 cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
134 } else {
135 new = old;
136 do {
137 new *= 2;
138 } while (new <= id->id_unit);
139 cd->cd_ndevs = new;
140 nsp = malloc(new * sizeof(void *), M_DEVBUF,
141 M_NOWAIT);
142 if (!nsp)
143 panic("config_search: expanding dev array");
144 bzero(nsp, new * sizeof(void *));
145 bcopy(cd->cd_devs, nsp, old * sizeof(void *));
146 free(cd->cd_devs, M_DEVBUF);
147 }
148 cd->cd_devs = nsp;
149 }
150 if (!cf) {
151 cf = malloc(sizeof(struct cfdata), M_DEVBUF, M_NOWAIT); if (!cf)
152 panic("config_search: creating cfdata");
153 }
154 cf->cf_driver = cd;
155 cf->cf_unit = id->id_unit;
156 cf->cf_fstate = 0;
157 cf->cf_loc = (void *)id;
158 cf->cf_flags = id->id_flags;
159 cf->cf_parents = 0;
160 cf->cf_ivstubs = 0;
161 if (dv && devsize != cd->cd_devsize) {
162 free(dv, M_DEVBUF);
163 dv = 0;
164 }
165 if (!dv) {
166 devsize = cd->cd_devsize;
167 dv = malloc(devsize, M_DEVBUF, M_NOWAIT);
168 if (!dv)
169 panic("config_search: creating softc");
170 }
171 bzero(dv, cd->cd_devsize);
172 dv->dv_class = cd->cd_class;
173 dv->dv_cfdata = cf;
174 dv->dv_unit = id->id_unit;
175 sprintf(dv->dv_xname, "%s%d", cd->cd_name, id->id_unit);
176 dv->dv_parent = parent;
177 cd->cd_devs[id->id_unit] = dv;
178 if (fn) {
179 if ((*fn)(parent, dv, aux))
180 return cf;
181 } else {
182 if ((*cd->cd_match)(parent, dv, aux))
183 return cf;
184 }
185 cd->cd_devs[id->id_unit] = 0;
186 }
187 if (cf)
188 free(cf, M_DEVBUF);
189 if (dv)
190 free(dv, M_DEVBUF);
191 return 0;
192 }
193
194 void
195 config_attach(parent, cf, aux, print)
196 struct device *parent;
197 struct cfdata *cf;
198 void *aux;
199 cfprint_t print;
200 {
201 struct isa_device *id = (void *)cf->cf_loc;
202 struct cfdriver *cd = cf->cf_driver;
203 struct device *dv = cd->cd_devs[id->id_unit];
204
205 cf->cf_fstate = id->id_state = FSTATE_FOUND;
206 printf("%s at %s", dv->dv_xname, parent ? parent->dv_xname : "isa0");
207 if (print)
208 (void) (*print)(aux, (char *)0);
209 (*cd->cd_attach)(parent, dv, aux);
210 }
211
212 int
213 config_found(parent, aux, print)
214 struct device *parent;
215 void *aux;
216 cfprint_t print;
217 {
218 struct cfdata *cf;
219
220 if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
221 config_attach(parent, cf, aux, print);
222 return 1;
223 }
224 if (print)
225 printf(msgs[(*print)(aux, parent->dv_xname)]);
226 return 0;
227 }
228
229 int
230 isaprint(aux, isa)
231 void *aux;
232 char *isa;
233 {
234 struct isa_attach_args *ia = aux;
235
236 if (ia->ia_iosize)
237 printf(" port 0x%x", ia->ia_iobase);
238 if (ia->ia_iosize > 1)
239 printf("-0x%x", ia->ia_iobase + ia->ia_iosize - 1);
240 if (ia->ia_msize)
241 printf(" iomem 0x%x", ia->ia_maddr - atdevbase + 0xa0000);
242 if (ia->ia_msize > 1)
243 printf("-0x%x",
244 ia->ia_maddr - atdevbase + 0xa0000 + ia->ia_msize - 1);
245 if (ia->ia_irq)
246 printf(" irq %d", ffs(ia->ia_irq) - 1);
247 if (ia->ia_drq != (u_short)-1)
248 printf(" drq %d", ia->ia_drq);
249 /* XXXX print flags */
250 return QUIET;
251 }
252
253 int
254 isasubmatch(parent, self, aux)
255 struct device *parent, *self;
256 void *aux;
257 {
258 struct isa_device *id = (void *)self->dv_cfdata->cf_loc;
259 struct isa_attach_args ia;
260
261 ia.ia_iobase = id->id_iobase;
262 ia.ia_iosize = 0x666;
263 ia.ia_irq = id->id_irq;
264 ia.ia_drq = id->id_drq;
265 ia.ia_maddr = id->id_maddr - 0xa0000 + atdevbase;
266 ia.ia_msize = id->id_msize;
267
268 if (!(*id->id_driver->cd_match)(parent, self, &ia)) {
269 /*
270 * If we don't do this, isa_configure() will repeatedly try to
271 * probe devices that weren't found. But we need to be careful
272 * to do it only for the ISA bus, or we would cause things like
273 * `com0 at ast? slave ?' to not probe on the second ast.
274 */
275 if (!parent)
276 id->id_state = FSTATE_FOUND;
277
278 return 0;
279 }
280
281 config_attach(parent, self->dv_cfdata, &ia, isaprint);
282
283 return 1;
284 }
285
286 void
287 isa_configure()
288 {
289
290 while (config_search(isasubmatch, NULL, NULL));
291
292 printf("biomask %x netmask %x ttymask %x\n",
293 (u_short)imask[IPL_BIO], (u_short)imask[IPL_NET],
294 (u_short)imask[IPL_TTY]);
295
296 spl0();
297 }
298
299 /* region of physical memory known to be contiguous */
300 vm_offset_t isaphysmem;
301 static caddr_t dma_bounce[8]; /* XXX */
302 static char bounced[8]; /* XXX */
303 #define MAXDMASZ 512 /* XXX */
304
305 /* high byte of address is stored in this port for i-th dma channel */
306 static short dmapageport[8] =
307 { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
308
309 /*
310 * isa_dmacascade(): program 8237 DMA controller channel to accept
311 * external dma control by a board.
312 */
313 void
314 isa_dmacascade(chan)
315 int chan;
316 {
317
318 #ifdef DIAGNOSTIC
319 if (chan < 0 || chan > 7)
320 panic("isa_dmacascade: impossible request");
321 #endif
322
323 /* set dma channel mode, and set dma channel mode */
324 if ((chan & 4) == 0) {
325 outb(DMA1_MODE, DMA37MD_CASCADE | chan);
326 outb(DMA1_SMSK, chan);
327 } else {
328 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
329 outb(DMA2_SMSK, chan & 3);
330 }
331 }
332
333 /*
334 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
335 * problems by using a bounce buffer.
336 */
337 void
338 isa_dmastart(flags, addr, nbytes, chan)
339 int flags;
340 caddr_t addr;
341 vm_size_t nbytes;
342 int chan;
343 {
344 vm_offset_t phys;
345 int waport;
346 caddr_t newaddr;
347
348 #ifdef DIAGNOSTIC
349 if (chan < 0 || chan > 7 ||
350 ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) :
351 (nbytes >= (1<<16))))
352 panic("isa_dmastart: impossible request");
353 #endif
354
355 if (isa_dmarangecheck(addr, nbytes, chan)) {
356 if (dma_bounce[chan] == 0)
357 dma_bounce[chan] =
358 /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/
359 (caddr_t) isaphysmem + NBPG*chan;
360 bounced[chan] = 1;
361 newaddr = dma_bounce[chan];
362 *(int *) newaddr = 0; /* XXX */
363 /* copy bounce buffer on write */
364 if ((flags & B_READ) == 0)
365 bcopy(addr, newaddr, nbytes);
366 addr = newaddr;
367 }
368
369 /* translate to physical */
370 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
371
372 if ((chan & 4) == 0) {
373 /*
374 * Program one of DMA channels 0..3. These are
375 * byte mode channels.
376 */
377 /* set dma channel mode, and reset address ff */
378 if (flags & B_READ)
379 outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_WRITE);
380 else
381 outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_READ);
382 outb(DMA1_FFC, 0);
383
384 /* send start address */
385 waport = DMA1_CHN(chan);
386 outb(waport, phys);
387 outb(waport, phys>>8);
388 outb(dmapageport[chan], phys>>16);
389
390 /* send count */
391 outb(waport + 1, --nbytes);
392 outb(waport + 1, nbytes>>8);
393
394 /* unmask channel */
395 outb(DMA1_SMSK, chan | DMA37SM_CLEAR);
396 } else {
397 /*
398 * Program one of DMA channels 4..7. These are
399 * word mode channels.
400 */
401 /* set dma channel mode, and reset address ff */
402 if (flags & B_READ)
403 outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_WRITE);
404 else
405 outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_READ);
406 outb(DMA2_FFC, 0);
407
408 /* send start address */
409 waport = DMA2_CHN(chan & 3);
410 outb(waport, phys>>1);
411 outb(waport, phys>>9);
412 outb(dmapageport[chan], phys>>16);
413
414 /* send count */
415 nbytes >>= 1;
416 outb(waport + 2, --nbytes);
417 outb(waport + 2, nbytes>>8);
418
419 /* unmask channel */
420 outb(DMA2_SMSK, (chan & 3) | DMA37SM_CLEAR);
421 }
422 }
423
424 void
425 isa_dmaabort(chan)
426 int chan;
427 {
428
429 #ifdef DIAGNOSTIC
430 if (chan < 0 || chan > 7)
431 panic("isa_dmadone: impossible request");
432 #endif
433
434 bounced[chan] = 0;
435
436 /* mask channel */
437 if ((chan & 4) == 0)
438 outb(DMA1_SMSK, DMA37SM_SET | chan);
439 else
440 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
441 }
442
443 void
444 isa_dmadone(flags, addr, nbytes, chan)
445 int flags;
446 caddr_t addr;
447 vm_size_t nbytes;
448 int chan;
449 {
450 u_char tc;
451
452 #ifdef DIAGNOSTIC
453 if (chan < 0 || chan > 7)
454 panic("isa_dmadone: impossible request");
455 #endif
456
457 /* check that the terminal count was reached */
458 if ((chan & 4) == 0)
459 tc = inb(DMA1_SR) & (1 << chan);
460 else
461 tc = inb(DMA2_SR) & (1 << (chan & 3));
462 if (tc == 0)
463 /* XXX probably should panic or something */
464 log(LOG_ERR, "dma channel %d not finished\n", chan);
465
466 /* copy bounce buffer on read */
467 if (bounced[chan]) {
468 bcopy(dma_bounce[chan], addr, nbytes);
469 bounced[chan] = 0;
470 }
471
472 /* mask channel */
473 if ((chan & 4) == 0)
474 outb(DMA1_SMSK, DMA37SM_SET | chan);
475 else
476 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3));
477 }
478
479 /*
480 * Check for problems with the address range of a DMA transfer
481 * (non-contiguous physical pages, outside of bus address space,
482 * crossing DMA page boundaries).
483 * Return true if special handling needed.
484 */
485 int
486 isa_dmarangecheck(va, length, chan)
487 vm_offset_t va;
488 u_long length;
489 int chan;
490 {
491 vm_offset_t phys, priorpage = 0, endva;
492 u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1);
493
494 endva = round_page(va + length);
495 for (; va < endva ; va += NBPG) {
496 phys = trunc_page(pmap_extract(pmap_kernel(), va));
497 if (phys == 0)
498 panic("isa_dmacheck: no physical page present");
499 if (phys >= (1<<24))
500 return 1;
501 if (priorpage) {
502 if (priorpage + NBPG != phys)
503 return 1;
504 /* check if crossing a DMA page boundary */
505 if ((priorpage ^ phys) & dma_pgmsk)
506 return 1;
507 }
508 priorpage = phys;
509 }
510 return 0;
511 }
512
513 /* head of queue waiting for physmem to become available */
514 struct buf isa_physmemq;
515
516 /* blocked waiting for resource to become free for exclusive use */
517 static isaphysmemflag;
518 /* if waited for and call requested when free (B_CALL) */
519 static void (*isaphysmemunblock)(); /* needs to be a list */
520
521 /*
522 * Allocate contiguous physical memory for transfer, returning
523 * a *virtual* address to region. May block waiting for resource.
524 * (assumed to be called at splbio())
525 */
526 caddr_t
527 isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) {
528
529 isaphysmemunblock = func;
530 while (isaphysmemflag & B_BUSY) {
531 isaphysmemflag |= B_WANTED;
532 sleep((caddr_t)&isaphysmemflag, PRIBIO);
533 }
534 isaphysmemflag |= B_BUSY;
535
536 return((caddr_t)isaphysmem);
537 }
538
539 /*
540 * Free contiguous physical memory used for transfer.
541 * (assumed to be called at splbio())
542 */
543 void
544 isa_freephysmem(caddr_t va, unsigned length) {
545
546 isaphysmemflag &= ~B_BUSY;
547 if (isaphysmemflag & B_WANTED) {
548 isaphysmemflag &= B_WANTED;
549 wakeup((caddr_t)&isaphysmemflag);
550 if (isaphysmemunblock)
551 (*isaphysmemunblock)();
552 }
553 }
554
555 static int beeping;
556
557 static void
558 sysbeepstop(int f)
559 {
560 int s = splhigh();
561
562 /* disable counter 2 */
563 disable_intr();
564 outb(PITAUX_PORT, inb(PITAUX_PORT) & ~PIT_SPKR);
565 enable_intr();
566 if (f)
567 timeout((timeout_t)sysbeepstop, (caddr_t)0, f);
568 else
569 beeping = 0;
570
571 splx(s);
572 }
573
574 void
575 sysbeep(int pitch, int period)
576 {
577 int s = splhigh();
578 static int last_pitch, last_period;
579
580 if (beeping) {
581 untimeout((timeout_t)sysbeepstop, (caddr_t)(last_period/2));
582 untimeout((timeout_t)sysbeepstop, (caddr_t)0);
583 }
584 if (!beeping || last_pitch != pitch) {
585 /*
586 * XXX - move timer stuff to clock.c.
587 */
588 disable_intr();
589 outb(TIMER_MODE, TIMER_SEL2|TIMER_16BIT|TIMER_SQWAVE);
590 outb(TIMER_CNTR2, TIMER_DIV(pitch)%256);
591 outb(TIMER_CNTR2, TIMER_DIV(pitch)/256);
592 outb(PITAUX_PORT, inb(PITAUX_PORT) | PIT_SPKR); /* enable counter 2 */
593 enable_intr();
594 }
595 last_pitch = pitch;
596 beeping = last_period = period;
597 timeout((timeout_t)sysbeepstop, (caddr_t)(period/2), period);
598
599 splx(s);
600 }
601