isapnp.c revision 39660
139444Smsmith/* 239444Smsmith * Copyright (c) 1998, Michael Smith 339444Smsmith * Copyright (c) 1996, Sujal M. Patel 439444Smsmith * All rights reserved. 539444Smsmith * 639444Smsmith * Redistribution and use in source and binary forms, with or without 739444Smsmith * modification, are permitted provided that the following conditions 839444Smsmith * are met: 939444Smsmith * 1. Redistributions of source code must retain the above copyright 1039444Smsmith * notice, this list of conditions and the following disclaimer. 1139444Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1239444Smsmith * notice, this list of conditions and the following disclaimer in the 1339444Smsmith * documentation and/or other materials provided with the distribution. 1439444Smsmith * 1539444Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1639444Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1739444Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1839444Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1939444Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2039444Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2139444Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2239444Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2339444Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2439444Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2539444Smsmith * SUCH DAMAGE. 2639444Smsmith * 2739660Smsmith * $Id: isapnp.c,v 1.1 1998/09/18 00:24:25 msmith Exp $ 2839444Smsmith */ 2939444Smsmith 3039444Smsmith/* 3139444Smsmith * Machine-independant ISA PnP enumerator implementing a subset of the 3239444Smsmith * ISA PnP specification. 3339444Smsmith */ 3439444Smsmith#include <stand.h> 3539444Smsmith#include <string.h> 3639444Smsmith#include <bootstrap.h> 3739444Smsmith#include <isapnp.h> 3839444Smsmith 3939444Smsmith#define inb(x) (archsw.arch_isainb((x))) 4039444Smsmith#define outb(x,y) (archsw.arch_isaoutb((x),(y))) 4139444Smsmith 4239444Smsmithstatic void isapnp_write(int d, u_char r); 4339444Smsmithstatic u_char isapnp_read(int d); 4439444Smsmithstatic void isapnp_send_Initiation_LFSR(); 4539444Smsmithstatic int isapnp_get_serial(u_int8_t *p); 4639444Smsmithstatic int isapnp_isolation_protocol(struct pnpinfo **pnplist); 4739444Smsmithstatic void isapnp_enumerate(struct pnpinfo **pnplist); 4839444Smsmith 4939444Smsmith/* PnP read data port */ 5039444Smsmithstatic int pnp_rd_port; 5139444Smsmith 5239444Smsmith#define _PNP_ID_LEN 9 5339444Smsmith 5439444Smsmithstruct pnphandler isapnphandler = 5539444Smsmith{ 5639444Smsmith "isapnp", 5739444Smsmith isapnp_enumerate 5839444Smsmith}; 5939444Smsmith 6039444Smsmithstatic void 6139444Smsmithisapnp_write(int d, u_char r) 6239444Smsmith{ 6339444Smsmith outb (_PNP_ADDRESS, d); 6439444Smsmith outb (_PNP_WRITE_DATA, r); 6539444Smsmith} 6639444Smsmith 6739444Smsmithstatic u_char 6839444Smsmithisapnp_read(int d) 6939444Smsmith{ 7039444Smsmith outb (_PNP_ADDRESS, d); 7139444Smsmith return (inb(3 | (pnp_rd_port <<2))); 7239444Smsmith} 7339444Smsmith 7439444Smsmith/* 7539444Smsmith * Send Initiation LFSR as described in "Plug and Play ISA Specification", 7639444Smsmith * Intel May 94. 7739444Smsmith */ 7839444Smsmithstatic void 7939444Smsmithisapnp_send_Initiation_LFSR() 8039444Smsmith{ 8139444Smsmith int cur, i; 8239444Smsmith 8339444Smsmith /* Reset the LSFR */ 8439444Smsmith outb(_PNP_ADDRESS, 0); 8539444Smsmith outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */ 8639444Smsmith 8739444Smsmith cur = 0x6a; 8839444Smsmith outb(_PNP_ADDRESS, cur); 8939444Smsmith 9039444Smsmith for (i = 1; i < 32; i++) { 9139444Smsmith cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); 9239444Smsmith outb(_PNP_ADDRESS, cur); 9339444Smsmith } 9439444Smsmith} 9539444Smsmith 9639444Smsmith/* 9739444Smsmith * Get the device's serial number. Returns 1 if the serial is valid. 9839444Smsmith */ 9939444Smsmithstatic int 10039444Smsmithisapnp_get_serial(u_int8_t *data) 10139444Smsmith{ 10239444Smsmith int i, bit, valid = 0, sum = 0x6a; 10339444Smsmith 10439444Smsmith bzero(data, _PNP_ID_LEN); 10539444Smsmith outb(_PNP_ADDRESS, SERIAL_ISOLATION); 10639444Smsmith for (i = 0; i < 72; i++) { 10739444Smsmith bit = inb((pnp_rd_port << 2) | 0x3) == 0x55; 10839444Smsmith delay(250); /* Delay 250 usec */ 10939444Smsmith 11039444Smsmith /* Can't Short Circuit the next evaluation, so 'and' is last */ 11139444Smsmith bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit; 11239444Smsmith delay(250); /* Delay 250 usec */ 11339444Smsmith 11439444Smsmith valid = valid || bit; 11539444Smsmith 11639444Smsmith if (i < 64) 11739444Smsmith sum = (sum >> 1) | 11839444Smsmith (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); 11939444Smsmith 12039444Smsmith data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); 12139444Smsmith } 12239444Smsmith 12339444Smsmith valid = valid && (data[8] == sum); 12439444Smsmith 12539444Smsmith return valid; 12639444Smsmith} 12739444Smsmith 12839444Smsmith/* 12939660Smsmith * Format a pnp id as a string in standard ISA PnP format, AAAIIRR 13039660Smsmith * where 'AAA' is the EISA ID, II is the product ID and RR the revision ID. 13139444Smsmith */ 13239444Smsmithstatic char * 13339444Smsmithisapnp_format(u_int8_t *data) 13439444Smsmith{ 13539660Smsmith static char idbuf[8]; 13639660Smsmith const char hextoascii[] = "0123456789abcdef"; 13739660Smsmith 13839660Smsmith idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); 13939660Smsmith idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); 14039660Smsmith idbuf[2] = '@' + (data[1] & 0x1f); 14139660Smsmith idbuf[3] = hextoascii[(data[2] >> 4)]; 14239660Smsmith idbuf[4] = hextoascii[(data[2] & 0xf)]; 14339660Smsmith idbuf[5] = hextoascii[(data[3] >> 4)]; 14439660Smsmith idbuf[6] = hextoascii[(data[3] & 0xf)]; 14539660Smsmith idbuf[7] = 0; 14639444Smsmith} 14739444Smsmith 14839444Smsmith/* 14939444Smsmith * Try to read a compatible device ID from the current device, return 15039444Smsmith * 1 if we found one. 15139444Smsmith */ 15239444Smsmith#define READ_RSC(c) {while ((isapnp_read(STATUS) & 1) == 0); (c) = isapnp_read(RESOURCE_DATA);} 15339444Smsmithstatic int 15439444Smsmithisapnp_getid(u_int8_t *data) 15539444Smsmith{ 15639444Smsmith int discard, pos, len; 15739444Smsmith u_int8_t c, t; 15839444Smsmith 15939444Smsmith discard = 0; 16039444Smsmith len = 0; 16139444Smsmith pos = 0; 16239444Smsmith 16339444Smsmith for (;;) { 16439444Smsmith READ_RSC(c); 16539444Smsmith /* skipping junk? */ 16639444Smsmith if (discard > 0) { 16739444Smsmith discard--; 16839444Smsmith continue; 16939444Smsmith } 17039444Smsmith /* copying data? */ 17139444Smsmith if (len > 0) { 17239444Smsmith data[pos++] = c; 17339444Smsmith /* got all data? */ 17439444Smsmith if (pos >= len) 17539444Smsmith return(1); 17639444Smsmith } 17739444Smsmith /* resource type */ 17839444Smsmith if (c & 0x80) { /* large resource, throw it away */ 17939444Smsmith if (c == 0xff) 18039444Smsmith return(0); /* end of resources */ 18139444Smsmith READ_RSC(c); 18239444Smsmith discard = c; 18339444Smsmith READ_RSC(c); 18439444Smsmith discard += ((int)c << 8); 18539444Smsmith continue; 18639444Smsmith } 18739444Smsmith /* small resource */ 18839444Smsmith t = (c >> 3) & 0xf; 18939444Smsmith if (t == 0xf) 19039444Smsmith return(0); /* end of resources */ 19139444Smsmith if ((t == LOG_DEVICE_ID) || (t == COMP_DEVICE_ID)) { 19239444Smsmith len = c & 7; 19339444Smsmith pos = 0; 19439444Smsmith continue; 19539444Smsmith } 19639444Smsmith discard = c & 7; /* unwanted small resource */ 19739444Smsmith } 19839444Smsmith 19939444Smsmith} 20039444Smsmith 20139444Smsmith 20239444Smsmith/* 20339444Smsmith * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port 20439444Smsmith * value (caller should try multiple READ_DATA locations before giving 20539444Smsmith * up). Upon exiting, all cards are aware that they should use 20639444Smsmith * pnp_rd_port as the READ_DATA port. 20739444Smsmith */ 20839444Smsmithstatic int 20939444Smsmithisapnp_isolation_protocol(struct pnpinfo **pilist) 21039444Smsmith{ 21139444Smsmith int csn; 21239444Smsmith struct pnpinfo *pi; 21339444Smsmith u_int8_t cardid[_PNP_ID_LEN]; 21439444Smsmith int ndevs; 21539444Smsmith 21639444Smsmith isapnp_send_Initiation_LFSR(); 21739444Smsmith ndevs = 0; 21839444Smsmith 21939444Smsmith isapnp_write(CONFIG_CONTROL, 0x04); /* Reset CSN for All Cards */ 22039444Smsmith 22139444Smsmith for (csn = 1; ; csn++) { 22239444Smsmith /* Wake up cards without a CSN (ie. all of them) */ 22339444Smsmith isapnp_write(WAKE, 0); 22439444Smsmith isapnp_write(SET_RD_DATA, pnp_rd_port); 22539444Smsmith outb(_PNP_ADDRESS, SERIAL_ISOLATION); 22639444Smsmith delay(1000); /* Delay 1 msec */ 22739444Smsmith 22839444Smsmith if (isapnp_get_serial(cardid)) { 22939444Smsmith isapnp_write(SET_CSN, csn); 23039444Smsmith pi = malloc(sizeof(struct pnpinfo)); 23139444Smsmith pi->pi_next = *pilist; 23239444Smsmith *pilist = pi; 23339444Smsmith ndevs++; 23439444Smsmith /* scan the card obtaining all the identifiers it holds */ 23539444Smsmith while (isapnp_getid(cardid)) { 23639444Smsmith printf(" %s\n", isapnp_format(cardid)); 23739444Smsmith pnp_addident(pi, isapnp_format(cardid)); 23839444Smsmith } 23939444Smsmith } else 24039444Smsmith break; 24139444Smsmith } 24239444Smsmith /* Move all cards to wait-for-key state */ 24339444Smsmith while (csn >= 0) { 24439444Smsmith isapnp_send_Initiation_LFSR(); 24539444Smsmith isapnp_write(WAKE, csn); 24639444Smsmith isapnp_write(CONFIG_CONTROL, 0x02); 24739444Smsmith delay(1000); /* XXX is it really necessary ? */ 24839444Smsmith } 24939444Smsmith return(ndevs); 25039444Smsmith} 25139444Smsmith 25239444Smsmith/* 25339444Smsmith * Locate ISA-PnP devices and populate the supplied list. 25439444Smsmith */ 25539444Smsmithstatic void 25639444Smsmithisapnp_enumerate(struct pnpinfo **pnplist) 25739444Smsmith{ 25839444Smsmith int devs; 25939444Smsmith 26039444Smsmith for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) { 26139444Smsmith 26239444Smsmith /* Look for something, quit when we find it */ 26339444Smsmith if ((devs = isapnp_isolation_protocol(pnplist)) > 0) 26439444Smsmith break; 26539444Smsmith } 26639444Smsmith printf("Found %d ISA PnP devices\n", devs); 26739444Smsmith} 26839444Smsmith 26939444Smsmith 270