1235368Sgnn/*-
2235368Sgnn * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3235368Sgnn * All rights reserved.
4235368Sgnn *
5235368Sgnn * Redistribution and use in source and binary forms, with or without
6235368Sgnn * modification, are permitted provided that the following conditions
7235368Sgnn * are met:
8235368Sgnn * 1. Redistributions of source code must retain the above copyright
9235368Sgnn *    notice, this list of conditions and the following disclaimer.
10235368Sgnn * 2. Redistributions in binary form must reproduce the above copyright
11235368Sgnn *    notice, this list of conditions and the following disclaimer in the
12235368Sgnn *    documentation and/or other materials provided with the distribution.
13235368Sgnn *
14235368Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235368Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235368Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235368Sgnn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18235368Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235368Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235368Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235368Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235368Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235368Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235368Sgnn * SUCH DAMAGE.
25235368Sgnn */
26235368Sgnn
27235368Sgnn#include <sys/cdefs.h>
28235368Sgnn__FBSDID("$FreeBSD$");
29235368Sgnn
30235368Sgnn/*
31235368Sgnn * PnP enumerator using the PCI BIOS.
32235368Sgnn */
33235368Sgnn
34235368Sgnn#include <stand.h>
35235368Sgnn#include <machine/stdarg.h>
36235368Sgnn#include <bootstrap.h>
37235368Sgnn#include <isapnp.h>
38235368Sgnn#include <btxv86.h>
39235368Sgnn#include "libi386.h"
40235368Sgnn
41235368Sgnn/*
42235368Sgnn * Stupid PCI BIOS interface doesn't let you simply enumerate everything
43235368Sgnn * that's there, instead you have to ask it if it has something.
44235368Sgnn *
45235368Sgnn * So we have to scan by class code, subclass code and sometimes programming
46235368Sgnn * interface.
47235368Sgnn */
48235368Sgnn
49235368Sgnnstruct pci_progif
50235368Sgnn{
51235368Sgnn    int		pi_code;
52235368Sgnn    const char	*pi_name;
53};
54
55static struct pci_progif progif_null[] = {
56    {0x0,	NULL},
57    {-1,	NULL}
58};
59
60static struct pci_progif progif_display[] = {
61    {0x0,	"VGA"},
62    {0x1,	"8514"},
63    {-1,	NULL}
64};
65
66static struct pci_progif progif_ide[] = {
67    {0x00,	NULL},
68    {0x01,	NULL},
69    {0x02,	NULL},
70    {0x03,	NULL},
71    {0x04,	NULL},
72    {0x05,	NULL},
73    {0x06,	NULL},
74    {0x07,	NULL},
75    {0x08,	NULL},
76    {0x09,	NULL},
77    {0x0a,	NULL},
78    {0x0b,	NULL},
79    {0x0c,	NULL},
80    {0x0d,	NULL},
81    {0x0e,	NULL},
82    {0x0f,	NULL},
83    {0x80,	NULL},
84    {0x81,	NULL},
85    {0x82,	NULL},
86    {0x83,	NULL},
87    {0x84,	NULL},
88    {0x85,	NULL},
89    {0x86,	NULL},
90    {0x87,	NULL},
91    {0x88,	NULL},
92    {0x89,	NULL},
93    {0x8a,	NULL},
94    {0x8b,	NULL},
95    {0x8c,	NULL},
96    {0x8d,	NULL},
97    {0x8e,	NULL},
98    {0x8f,	NULL},
99    {-1,	NULL}
100};
101
102static struct pci_progif progif_serial[] = {
103    {0x0,	"8250"},
104    {0x1,	"16450"},
105    {0x2,	"16550"},
106    {-1,	NULL}
107};
108
109static struct pci_progif progif_parallel[] = {
110    {0x0,	"Standard"},
111    {0x1,	"Bidirectional"},
112    {0x2,	"ECP"},
113    {-1,	NULL}
114};
115
116static struct pci_progif progif_firewire[] = {
117    {0x10,	"OHCI"},
118    {-1,	NULL}
119};
120
121struct pci_subclass
122{
123    int			ps_subclass;
124    const char		*ps_name;
125    struct pci_progif	*ps_progif;	/* if set, use for programming interface value(s) */
126};
127
128static struct pci_subclass subclass_old[] = {
129    {0x0,	"Old non-VGA",		progif_null},
130    {0x1,	"Old VGA",		progif_null},
131    {-1,	NULL,			NULL}
132};
133
134static struct pci_subclass subclass_mass[] = {
135    {0x0,	"SCSI",			progif_null},
136    {0x1,	"IDE",			progif_ide},
137    {0x2,	"Floppy disk",		progif_null},
138    {0x3,	"IPI",			progif_null},
139    {0x4,	"RAID",			progif_null},
140    {0x80,	"mass storage",		progif_null},
141    {-1,	NULL,			NULL}
142};
143
144static struct pci_subclass subclass_net[] = {
145    {0x0,	"Ethernet",		progif_null},
146    {0x1,	"Token ring",		progif_null},
147    {0x2,	"FDDI",			progif_null},
148    {0x3,	"ATM",			progif_null},
149    {0x80,	"network",		progif_null},
150    {-1,	NULL,			NULL}
151};
152
153static struct pci_subclass subclass_display[] = {
154    {0x0,	NULL,			progif_display},
155    {0x1,	"XGA",			progif_null},
156    {0x80,	"other",		progif_null},
157    {-1,	NULL,			NULL}
158};
159
160static struct pci_subclass subclass_comms[] = {
161    {0x0,	"serial",		progif_serial},
162    {0x1,	"parallel",		progif_parallel},
163    {0x80,	"communications",	progif_null},
164    {-1,	NULL,			NULL}
165};
166
167static struct pci_subclass subclass_serial[] = {
168    {0x0,	"FireWire",		progif_firewire},
169    {0x1,	"ACCESS.bus",		progif_null},
170    {0x2,	"SSA",			progif_null},
171    {0x3,	"USB",			progif_null},
172    {0x4,	"Fibrechannel",		progif_null},
173    {-1,	NULL,			NULL}
174};
175
176static struct pci_class
177{
178    int			pc_class;
179    const char		*pc_name;
180    struct pci_subclass	*pc_subclass;
181} pci_classes[] = {
182    {0x0,	"device",	subclass_old},
183    {0x1,	"controller",	subclass_mass},
184    {0x2,	"controller",	subclass_net},
185    {0x3,	"display",	subclass_display},
186    {0x7,	"controller",	subclass_comms},
187    {0xc,	"controller",	subclass_serial},
188    {-1,	NULL,		NULL}
189};
190
191
192static void	biospci_enumerate(void);
193static void	biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi);
194
195static int	biospci_version;
196static int	biospci_hwcap;
197
198struct pnphandler biospcihandler =
199{
200    "PCI BIOS",
201    biospci_enumerate
202};
203
204static void
205biospci_enumerate(void)
206{
207    int			device_index, err;
208    uint32_t		locator, devid;
209    struct pci_class	*pc;
210    struct pci_subclass *psc;
211    struct pci_progif	*ppi;
212
213    /* Find the PCI BIOS */
214    v86.ctl = V86_FLAGS;
215    v86.addr = 0x1a;
216    v86.eax = 0xb101;
217    v86.edi = 0x0;
218    v86int();
219
220    /* Check for OK response */
221    if (V86_CY(v86.efl) || ((v86.eax & 0xff00) != 0) ||
222	(v86.edx != 0x20494350))
223	return;
224
225    biospci_version = v86.ebx & 0xffff;
226    biospci_hwcap = v86.eax & 0xff;
227#if 0
228    printf("PCI BIOS %d.%d%s%s\n",
229	   bcd2bin((biospci_version >> 8) & 0xf), bcd2bin(biospci_version & 0xf),
230	   (biospci_hwcap & 1) ? " config1" : "", (biospci_hwcap & 2) ? " config2" : "");
231#endif
232    /* Iterate over known classes */
233    for (pc = pci_classes; pc->pc_class >= 0; pc++) {
234	/* Iterate over subclasses */
235	for (psc = pc->pc_subclass; psc->ps_subclass >= 0; psc++) {
236	    /* Iterate over programming interfaces */
237	    for (ppi = psc->ps_progif; ppi->pi_code >= 0; ppi++) {
238
239		/* Scan for matches */
240		for (device_index = 0; ; device_index++) {
241		    /* Look for a match */
242		    err = biospci_find_devclass((pc->pc_class << 16)
243			+ (psc->ps_subclass << 8) + ppi->pi_code,
244			device_index, &locator);
245		    if (err != 0)
246			break;
247
248		    /* Read the device identifier from the nominated device */
249		    err = biospci_read_config(locator, 0, 2, &devid);
250		    if (err != 0)
251			break;
252
253		    /* We have the device ID, create a PnP object and save everything */
254		    biospci_addinfo(devid, pc, psc, ppi);
255		}
256	    }
257	}
258    }
259}
260
261static void
262biospci_addinfo(int devid, struct pci_class *pc, struct pci_subclass *psc, struct pci_progif *ppi)
263{
264    struct pnpinfo	*pi;
265    char		desc[80];
266
267
268    /* build the description */
269    desc[0] = 0;
270    if (ppi->pi_name != NULL) {
271	strcat(desc, ppi->pi_name);
272	strcat(desc, " ");
273    }
274    if (psc->ps_name != NULL) {
275	strcat(desc, psc->ps_name);
276	strcat(desc, " ");
277    }
278    if (pc->pc_name != NULL)
279	strcat(desc, pc->pc_name);
280
281    pi = pnp_allocinfo();
282    pi->pi_desc = strdup(desc);
283    sprintf(desc,"0x%08x", devid);
284    pnp_addident(pi, desc);
285    pnp_addinfo(pi);
286}
287
288int
289biospci_find_devclass(uint32_t class, int index, uint32_t *locator)
290{
291	v86.ctl = V86_FLAGS;
292	v86.addr = 0x1a;
293	v86.eax = 0xb103;
294	v86.ecx = class;
295	v86.esi = index;
296	v86int();
297
298	 /* error */
299	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
300		return (-1);
301
302	*locator = v86.ebx;
303	return (0);
304}
305/*
306 * Configuration space access methods.
307 * width = 0(byte), 1(word) or 2(dword).
308 */
309int
310biospci_write_config(uint32_t locator, int offset, int width, uint32_t val)
311{
312	v86.ctl = V86_FLAGS;
313	v86.addr = 0x1a;
314	v86.eax = 0xb10b + width;
315	v86.ebx = locator;
316	v86.edi = offset;
317	v86.ecx = val;
318	v86int();
319
320	 /* error */
321	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
322		return (-1);
323
324	return(0);
325}
326
327int
328biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val)
329{
330	v86.ctl = V86_FLAGS;
331	v86.addr = 0x1a;
332	v86.eax = 0xb108 + width;
333	v86.ebx = locator;
334	v86.edi = offset;
335	v86int();
336
337	 /* error */
338	if (V86_CY(v86.efl) || (v86.eax & 0xff00))
339		return (-1);
340
341	*val = v86.ecx;
342	return (0);
343}
344
345uint32_t
346biospci_locator(int8_t bus, uint8_t device, uint8_t function)
347{
348
349	return ((bus << 8) | ((device & 0x1f) << 3) | (function & 0x7));
350}
351