NetBSD/sys/arch/i386/stand/lib/isapnp.c

229 lines
5.4 KiB
C

/* $NetBSD: isapnp.c,v 1.1.1.1 1997/03/14 02:40:33 perry Exp $ */
/*
* Copyright (c) 1997
* Matthias Drochner. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project
* by Matthias Drochner.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* minimal ISA PnP implementation: find adapter,
return settings (1 IO and 1 DMA only for now)
*/
#include <sys/types.h>
#include <machine/pio.h>
#include <lib/libsa/stand.h>
#include <libi386.h>
#include "isapnpvar.h"
#define PNPADDR 0x0279
#define PNPWDATA 0x0a79
#define PNPRDATAMIN 0x0203
#define PNPRDATAMAX 0x03ff
enum{
DATAPORT,
ISOL,
CONTROL,
WAKE,
RESDATA,
RESSTAT,
SETCSN,
SETLDEV
};
#define MEMBASE 0x40
#define IOBASE 0x60
#define INTBASE 0x70
#define DMABASE 0x74
static int pnpdataport;
static int getiobase(nr)
int nr;
{
unsigned short iobase;
outb(PNPADDR, SETLDEV);
outb(PNPWDATA, 0); /* subdev 0 */
outb(PNPADDR, IOBASE + nr * 2);
iobase = (inb(pnpdataport) << 8);
outb(PNPADDR, IOBASE + nr * 2 + 1);
iobase |= inb(pnpdataport);
return(iobase);
}
static int getdmachan(nr)
int nr;
{
unsigned char dmachannel;
outb(PNPADDR, SETLDEV);
outb(PNPWDATA, 0); /* subdev 0 */
outb(PNPADDR, DMABASE+nr);
dmachannel=inb(pnpdataport) & 0x07;
return(dmachannel);
}
struct cardid{
unsigned char eisaid[4];
unsigned int serial;
unsigned char crc;
};
/*
do isolation, call pnpscanresc() in board config state
*/
static int pnpisol(csn)
int csn;
{
unsigned char buf[9];
int i, j;
struct cardid *id;
unsigned char crc = 0x6a;
/* do 72 pairs of reads from ISOL register
all but 1 go to sleep state (ch. 3.3) */
outb(PNPADDR, ISOL);
delay(1000);
for(i = 0; i < 9; i++){
for(j = 0; j < 8; j++){
unsigned char a,b;
int bitset;
a = inb(pnpdataport);
b = inb(pnpdataport);
if((a == 0x55) && (b == 0xaa))
bitset = 1;
else if((a == 0xff) && (b == 0xff))
bitset = 0;
else
return(-1); /* data port conflict */
buf[i] = (buf[i] >> 1) | (bitset << 7);
if(i < 8) /* calc crc for first 8 bytes (app. B.2) */
crc = (crc >> 1) |
((bitset != ((crc & 1) == !(crc & 2))) << 7);
delay(250);
}
}
id = (struct cardid*)buf;
if(id->crc != crc) return(0); /* normal end */
outb(PNPADDR, SETCSN);
outb(PNPWDATA, csn); /* set csn for winning card and put it to config state */
return((id->eisaid[0] << 24) | (id->eisaid[1] << 16)
| (id->eisaid[2] << 8) | (id->eisaid[3]));
}
static void pnpisolreset()
{
outb(PNPADDR, WAKE);
outb(PNPWDATA, 0); /* put all remaining cards to isolation state */
}
/*
send initiation sequence (app. B.1)
*/
static void pnpinit()
{
int i;
unsigned char key = 0x6a;
outb(PNPADDR, 0);
outb(PNPADDR, 0);
for(i = 0; i < 32; i++){
outb(PNPADDR, key);
key = (key >> 1) |
(((key & 1) == !(key & 2)) << 7);
}
}
int isapnp_finddev(id, iobase, dmachan)
int id;
int *iobase, *dmachan;
{
int csn;
outb(PNPADDR, CONTROL);
outb(PNPWDATA, 2); /* XXX force wait for key */
/* scan all allowed data ports (ch. 3.1) */
for(pnpdataport = PNPRDATAMIN; pnpdataport <= PNPRDATAMAX;
pnpdataport += 4){
int res, found = 0;
pnpinit(); /* initiation sequence */
outb(PNPADDR, CONTROL);
outb(PNPWDATA, 4); /* CSN=0 - only these respond to WAKE[0] */
outb(PNPADDR, WAKE);
outb(PNPWDATA, 0); /* put into isolation state */
outb(PNPADDR, DATAPORT);
outb(PNPWDATA, pnpdataport>>2); /* set READ_DATA port */
csn = 0;
do{
res = pnpisol(++csn);
if((res) == id) {
if(iobase) *iobase = getiobase(0);
if(dmachan) *dmachan = getdmachan(0);
found = 1;
}
pnpisolreset();
} while((res != 0) && (res != -1));
outb(PNPADDR, CONTROL);
outb(PNPWDATA, 2); /* return to wait for key */
if(csn > 1) /* at least 1 board found */
return(!found);
/* if no board found, try next dataport */
}
return(-1); /* nothing found */
}