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