1119483Sobrien/*- 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 */ 2739444Smsmith 28119483Sobrien#include <sys/cdefs.h> 29119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/isapnp.c 332154 2018-04-06 21:37:25Z kevans $"); 30119483Sobrien 3139444Smsmith/* 3239444Smsmith * Machine-independant ISA PnP enumerator implementing a subset of the 3339444Smsmith * ISA PnP specification. 3439444Smsmith */ 3539444Smsmith#include <stand.h> 3639444Smsmith#include <string.h> 3739444Smsmith#include <bootstrap.h> 3839444Smsmith#include <isapnp.h> 3939444Smsmith 40299499Spfg#define inb(x) (archsw.arch_isainb((x))) 41299499Spfg#define outb(x,y) (archsw.arch_isaoutb((x),(y))) 4239444Smsmith 4364187Sjhbstatic void isapnp_write(int d, int r); 4464187Sjhbstatic void isapnp_send_Initiation_LFSR(void); 45332154Skevansstatic int isapnp_get_serial(uint8_t *p); 4640553Smsmithstatic int isapnp_isolation_protocol(void); 4740553Smsmithstatic void isapnp_enumerate(void); 4839444Smsmith 4939444Smsmith/* PnP read data port */ 5040597Smsmithint isapnp_readport = 0; 5139444Smsmith 52299499Spfg#define _PNP_ID_LEN 9 5339444Smsmith 5439444Smsmithstruct pnphandler isapnphandler = 5539444Smsmith{ 5640553Smsmith "ISA bus", 5739444Smsmith isapnp_enumerate 5839444Smsmith}; 5939444Smsmith 6039444Smsmithstatic void 6164187Sjhbisapnp_write(int d, int r) 6239444Smsmith{ 6339444Smsmith outb (_PNP_ADDRESS, d); 6439444Smsmith outb (_PNP_WRITE_DATA, r); 6539444Smsmith} 6639444Smsmith 6739444Smsmith/* 6839444Smsmith * Send Initiation LFSR as described in "Plug and Play ISA Specification", 6939444Smsmith * Intel May 94. 7039444Smsmith */ 7139444Smsmithstatic void 7264187Sjhbisapnp_send_Initiation_LFSR(void) 7339444Smsmith{ 7439444Smsmith int cur, i; 7539444Smsmith 7639444Smsmith /* Reset the LSFR */ 7739444Smsmith outb(_PNP_ADDRESS, 0); 7839444Smsmith outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */ 7939444Smsmith 8039444Smsmith cur = 0x6a; 8139444Smsmith outb(_PNP_ADDRESS, cur); 8239444Smsmith 8339444Smsmith for (i = 1; i < 32; i++) { 8439444Smsmith cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); 8539444Smsmith outb(_PNP_ADDRESS, cur); 8639444Smsmith } 8739444Smsmith} 8839444Smsmith 8939444Smsmith/* 9039444Smsmith * Get the device's serial number. Returns 1 if the serial is valid. 9139444Smsmith */ 9239444Smsmithstatic int 93332154Skevansisapnp_get_serial(uint8_t *data) 9439444Smsmith{ 9539444Smsmith int i, bit, valid = 0, sum = 0x6a; 9639444Smsmith 9739444Smsmith bzero(data, _PNP_ID_LEN); 9839444Smsmith outb(_PNP_ADDRESS, SERIAL_ISOLATION); 9939444Smsmith for (i = 0; i < 72; i++) { 10040597Smsmith bit = inb(isapnp_readport) == 0x55; 10139444Smsmith delay(250); /* Delay 250 usec */ 10239444Smsmith 10339444Smsmith /* Can't Short Circuit the next evaluation, so 'and' is last */ 10440597Smsmith bit = (inb(isapnp_readport) == 0xaa) && bit; 10539444Smsmith delay(250); /* Delay 250 usec */ 10639444Smsmith 10739444Smsmith valid = valid || bit; 10839444Smsmith 10939444Smsmith if (i < 64) 11039444Smsmith sum = (sum >> 1) | 11139444Smsmith (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); 11239444Smsmith 11339444Smsmith data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); 11439444Smsmith } 11539444Smsmith 11639444Smsmith valid = valid && (data[8] == sum); 11739444Smsmith 11839444Smsmith return valid; 11939444Smsmith} 12039444Smsmith 12139444Smsmith/* 12240553Smsmith * Fills the buffer with resource info from the device. 12340553Smsmith * Returns nonzero if the device fails to report 12439444Smsmith */ 12539444Smsmithstatic int 126332154Skevansisapnp_get_resource_info(uint8_t *buffer, int len) 12739444Smsmith{ 12840553Smsmith int i, j; 12940553Smsmith u_char temp; 13040553Smsmith 13140553Smsmith for (i = 0; i < len; i++) { 132299499Spfg outb(_PNP_ADDRESS, STATUS); 133299499Spfg for (j = 0; j < 100; j++) { 134299499Spfg if ((inb(isapnp_readport)) & 0x1) 135299499Spfg break; 136299499Spfg delay(1); 137299499Spfg } 138299499Spfg if (j == 100) { 139299499Spfg printf("PnP device failed to report resource data\n"); 140299499Spfg return(1); 141299499Spfg } 142299499Spfg outb(_PNP_ADDRESS, RESOURCE_DATA); 143299499Spfg temp = inb(isapnp_readport); 144299499Spfg if (buffer != NULL) 145299499Spfg buffer[i] = temp; 14640553Smsmith } 14740553Smsmith return(0); 14840553Smsmith} 14940553Smsmith 15040553Smsmith/* 15140553Smsmith * Scan Resource Data for useful information. 15240553Smsmith * 15340553Smsmith * We scan the resource data for compatible device IDs and 15440553Smsmith * identifier strings; we only take the first identifier string 15540553Smsmith * and assume it's for the card as a whole. 15640553Smsmith * 15740553Smsmith * Returns 0 if the scan completed OK, nonzero on error. 15840553Smsmith */ 15940553Smsmithstatic int 16040553Smsmithisapnp_scan_resdata(struct pnpinfo *pi) 16140553Smsmith{ 16240553Smsmith u_char tag, resinfo[8]; 16364187Sjhb u_int limit; 16464187Sjhb size_t large_len; 16564187Sjhb u_char *str; 16640553Smsmith 16740553Smsmith limit = 1000; 16840553Smsmith while ((limit-- > 0) && !isapnp_get_resource_info(&tag, 1)) { 169299499Spfg if (PNP_RES_TYPE(tag) == 0) { 170299499Spfg /* Small resource */ 171299499Spfg switch (PNP_SRES_NUM(tag)) { 17240553Smsmith 173299499Spfg case COMP_DEVICE_ID: 174299499Spfg /* Got a compatible device id resource */ 175299499Spfg if (isapnp_get_resource_info(resinfo, PNP_SRES_LEN(tag))) 17640553Smsmith return(1); 17740597Smsmith pnp_addident(pi, pnp_eisaformat(resinfo)); 17840553Smsmith 179299499Spfg case END_TAG: 18040553Smsmith return(0); 181299499Spfg break; 18240553Smsmith 183299499Spfg default: 184299499Spfg /* Skip this resource */ 185299499Spfg if (isapnp_get_resource_info(NULL, PNP_SRES_LEN(tag))) 18640553Smsmith return(1); 187299499Spfg break; 188299499Spfg } 189299499Spfg } else { 19040553Smsmith /* Large resource */ 19140553Smsmith if (isapnp_get_resource_info(resinfo, 2)) 19239444Smsmith return(1); 19340553Smsmith 19440553Smsmith large_len = resinfo[1]; 19540553Smsmith large_len = (large_len << 8) + resinfo[0]; 19640553Smsmith 19740553Smsmith switch(PNP_LRES_NUM(tag)) { 19840553Smsmith 19940553Smsmith case ID_STRING_ANSI: 20040553Smsmith str = malloc(large_len + 1); 20164187Sjhb if (isapnp_get_resource_info(str, (ssize_t)large_len)) { 20240553Smsmith free(str); 20340553Smsmith return(1); 20440553Smsmith } 20540553Smsmith str[large_len] = 0; 20640553Smsmith if (pi->pi_desc == NULL) { 20764187Sjhb pi->pi_desc = (char *)str; 20840553Smsmith } else { 20940553Smsmith free(str); 21040553Smsmith } 21140553Smsmith break; 21240553Smsmith 21340553Smsmith default: 21440553Smsmith /* Large resource, skip it */ 21564187Sjhb if (isapnp_get_resource_info(NULL, (ssize_t)large_len)) 21640553Smsmith return(1); 21740553Smsmith } 21839444Smsmith } 21939444Smsmith } 22040553Smsmith return(1); 22139444Smsmith} 22239444Smsmith 22339444Smsmith/* 22440597Smsmith * Run the isolation protocol. Upon exiting, all cards are aware that 22540597Smsmith * they should use isapnp_readport as the READ_DATA port. 22639444Smsmith */ 22739444Smsmithstatic int 22840553Smsmithisapnp_isolation_protocol(void) 22939444Smsmith{ 23039444Smsmith int csn; 23139444Smsmith struct pnpinfo *pi; 232332154Skevans uint8_t cardid[_PNP_ID_LEN]; 23339444Smsmith int ndevs; 23439444Smsmith 23539444Smsmith isapnp_send_Initiation_LFSR(); 23639444Smsmith ndevs = 0; 23739444Smsmith 23839444Smsmith isapnp_write(CONFIG_CONTROL, 0x04); /* Reset CSN for All Cards */ 23939444Smsmith 24039444Smsmith for (csn = 1; ; csn++) { 24139444Smsmith /* Wake up cards without a CSN (ie. all of them) */ 24239444Smsmith isapnp_write(WAKE, 0); 24340597Smsmith isapnp_write(SET_RD_DATA, (isapnp_readport >> 2)); 24439444Smsmith outb(_PNP_ADDRESS, SERIAL_ISOLATION); 24539444Smsmith delay(1000); /* Delay 1 msec */ 24639444Smsmith 24739444Smsmith if (isapnp_get_serial(cardid)) { 24839444Smsmith isapnp_write(SET_CSN, csn); 24940553Smsmith pi = pnp_allocinfo(); 25039444Smsmith ndevs++; 25140597Smsmith pnp_addident(pi, pnp_eisaformat(cardid)); 25239444Smsmith /* scan the card obtaining all the identifiers it holds */ 25340553Smsmith if (isapnp_scan_resdata(pi)) { 25440553Smsmith pnp_freeinfo(pi); /* error getting data, ignore */ 25540553Smsmith } else { 25640553Smsmith pnp_addinfo(pi); 25739444Smsmith } 25840553Smsmith } else { 25939444Smsmith break; 26040553Smsmith } 26139444Smsmith } 26239444Smsmith /* Move all cards to wait-for-key state */ 26340553Smsmith while (--csn > 0) { 26439444Smsmith isapnp_send_Initiation_LFSR(); 26539444Smsmith isapnp_write(WAKE, csn); 26639444Smsmith isapnp_write(CONFIG_CONTROL, 0x02); 26739444Smsmith delay(1000); /* XXX is it really necessary ? */ 26840553Smsmith csn--; 26939444Smsmith } 27039444Smsmith return(ndevs); 27139444Smsmith} 27239444Smsmith 27339444Smsmith/* 27439444Smsmith * Locate ISA-PnP devices and populate the supplied list. 27539444Smsmith */ 27639444Smsmithstatic void 27740553Smsmithisapnp_enumerate(void) 27839444Smsmith{ 27940597Smsmith int pnp_rd_port; 28040597Smsmith 28140597Smsmith /* Check for I/O port access */ 28240597Smsmith if ((archsw.arch_isainb == NULL) || (archsw.arch_isaoutb == NULL)) 28340597Smsmith return; 28439444Smsmith 28540597Smsmith /* 28640597Smsmith * Validate a possibly-suggested read port value. If the autoscan failed 28740597Smsmith * last time, this will return us to autoscan mode again. 28840597Smsmith */ 28940597Smsmith if ((isapnp_readport > 0) && 29040597Smsmith (((isapnp_readport < 0x203) || 29140597Smsmith (isapnp_readport > 0x3ff) || 29240597Smsmith (isapnp_readport & 0x3) != 0x3))) 29340597Smsmith /* invalid, go look for ourselves */ 29440597Smsmith isapnp_readport = 0; 29540553Smsmith 29640597Smsmith if (isapnp_readport < 0) { 29740597Smsmith /* someone is telling us there is no ISA in the system */ 29840597Smsmith return; 29940597Smsmith 30040597Smsmith } else if (isapnp_readport > 0) { 30140597Smsmith /* Someone has told us where the port is/should be, or we found one last time */ 30240597Smsmith isapnp_isolation_protocol(); 30340597Smsmith 30440597Smsmith } else { 30540597Smsmith /* No clues, look for it ourselves */ 30640597Smsmith for (pnp_rd_port = 0x80; pnp_rd_port < 0xff; pnp_rd_port += 0x10) { 30740597Smsmith /* Look for something, quit when we find it */ 30840597Smsmith isapnp_readport = (pnp_rd_port << 2) | 0x3; 30940597Smsmith if (isapnp_isolation_protocol() > 0) 31040597Smsmith break; 31140597Smsmith } 31239444Smsmith } 31339444Smsmith} 314