isapnp.c revision 40553
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 * 2740553Smsmith * $Id: isapnp.c,v 1.2 1998/09/26 01:29:13 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); 4640553Smsmithstatic int isapnp_isolation_protocol(void); 4740553Smsmithstatic void isapnp_enumerate(void); 4839444Smsmith 4939444Smsmith/* PnP read data port */ 5039444Smsmithstatic int pnp_rd_port; 5139444Smsmith 5239444Smsmith#define _PNP_ID_LEN 9 5339444Smsmith 5439444Smsmithstruct pnphandler isapnphandler = 5539444Smsmith{ 5640553Smsmith "ISA bus", 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; 14640553Smsmith return(idbuf); 14739444Smsmith} 14839444Smsmith 14939444Smsmith/* 15040553Smsmith * Fills the buffer with resource info from the device. 15140553Smsmith * Returns nonzero if the device fails to report 15239444Smsmith */ 15339444Smsmithstatic int 15440553Smsmithisapnp_get_resource_info(u_int8_t *buffer, int len) 15539444Smsmith{ 15640553Smsmith int i, j; 15740553Smsmith u_char temp; 15840553Smsmith 15940553Smsmith for (i = 0; i < len; i++) { 16040553Smsmith outb(_PNP_ADDRESS, STATUS); 16140553Smsmith for (j = 0; j < 100; j++) { 16240553Smsmith if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1) 16340553Smsmith break; 16440553Smsmith delay(1); 16540553Smsmith } 16640553Smsmith if (j == 100) { 16740553Smsmith printf("PnP device failed to report resource data\n"); 16840553Smsmith return(1); 16940553Smsmith } 17040553Smsmith outb(_PNP_ADDRESS, RESOURCE_DATA); 17140553Smsmith temp = inb((pnp_rd_port << 2) | 0x3); 17240553Smsmith if (buffer != NULL) 17340553Smsmith buffer[i] = temp; 17440553Smsmith } 17540553Smsmith return(0); 17640553Smsmith} 17740553Smsmith 17840553Smsmith/* 17940553Smsmith * Scan Resource Data for useful information. 18040553Smsmith * 18140553Smsmith * We scan the resource data for compatible device IDs and 18240553Smsmith * identifier strings; we only take the first identifier string 18340553Smsmith * and assume it's for the card as a whole. 18440553Smsmith * 18540553Smsmith * Returns 0 if the scan completed OK, nonzero on error. 18640553Smsmith */ 18740553Smsmithstatic int 18840553Smsmithisapnp_scan_resdata(struct pnpinfo *pi) 18940553Smsmith{ 19040553Smsmith u_char tag, resinfo[8]; 19140553Smsmith int large_len, limit; 19240553Smsmith char *str; 19340553Smsmith 19440553Smsmith limit = 1000; 19540553Smsmith while ((limit-- > 0) && !isapnp_get_resource_info(&tag, 1)) { 19640553Smsmith if (PNP_RES_TYPE(tag) == 0) { 19740553Smsmith /* Small resource */ 19840553Smsmith switch (PNP_SRES_NUM(tag)) { 19940553Smsmith 20040553Smsmith case COMP_DEVICE_ID: 20140553Smsmith /* Got a compatible device id resource */ 20240553Smsmith if (isapnp_get_resource_info(resinfo, PNP_SRES_LEN(tag))) 20340553Smsmith return(1); 20440553Smsmith pnp_addident(pi, isapnp_format(resinfo)); 20540553Smsmith 20640553Smsmith case END_TAG: 20740553Smsmith return(0); 20840553Smsmith break; 20940553Smsmith 21040553Smsmith default: 21140553Smsmith /* Skip this resource */ 21240553Smsmith if (isapnp_get_resource_info(NULL, PNP_SRES_LEN(tag))) 21340553Smsmith return(1); 21440553Smsmith break; 21540553Smsmith } 21640553Smsmith } else { 21740553Smsmith /* Large resource */ 21840553Smsmith if (isapnp_get_resource_info(resinfo, 2)) 21939444Smsmith return(1); 22040553Smsmith 22140553Smsmith large_len = resinfo[1]; 22240553Smsmith large_len = (large_len << 8) + resinfo[0]; 22340553Smsmith 22440553Smsmith switch(PNP_LRES_NUM(tag)) { 22540553Smsmith 22640553Smsmith case ID_STRING_ANSI: 22740553Smsmith str = malloc(large_len + 1); 22840553Smsmith if (isapnp_get_resource_info(str, large_len)) { 22940553Smsmith free(str); 23040553Smsmith return(1); 23140553Smsmith } 23240553Smsmith str[large_len] = 0; 23340553Smsmith if (pi->pi_desc == NULL) { 23440553Smsmith pi->pi_desc = str; 23540553Smsmith } else { 23640553Smsmith free(str); 23740553Smsmith } 23840553Smsmith break; 23940553Smsmith 24040553Smsmith default: 24140553Smsmith /* Large resource, skip it */ 24240553Smsmith if (isapnp_get_resource_info(NULL, large_len)) 24340553Smsmith return(1); 24440553Smsmith } 24539444Smsmith } 24639444Smsmith } 24740553Smsmith return(1); 24839444Smsmith} 24939444Smsmith 25039444Smsmith/* 25139444Smsmith * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port 25239444Smsmith * value (caller should try multiple READ_DATA locations before giving 25339444Smsmith * up). Upon exiting, all cards are aware that they should use 25439444Smsmith * pnp_rd_port as the READ_DATA port. 25539444Smsmith */ 25639444Smsmithstatic int 25740553Smsmithisapnp_isolation_protocol(void) 25839444Smsmith{ 25939444Smsmith int csn; 26039444Smsmith struct pnpinfo *pi; 26139444Smsmith u_int8_t cardid[_PNP_ID_LEN]; 26239444Smsmith int ndevs; 26339444Smsmith 26439444Smsmith isapnp_send_Initiation_LFSR(); 26539444Smsmith ndevs = 0; 26639444Smsmith 26739444Smsmith isapnp_write(CONFIG_CONTROL, 0x04); /* Reset CSN for All Cards */ 26839444Smsmith 26939444Smsmith for (csn = 1; ; csn++) { 27039444Smsmith /* Wake up cards without a CSN (ie. all of them) */ 27139444Smsmith isapnp_write(WAKE, 0); 27239444Smsmith isapnp_write(SET_RD_DATA, pnp_rd_port); 27339444Smsmith outb(_PNP_ADDRESS, SERIAL_ISOLATION); 27439444Smsmith delay(1000); /* Delay 1 msec */ 27539444Smsmith 27639444Smsmith if (isapnp_get_serial(cardid)) { 27739444Smsmith isapnp_write(SET_CSN, csn); 27840553Smsmith pi = pnp_allocinfo(); 27939444Smsmith ndevs++; 28040553Smsmith pnp_addident(pi, isapnp_format(cardid)); 28139444Smsmith /* scan the card obtaining all the identifiers it holds */ 28240553Smsmith if (isapnp_scan_resdata(pi)) { 28340553Smsmith pnp_freeinfo(pi); /* error getting data, ignore */ 28440553Smsmith } else { 28540553Smsmith pnp_addinfo(pi); 28639444Smsmith } 28740553Smsmith } else { 28839444Smsmith break; 28940553Smsmith } 29039444Smsmith } 29139444Smsmith /* Move all cards to wait-for-key state */ 29240553Smsmith while (--csn > 0) { 29339444Smsmith isapnp_send_Initiation_LFSR(); 29439444Smsmith isapnp_write(WAKE, csn); 29539444Smsmith isapnp_write(CONFIG_CONTROL, 0x02); 29639444Smsmith delay(1000); /* XXX is it really necessary ? */ 29740553Smsmith csn--; 29839444Smsmith } 29939444Smsmith return(ndevs); 30039444Smsmith} 30139444Smsmith 30239444Smsmith/* 30339444Smsmith * Locate ISA-PnP devices and populate the supplied list. 30439444Smsmith */ 30539444Smsmithstatic void 30640553Smsmithisapnp_enumerate(void) 30739444Smsmith{ 30839444Smsmith int devs; 30939444Smsmith 31039444Smsmith for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) { 31140553Smsmith 31239444Smsmith /* Look for something, quit when we find it */ 31340553Smsmith if ((devs = isapnp_isolation_protocol()) > 0) 31439444Smsmith break; 31539444Smsmith } 31639444Smsmith} 31739444Smsmith 31839444Smsmith 319