isa.c revision 2056
14Srgrimes/*- 24Srgrimes * Copyright (c) 1991 The Regents of the University of California. 34Srgrimes * All rights reserved. 44Srgrimes * 54Srgrimes * This code is derived from software contributed to Berkeley by 64Srgrimes * William Jolitz. 74Srgrimes * 84Srgrimes * Redistribution and use in source and binary forms, with or without 94Srgrimes * modification, are permitted provided that the following conditions 104Srgrimes * are met: 114Srgrimes * 1. Redistributions of source code must retain the above copyright 124Srgrimes * notice, this list of conditions and the following disclaimer. 134Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 144Srgrimes * notice, this list of conditions and the following disclaimer in the 154Srgrimes * documentation and/or other materials provided with the distribution. 164Srgrimes * 3. All advertising materials mentioning features or use of this software 174Srgrimes * must display the following acknowledgement: 184Srgrimes * This product includes software developed by the University of 194Srgrimes * California, Berkeley and its contributors. 204Srgrimes * 4. Neither the name of the University nor the names of its contributors 214Srgrimes * may be used to endorse or promote products derived from this software 224Srgrimes * without specific prior written permission. 234Srgrimes * 244Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 254Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 264Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 274Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 284Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 294Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 304Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 314Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 324Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 334Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 344Srgrimes * SUCH DAMAGE. 354Srgrimes * 36593Srgrimes * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 372056Swollman * $Id: isa.c,v 1.19 1994/08/10 04:39:52 wollman Exp $ 384Srgrimes */ 394Srgrimes 404Srgrimes/* 414Srgrimes * code to manage AT bus 424Srgrimes * 434Srgrimes * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): 444Srgrimes * Fixed uninitialized variable problem and added code to deal 454Srgrimes * with DMA page boundaries in isa_dmarangecheck(). Fixed word 464Srgrimes * mode DMA count compution and reorganized DMA setup code in 474Srgrimes * isa_dmastart() 484Srgrimes */ 494Srgrimes 502056Swollman#include <sys/param.h> 512056Swollman#include <sys/systm.h> /* isn't it a joy */ 522056Swollman#include <sys/kernel.h> /* to have three of these */ 532056Swollman#include <sys/conf.h> 542056Swollman#include <sys/file.h> 552056Swollman#include <sys/buf.h> 562056Swollman#include <sys/uio.h> 572056Swollman#include <sys/syslog.h> 582056Swollman#include <sys/malloc.h> 592056Swollman#include <sys/rlist.h> 602056Swollman#include <machine/segments.h> 612056Swollman#include <vm/vm.h> 621549Srgrimes#include <machine/spl.h> 632056Swollman#include <i386/isa/isa_device.h> 642056Swollman#include <i386/isa/isa.h> 652056Swollman#include <i386/isa/icu.h> 662056Swollman#include <i386/isa/ic/i8237.h> 672056Swollman#include <i386/isa/ic/i8042.h> 684Srgrimes 694Srgrimes/* 704Srgrimes** Register definitions for DMA controller 1 (channels 0..3): 714Srgrimes*/ 724Srgrimes#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ 734Srgrimes#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ 744Srgrimes#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ 754Srgrimes#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ 764Srgrimes 774Srgrimes/* 784Srgrimes** Register definitions for DMA controller 2 (channels 4..7): 794Srgrimes*/ 80630Srgrimes#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ 814Srgrimes#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ 824Srgrimes#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ 834Srgrimes#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ 844Srgrimes 85798Swollmanvoid config_isadev __P((struct isa_device *, u_int *)); 864Srgrimes 874Srgrimes/* 88593Srgrimes * print a conflict message 894Srgrimes */ 90593Srgrimesvoid 91593Srgrimesconflict(dvp, tmpdvp, item, reason, format) 92593Srgrimes struct isa_device *dvp, *tmpdvp; 93593Srgrimes int item; 94593Srgrimes char *reason; 95593Srgrimes char *format; 96593Srgrimes{ 97593Srgrimes printf("%s%d not probed due to %s conflict with %s%d at ", 98593Srgrimes dvp->id_driver->name, dvp->id_unit, reason, 99593Srgrimes tmpdvp->id_driver->name, tmpdvp->id_unit); 100593Srgrimes printf(format, item); 101593Srgrimes printf("\n"); 1024Srgrimes} 1034Srgrimes 1044Srgrimes/* 105593Srgrimes * Check to see if things are alread in use, like IRQ's, I/O addresses 106593Srgrimes * and Memory addresses. 1074Srgrimes */ 108593Srgrimesint 109593Srgrimeshaveseen(dvp, tmpdvp) 110593Srgrimes struct isa_device *dvp, *tmpdvp; 1114Srgrimes{ 112593Srgrimes int status = 0; 1134Srgrimes 114593Srgrimes /* 115593Srgrimes * Only check against devices that have already been found 116593Srgrimes */ 117593Srgrimes if (tmpdvp->id_alive) { 118593Srgrimes /* 119593Srgrimes * Check for I/O address conflict. We can only check the 120593Srgrimes * starting address of the device against the range of the 121593Srgrimes * device that has already been probed since we do not 122593Srgrimes * know how many I/O addresses this device uses. 123593Srgrimes */ 124593Srgrimes if (tmpdvp->id_alive != -1) { 125593Srgrimes if ((dvp->id_iobase >= tmpdvp->id_iobase) && 126593Srgrimes (dvp->id_iobase <= 127593Srgrimes (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { 128593Srgrimes conflict(dvp, tmpdvp, dvp->id_iobase, 129593Srgrimes "I/O address", "0x%x"); 130593Srgrimes status = 1; 131593Srgrimes } 1324Srgrimes } 133593Srgrimes /* 134593Srgrimes * Check for Memory address conflict. We can check for 135593Srgrimes * range overlap, but it will not catch all cases since the 136593Srgrimes * driver may adjust the msize paramater during probe, for 137593Srgrimes * now we just check that the starting address does not 138593Srgrimes * fall within any allocated region. 139593Srgrimes * XXX could add a second check after the probe for overlap, 140593Srgrimes * since at that time we would know the full range. 141593Srgrimes * XXX KERNBASE is a hack, we should have vaddr in the table! 142593Srgrimes */ 143593Srgrimes if(tmpdvp->id_maddr) { 144593Srgrimes if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && 145593Srgrimes (KERNBASE + dvp->id_maddr <= 146593Srgrimes (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { 147593Srgrimes conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", 148593Srgrimes "0x%x"); 149593Srgrimes status = 1; 1504Srgrimes } 151593Srgrimes } 152736Salm#ifndef COM_MULTIPORT 153593Srgrimes /* 154593Srgrimes * Check for IRQ conflicts. 155593Srgrimes */ 156593Srgrimes if(tmpdvp->id_irq) { 157593Srgrimes if (tmpdvp->id_irq == dvp->id_irq) { 158593Srgrimes conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1, 159593Srgrimes "irq", "%d"); 160593Srgrimes status = 1; 1614Srgrimes } 162593Srgrimes } 163736Salm#endif 164593Srgrimes /* 165593Srgrimes * Check for DRQ conflicts. 166593Srgrimes */ 167593Srgrimes if(tmpdvp->id_drq != -1) { 168593Srgrimes if (tmpdvp->id_drq == dvp->id_drq) { 169593Srgrimes conflict(dvp, tmpdvp, dvp->id_drq, 170593Srgrimes "drq", "%d"); 171593Srgrimes status = 1; 1724Srgrimes } 173593Srgrimes } 174593Srgrimes } 175593Srgrimes return (status); 176593Srgrimes} 1774Srgrimes 178593Srgrimes/* 179593Srgrimes * Search through all the isa_devtab_* tables looking for anything that 180593Srgrimes * conflicts with the current device. 181593Srgrimes */ 182593Srgrimesint 183593Srgrimeshaveseen_isadev(dvp) 184593Srgrimes struct isa_device *dvp; 185593Srgrimes{ 186593Srgrimes struct isa_device *tmpdvp; 187593Srgrimes int status = 0; 1884Srgrimes 189593Srgrimes for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { 190593Srgrimes status |= haveseen(dvp, tmpdvp); 191593Srgrimes } 192593Srgrimes for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { 193593Srgrimes status |= haveseen(dvp, tmpdvp); 194593Srgrimes } 195593Srgrimes for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { 196593Srgrimes status |= haveseen(dvp, tmpdvp); 197593Srgrimes } 198593Srgrimes for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { 199593Srgrimes status |= haveseen(dvp, tmpdvp); 200593Srgrimes } 201593Srgrimes return(status); 2024Srgrimes} 203593Srgrimes 2044Srgrimes/* 2054Srgrimes * Configure all ISA devices 2064Srgrimes */ 207593Srgrimesvoid 2084Srgrimesisa_configure() { 2094Srgrimes struct isa_device *dvp; 2104Srgrimes 2114Srgrimes enable_intr(); 2124Srgrimes splhigh(); 2134Srgrimes INTREN(IRQ_SLAVE); 214593Srgrimes printf("Probing for devices on the ISA bus:\n"); 215593Srgrimes for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { 216593Srgrimes if (!haveseen_isadev(dvp)) 2171321Sdg config_isadev(dvp,&tty_imask); 218593Srgrimes } 219593Srgrimes for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { 220593Srgrimes if (!haveseen_isadev(dvp)) 2211321Sdg config_isadev(dvp,&bio_imask); 222593Srgrimes } 223593Srgrimes for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { 224593Srgrimes if (!haveseen_isadev(dvp)) 2251321Sdg config_isadev(dvp,&net_imask); 226593Srgrimes } 227593Srgrimes for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { 228593Srgrimes if (!haveseen_isadev(dvp)) 229593Srgrimes config_isadev(dvp,(u_int *) NULL); 230593Srgrimes } 2311321Sdg bio_imask |= SWI_CLOCK_MASK; 2321321Sdg net_imask |= SWI_NET_MASK; 2331321Sdg tty_imask |= SWI_TTY_MASK; 2341321Sdg 235593Srgrimes/* 2361321Sdg * XXX we should really add the tty device to net_imask when the line is 237593Srgrimes * switched to SLIPDISC, and then remove it when it is switched away from 2381321Sdg * SLIPDISC. No need to block out ALL ttys during a splimp when only one 239593Srgrimes * of them is running slip. 2401321Sdg * 2411321Sdg * XXX actually, blocking all ttys during a splimp doesn't matter so much 2421321Sdg * with sio because the serial interrupt layer doesn't use tty_imask. Only 2431321Sdg * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked 2441321Sdg * during spltty. 245593Srgrimes */ 2464Srgrimes#include "sl.h" 2474Srgrimes#if NSL > 0 2481321Sdg net_imask |= tty_imask; 2491321Sdg tty_imask = net_imask; 2504Srgrimes#endif 2511321Sdg /* bio_imask |= tty_imask ; can some tty devices use buffers? */ 2521321Sdg#ifdef DIAGNOSTIC 2531321Sdg printf("bio_imask %x tty_imask %x net_imask %x\n", 2541321Sdg bio_imask, tty_imask, net_imask); 2551321Sdg#endif 2564Srgrimes splnone(); 2574Srgrimes} 2584Srgrimes 2594Srgrimes/* 2604Srgrimes * Configure an ISA device. 2614Srgrimes */ 262798Swollmanvoid 2634Srgrimesconfig_isadev(isdp, mp) 2644Srgrimes struct isa_device *isdp; 2654Srgrimes u_int *mp; 2664Srgrimes{ 267593Srgrimes struct isa_driver *dp = isdp->id_driver; 2684Srgrimes 269593Srgrimes if (isdp->id_maddr) { 270593Srgrimes extern u_int atdevbase; 2714Srgrimes 272593Srgrimes isdp->id_maddr -= 0xa0000; /* XXX should be a define */ 273593Srgrimes isdp->id_maddr += atdevbase; 274593Srgrimes } 275593Srgrimes isdp->id_alive = (*dp->probe)(isdp); 276593Srgrimes if (isdp->id_alive) { 277593Srgrimes /* 278593Srgrimes * Only print the I/O address range if id_alive != -1 279593Srgrimes * Right now this is a temporary fix just for the new 280593Srgrimes * NPX code so that if it finds a 486 that can use trap 281593Srgrimes * 16 it will not report I/O addresses. 282593Srgrimes * Rod Grimes 04/26/94 283593Srgrimes */ 284593Srgrimes printf("%s%d", dp->name, isdp->id_unit); 285593Srgrimes if (isdp->id_alive != -1) { 286593Srgrimes printf(" at 0x%x", isdp->id_iobase); 287593Srgrimes if ((isdp->id_iobase + isdp->id_alive - 1) != 288593Srgrimes isdp->id_iobase) { 289593Srgrimes printf("-0x%x", 290593Srgrimes isdp->id_iobase + 291593Srgrimes isdp->id_alive - 1); 292593Srgrimes } 2934Srgrimes } 294593Srgrimes if(isdp->id_irq) 295593Srgrimes printf(" irq %d", ffs(isdp->id_irq) - 1); 296593Srgrimes if (isdp->id_drq != -1) 297593Srgrimes printf(" drq %d", isdp->id_drq); 298593Srgrimes if (isdp->id_maddr) 299593Srgrimes printf(" maddr 0x%x", kvtop(isdp->id_maddr)); 300593Srgrimes if (isdp->id_msize) 301593Srgrimes printf(" msize %d", isdp->id_msize); 302593Srgrimes if (isdp->id_flags) 303593Srgrimes printf(" flags 0x%x", isdp->id_flags); 3041002Srgrimes if (isdp->id_iobase) { 3051002Srgrimes if (isdp->id_iobase < 0x100) { 3061002Srgrimes printf(" on motherboard\n"); 3071002Srgrimes } else { 3081002Srgrimes if (isdp->id_iobase >= 0x1000) { 3091002Srgrimes printf (" on eisa\n"); 3101002Srgrimes } else { 3111002Srgrimes printf (" on isa\n"); 3121002Srgrimes } 3131002Srgrimes } 3141002Srgrimes } 3154Srgrimes 316593Srgrimes (*dp->attach)(isdp); 31724Srgrimes 318593Srgrimes if(isdp->id_irq) { 319593Srgrimes int intrno; 3204Srgrimes 321593Srgrimes intrno = ffs(isdp->id_irq)-1; 322593Srgrimes setidt(ICU_OFFSET+intrno, isdp->id_intr, 323593Srgrimes SDT_SYS386IGT, SEL_KPL); 324593Srgrimes if(mp) { 325593Srgrimes INTRMASK(*mp,isdp->id_irq); 3264Srgrimes } 327593Srgrimes INTREN(isdp->id_irq); 3284Srgrimes } 329593Srgrimes } else { 330593Srgrimes printf("%s%d not found", dp->name, isdp->id_unit); 331593Srgrimes if (isdp->id_iobase) { 332593Srgrimes printf(" at 0x%x", isdp->id_iobase); 333593Srgrimes } 334593Srgrimes printf("\n"); 335593Srgrimes } 3364Srgrimes} 3374Srgrimes 3384Srgrimes#define IDTVEC(name) __CONCAT(X,name) 3394Srgrimes/* default interrupt vector table entries */ 340879Swollmantypedef void inthand_t(); 341879Swollmantypedef void (*inthand_func_t)(); 342879Swollmanextern inthand_t 343879Swollman IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3), 3444Srgrimes IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7), 3454Srgrimes IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), 3464Srgrimes IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); 3474Srgrimes 3481321Sdgstatic inthand_func_t defvec[ICU_LEN] = { 3494Srgrimes &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), 3504Srgrimes &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), 3514Srgrimes &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), 3524Srgrimes &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; 3534Srgrimes 3544Srgrimes/* 3554Srgrimes * Fill in default interrupt table (in case of spuruious interrupt 3564Srgrimes * during configuration of kernel, setup interrupt control unit 3574Srgrimes */ 358798Swollmanvoid 359798Swollmanisa_defaultirq() 360798Swollman{ 3614Srgrimes int i; 3624Srgrimes 3634Srgrimes /* icu vectors */ 3641321Sdg for (i = 0; i < ICU_LEN; i++) 3651321Sdg setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL); 3664Srgrimes 3674Srgrimes /* initialize 8259's */ 3684Srgrimes outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ 3694Srgrimes outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ 3704Srgrimes outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ 3714Srgrimes#ifdef AUTO_EOI_1 3724Srgrimes outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ 3734Srgrimes#else 3744Srgrimes outb(IO_ICU1+1, 1); /* 8086 mode */ 3754Srgrimes#endif 3764Srgrimes outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ 3774Srgrimes outb(IO_ICU1, 0x0a); /* default to IRR on read */ 3784Srgrimes outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ 3794Srgrimes 3804Srgrimes outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ 3814Srgrimes outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ 3824Srgrimes outb(IO_ICU2+1,2); /* my slave id is 2 */ 3834Srgrimes#ifdef AUTO_EOI_2 3844Srgrimes outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ 3854Srgrimes#else 3864Srgrimes outb(IO_ICU2+1,1); /* 8086 mode */ 3874Srgrimes#endif 3884Srgrimes outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ 3894Srgrimes outb(IO_ICU2, 0x0a); /* default to IRR on read */ 3904Srgrimes} 3914Srgrimes 3924Srgrimes/* region of physical memory known to be contiguous */ 3934Srgrimesvm_offset_t isaphysmem; 3944Srgrimesstatic caddr_t dma_bounce[8]; /* XXX */ 3954Srgrimesstatic char bounced[8]; /* XXX */ 3964Srgrimes#define MAXDMASZ 512 /* XXX */ 3974Srgrimes 3984Srgrimes/* high byte of address is stored in this port for i-th dma channel */ 3994Srgrimesstatic short dmapageport[8] = 4004Srgrimes { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 4014Srgrimes 4024Srgrimes/* 4034Srgrimes * isa_dmacascade(): program 8237 DMA controller channel to accept 4044Srgrimes * external dma control by a board. 4054Srgrimes */ 4064Srgrimesvoid isa_dmacascade(unsigned chan) 4074Srgrimes{ 4084Srgrimes if (chan > 7) 4094Srgrimes panic("isa_dmacascade: impossible request"); 4104Srgrimes 4114Srgrimes /* set dma channel mode, and set dma channel mode */ 4124Srgrimes if ((chan & 4) == 0) { 4134Srgrimes outb(DMA1_MODE, DMA37MD_CASCADE | chan); 4144Srgrimes outb(DMA1_SMSK, chan); 4154Srgrimes } else { 4164Srgrimes outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 4174Srgrimes outb(DMA2_SMSK, chan & 3); 4184Srgrimes } 4194Srgrimes} 4204Srgrimes 4214Srgrimes/* 4224Srgrimes * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 4234Srgrimes * problems by using a bounce buffer. 4244Srgrimes */ 4254Srgrimesvoid isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan) 4264Srgrimes{ vm_offset_t phys; 4274Srgrimes int waport; 4284Srgrimes caddr_t newaddr; 4294Srgrimes 4304Srgrimes if ( chan > 7 4314Srgrimes || (chan < 4 && nbytes > (1<<16)) 4324Srgrimes || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) 4334Srgrimes panic("isa_dmastart: impossible request"); 4344Srgrimes 4354Srgrimes if (isa_dmarangecheck(addr, nbytes, chan)) { 4364Srgrimes if (dma_bounce[chan] == 0) 4374Srgrimes dma_bounce[chan] = 4384Srgrimes /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ 4394Srgrimes (caddr_t) isaphysmem + NBPG*chan; 4404Srgrimes bounced[chan] = 1; 4414Srgrimes newaddr = dma_bounce[chan]; 4424Srgrimes *(int *) newaddr = 0; /* XXX */ 4434Srgrimes 4444Srgrimes /* copy bounce buffer on write */ 4454Srgrimes if (!(flags & B_READ)) 4464Srgrimes bcopy(addr, newaddr, nbytes); 4474Srgrimes addr = newaddr; 4484Srgrimes } 4494Srgrimes 4504Srgrimes /* translate to physical */ 4514Srgrimes phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 4524Srgrimes 4534Srgrimes if ((chan & 4) == 0) { 4544Srgrimes /* 4554Srgrimes * Program one of DMA channels 0..3. These are 4564Srgrimes * byte mode channels. 4574Srgrimes */ 4584Srgrimes /* set dma channel mode, and reset address ff */ 4594Srgrimes if (flags & B_READ) 4604Srgrimes outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 4614Srgrimes else 4624Srgrimes outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 4634Srgrimes outb(DMA1_FFC, 0); 4644Srgrimes 4654Srgrimes /* send start address */ 4664Srgrimes waport = DMA1_CHN(chan); 4674Srgrimes outb(waport, phys); 4684Srgrimes outb(waport, phys>>8); 4694Srgrimes outb(dmapageport[chan], phys>>16); 4704Srgrimes 4714Srgrimes /* send count */ 4724Srgrimes outb(waport + 1, --nbytes); 4734Srgrimes outb(waport + 1, nbytes>>8); 4744Srgrimes 4754Srgrimes /* unmask channel */ 4764Srgrimes outb(DMA1_SMSK, chan); 4774Srgrimes } else { 4784Srgrimes /* 4794Srgrimes * Program one of DMA channels 4..7. These are 4804Srgrimes * word mode channels. 4814Srgrimes */ 4824Srgrimes /* set dma channel mode, and reset address ff */ 4834Srgrimes if (flags & B_READ) 4844Srgrimes outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 4854Srgrimes else 4864Srgrimes outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 4874Srgrimes outb(DMA2_FFC, 0); 4884Srgrimes 4894Srgrimes /* send start address */ 4904Srgrimes waport = DMA2_CHN(chan - 4); 4914Srgrimes outb(waport, phys>>1); 4924Srgrimes outb(waport, phys>>9); 4934Srgrimes outb(dmapageport[chan], phys>>16); 4944Srgrimes 4954Srgrimes /* send count */ 4964Srgrimes nbytes >>= 1; 4974Srgrimes outb(waport + 2, --nbytes); 4984Srgrimes outb(waport + 2, nbytes>>8); 4994Srgrimes 5004Srgrimes /* unmask channel */ 5014Srgrimes outb(DMA2_SMSK, chan & 3); 5024Srgrimes } 5034Srgrimes} 5044Srgrimes 5054Srgrimesvoid isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) 5064Srgrimes{ 5074Srgrimes 5084Srgrimes /* copy bounce buffer on read */ 5094Srgrimes /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/ 5104Srgrimes if (bounced[chan]) { 5114Srgrimes bcopy(dma_bounce[chan], addr, nbytes); 5124Srgrimes bounced[chan] = 0; 5134Srgrimes } 5144Srgrimes} 5154Srgrimes 5164Srgrimes/* 5174Srgrimes * Check for problems with the address range of a DMA transfer 5184Srgrimes * (non-contiguous physical pages, outside of bus address space, 5194Srgrimes * crossing DMA page boundaries). 5204Srgrimes * Return true if special handling needed. 5214Srgrimes */ 5224Srgrimes 523798Swollmanint 5244Srgrimesisa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) { 5254Srgrimes vm_offset_t phys, priorpage = 0, endva; 5264Srgrimes u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 5274Srgrimes 5284Srgrimes endva = (vm_offset_t)round_page(va + length); 5294Srgrimes for (; va < (caddr_t) endva ; va += NBPG) { 5304Srgrimes phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); 5314Srgrimes#define ISARAM_END RAM_END 5324Srgrimes if (phys == 0) 5334Srgrimes panic("isa_dmacheck: no physical page present"); 5341323Sache if (phys >= ISARAM_END) 5354Srgrimes return (1); 5364Srgrimes if (priorpage) { 5374Srgrimes if (priorpage + NBPG != phys) 5384Srgrimes return (1); 5394Srgrimes /* check if crossing a DMA page boundary */ 5404Srgrimes if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) 5414Srgrimes return (1); 5424Srgrimes } 5434Srgrimes priorpage = phys; 5444Srgrimes } 5454Srgrimes return (0); 5464Srgrimes} 5474Srgrimes 5484Srgrimes/* head of queue waiting for physmem to become available */ 5494Srgrimesstruct buf isa_physmemq; 5504Srgrimes 5514Srgrimes/* blocked waiting for resource to become free for exclusive use */ 5524Srgrimesstatic isaphysmemflag; 5534Srgrimes/* if waited for and call requested when free (B_CALL) */ 5544Srgrimesstatic void (*isaphysmemunblock)(); /* needs to be a list */ 5554Srgrimes 5564Srgrimes/* 5574Srgrimes * Allocate contiguous physical memory for transfer, returning 5584Srgrimes * a *virtual* address to region. May block waiting for resource. 5594Srgrimes * (assumed to be called at splbio()) 5604Srgrimes */ 5614Srgrimescaddr_t 5624Srgrimesisa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { 5634Srgrimes 5644Srgrimes isaphysmemunblock = func; 5654Srgrimes while (isaphysmemflag & B_BUSY) { 5664Srgrimes isaphysmemflag |= B_WANTED; 567798Swollman tsleep((caddr_t)&isaphysmemflag, PRIBIO, "isaphys", 0); 5684Srgrimes } 5694Srgrimes isaphysmemflag |= B_BUSY; 5704Srgrimes 5714Srgrimes return((caddr_t)isaphysmem); 5724Srgrimes} 5734Srgrimes 5744Srgrimes/* 5754Srgrimes * Free contiguous physical memory used for transfer. 5764Srgrimes * (assumed to be called at splbio()) 5774Srgrimes */ 5784Srgrimesvoid 5794Srgrimesisa_freephysmem(caddr_t va, unsigned length) { 5804Srgrimes 5814Srgrimes isaphysmemflag &= ~B_BUSY; 5824Srgrimes if (isaphysmemflag & B_WANTED) { 5834Srgrimes isaphysmemflag &= B_WANTED; 584798Swollman wakeup((caddr_t)&isaphysmemflag); 5854Srgrimes if (isaphysmemunblock) 5864Srgrimes (*isaphysmemunblock)(); 5874Srgrimes } 5884Srgrimes} 5894Srgrimes 5902001Swollman#define NMI_PARITY (1 << 7) 5912001Swollman#define NMI_IOCHAN (1 << 6) 5922001Swollman#define ENMI_WATCHDOG (1 << 7) 5932001Swollman#define ENMI_BUSTIMER (1 << 6) 5942001Swollman#define ENMI_IOSTATUS (1 << 5) 5952001Swollman 5964Srgrimes/* 5974Srgrimes * Handle a NMI, possibly a machine check. 5984Srgrimes * return true to panic system, false to ignore. 5994Srgrimes */ 600798Swollmanint 601798Swollmanisa_nmi(cd) 602798Swollman int cd; 603798Swollman{ 6042001Swollman int isa_port = inb(0x61); 6052001Swollman int eisa_port = inb(0x461); 6062001Swollman if(isa_port & NMI_PARITY) { 6072001Swollman panic("RAM parity error, likely hardware failure."); 6082001Swollman } else if(isa_port & NMI_IOCHAN) { 6092001Swollman panic("I/O channel check, likely hardware failure."); 6102001Swollman } else if(eisa_port & ENMI_WATCHDOG) { 6112001Swollman panic("EISA watchdog timer expired, likely hardware failure."); 6122001Swollman } else if(eisa_port & ENMI_BUSTIMER) { 6132001Swollman panic("EISA bus timeout, likely hardware failure."); 6142001Swollman } else if(eisa_port & ENMI_IOSTATUS) { 6152001Swollman panic("EISA I/O port status error."); 6162001Swollman } else { 6172001Swollman printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); 6182001Swollman return(0); 6192001Swollman } 6204Srgrimes} 6214Srgrimes 6224Srgrimes/* 6234Srgrimes * Caught a stray interrupt, notify 6244Srgrimes */ 625798Swollmanvoid 626798Swollmanisa_strayintr(d) 627798Swollman int d; 628798Swollman{ 6294Srgrimes 6304Srgrimes /* DON'T BOTHER FOR NOW! */ 6314Srgrimes /* for some reason, we get bursts of intr #7, even if not enabled! */ 6324Srgrimes /* 6334Srgrimes * Well the reason you got bursts of intr #7 is because someone 6344Srgrimes * raised an interrupt line and dropped it before the 8259 could 6354Srgrimes * prioritize it. This is documented in the intel data book. This 6364Srgrimes * means you have BAD hardware! I have changed this so that only 6374Srgrimes * the first 5 get logged, then it quits logging them, and puts 6384Srgrimes * out a special message. rgrimes 3/25/1993 6394Srgrimes */ 6404Srgrimes extern u_long intrcnt_stray; 6414Srgrimes 6424Srgrimes intrcnt_stray++; 6434Srgrimes if (intrcnt_stray <= 5) 6444Srgrimes log(LOG_ERR,"ISA strayintr %x\n", d); 6454Srgrimes if (intrcnt_stray == 5) 6464Srgrimes log(LOG_CRIT,"Too many ISA strayintr not logging any more\n"); 6474Srgrimes} 6484Srgrimes 6494Srgrimes/* 6504Srgrimes * find an ISA device in a given isa_devtab_* table, given 6514Srgrimes * the table to search, the expected id_driver entry, and the unit number. 6524Srgrimes * 6534Srgrimes * this function is defined in isa_device.h, and this location is debatable; 6544Srgrimes * i put it there because it's useless w/o, and directly operates on 6554Srgrimes * the other stuff in that file. 6564Srgrimes * 6574Srgrimes */ 6584Srgrimes 6594Srgrimesstruct isa_device *find_isadev(table, driverp, unit) 6604Srgrimes struct isa_device *table; 6614Srgrimes struct isa_driver *driverp; 6624Srgrimes int unit; 6634Srgrimes{ 6644Srgrimes if (driverp == NULL) /* sanity check */ 6654Srgrimes return NULL; 6664Srgrimes 6674Srgrimes while ((table->id_driver != driverp) || (table->id_unit != unit)) { 6684Srgrimes if (table->id_driver == 0) 6694Srgrimes return NULL; 6704Srgrimes 6714Srgrimes table++; 6724Srgrimes } 6734Srgrimes 6744Srgrimes return table; 6754Srgrimes} 6764Srgrimes 6774Srgrimes/* 6784Srgrimes * Return nonzero if a (masked) irq is pending for a given device. 6794Srgrimes */ 6804Srgrimesint 6814Srgrimesisa_irq_pending(dvp) 6824Srgrimes struct isa_device *dvp; 6834Srgrimes{ 6844Srgrimes unsigned id_irq; 6854Srgrimes 6864Srgrimes id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */ 6874Srgrimes if (id_irq & 0xff) 6884Srgrimes return (inb(IO_ICU1) & id_irq); 6894Srgrimes return (inb(IO_ICU2) & (id_irq >> 8)); 6904Srgrimes} 691