1/*	$NetBSD: isapnp.c,v 1.4 2005/12/11 12:17:48 christos Exp $	 */
2
3/*
4 * Copyright (c) 1997
5 *	Matthias Drochner.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29/*
30 * minimal ISA PnP implementation: find adapter, return settings (1 IO and 1
31 * DMA only for now)
32 */
33
34#include <sys/types.h>
35#include <machine/pio.h>
36
37#include <lib/libsa/stand.h>
38
39#include <libi386.h>
40
41#include "isapnpvar.h"
42
43#define PNPADDR 0x0279
44#define PNPWDATA 0x0a79
45#define PNPRDATAMIN 0x0203
46#define PNPRDATAMAX 0x03ff
47
48enum {
49	DATAPORT,
50	ISOL,
51	CONTROL,
52	WAKE,
53	RESDATA,
54	RESSTAT,
55	SETCSN,
56	SETLDEV
57};
58
59#define MEMBASE 0x40
60#define IOBASE 0x60
61#define INTBASE 0x70
62#define DMABASE 0x74
63
64static int      pnpdataport;
65
66static int
67getiobase(int nr)
68{
69	unsigned short  iobase;
70
71	outb(PNPADDR, SETLDEV);
72	outb(PNPWDATA, 0);	/* subdev 0 */
73
74	outb(PNPADDR, IOBASE + nr * 2);
75	iobase = (inb(pnpdataport) << 8);
76	outb(PNPADDR, IOBASE + nr * 2 + 1);
77	iobase |= inb(pnpdataport);
78
79	return iobase;
80}
81
82static int
83getdmachan(int nr)
84{
85	unsigned char   dmachannel;
86
87	outb(PNPADDR, SETLDEV);
88	outb(PNPWDATA, 0);	/* subdev 0 */
89
90	outb(PNPADDR, DMABASE + nr);
91	dmachannel = inb(pnpdataport) & 0x07;
92
93	return dmachannel;
94}
95
96struct cardid {
97	unsigned char   eisaid[4];
98	unsigned int    serial;
99	unsigned char   crc;
100};
101
102/*
103 do isolation, call pnpscanresc() in board config state
104 */
105static int
106pnpisol(int csn)
107{
108	unsigned char   buf[9];
109	int             i, j;
110	struct cardid  *id;
111	unsigned char   crc = 0x6a;
112
113	/*
114	 * do 72 pairs of reads from ISOL register all but 1 go to sleep
115	 * state (ch. 3.3)
116	 */
117	outb(PNPADDR, ISOL);
118	delay(1000);
119
120	for (i = 0; i < 9; i++) {
121		for (j = 0; j < 8; j++) {
122			unsigned char   a, b;
123			int             bitset;
124
125			a = inb(pnpdataport);
126			b = inb(pnpdataport);
127			if ((a == 0x55) && (b == 0xaa))
128				bitset = 1;
129			else if ((a == 0xff) && (b == 0xff))
130				bitset = 0;
131			else
132				return -1;	/* data port conflict */
133
134			buf[i] = (buf[i] >> 1) | (bitset << 7);
135
136			if (i < 8)	/* calc crc for first 8 bytes (app.
137					 * B.2) */
138				crc = (crc >> 1) |
139				  ((bitset != ((crc & 1) == !(crc & 2))) << 7);
140
141			delay(250);
142		}
143	}
144	id = (struct cardid *) buf;
145
146	if (id->crc != crc)
147		return 0;	/* normal end */
148
149	outb(PNPADDR, SETCSN);
150	outb(PNPWDATA, csn);	/* set csn for winning card and put it to
151				 * config state */
152
153	return (id->eisaid[0] << 24) | (id->eisaid[1] << 16)
154		| (id->eisaid[2] << 8) | (id->eisaid[3]);
155}
156
157static void
158pnpisolreset(void)
159{
160	outb(PNPADDR, WAKE);
161	outb(PNPWDATA, 0);	/* put all remaining cards to isolation state */
162}
163
164/*
165 send initiation sequence (app. B.1)
166 */
167static void
168pnpinit(void)
169{
170	int             i;
171	unsigned char   key = 0x6a;
172
173	outb(PNPADDR, 0);
174	outb(PNPADDR, 0);
175
176	for (i = 0; i < 32; i++) {
177		outb(PNPADDR, key);
178		key = (key >> 1) |
179			(((key & 1) == !(key & 2)) << 7);
180	}
181}
182
183int
184isapnp_finddev(int id, int *iobase, int *dmachan)
185{
186	int             csn;
187
188	outb(PNPADDR, CONTROL);
189	outb(PNPWDATA, 2);	/* XXX force wait for key */
190
191	/* scan all allowed data ports (ch. 3.1) */
192	for (pnpdataport = PNPRDATAMIN; pnpdataport <= PNPRDATAMAX;
193	     pnpdataport += 4) {
194		int             res, found = 0;
195
196		pnpinit();	/* initiation sequence */
197
198		outb(PNPADDR, CONTROL);
199		outb(PNPWDATA, 4);	/* CSN=0 - only these respond to
200					 * WAKE[0] */
201
202		outb(PNPADDR, WAKE);
203		outb(PNPWDATA, 0);	/* put into isolation state */
204
205		outb(PNPADDR, DATAPORT);
206		outb(PNPWDATA, pnpdataport >> 2);	/* set READ_DATA port */
207
208		csn = 0;
209		do {
210			res = pnpisol(++csn);
211
212			if ((res) == id) {
213				if (iobase)
214					*iobase = getiobase(0);
215				if (dmachan)
216					*dmachan = getdmachan(0);
217				found = 1;
218			}
219			pnpisolreset();
220		} while ((res != 0) && (res != -1));
221
222		outb(PNPADDR, CONTROL);
223		outb(PNPWDATA, 2);	/* return to wait for key */
224
225		if (csn > 1)	/* at least 1 board found */
226			return !found;
227
228		/* if no board found, try next dataport */
229	}
230	return -1;		/* nothing found */
231}
232