/* $NetBSD: isapnp.c,v 1.5 2008/12/14 17:03:43 christos 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. * * 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 #include #include #include #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(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(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(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(void) { outb(PNPADDR, WAKE); outb(PNPWDATA, 0); /* put all remaining cards to isolation state */ } /* send initiation sequence (app. B.1) */ static void pnpinit(void) { 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(int id, int *iobase, int *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 */ }