pci_cfgreg.c revision 66529
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4 * Copyright (c) 2000, BSDi
5 * 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 unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 66529 2000-10-02 07:11:13Z msmith $
29 *
30 */
31
32#include <sys/param.h>		/* XXX trim includes */
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/module.h>
37#include <sys/malloc.h>
38
39#include <pci/pcivar.h>
40#include <pci/pcireg.h>
41#include <isa/isavar.h>
42#include <machine/nexusvar.h>
43#include <machine/pci_cfgreg.h>
44
45#include <machine/segments.h>
46#include <machine/pc/bios.h>
47
48#include "pcib_if.h"
49
50static int cfgmech;
51static int devmax;
52static int usebios;
53
54static int	pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
55static void	pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
56static int	pcibios_cfgopen(void);
57static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
58static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
59static int	pcireg_cfgopen(void);
60
61/*
62 * Initialise access to PCI configuration space
63 */
64int
65pci_cfgregopen(void)
66{
67    static int	opened = 0;
68
69    if (opened)
70	return(1);
71
72    if (pcibios_cfgopen() != 0) {
73	usebios = 1;
74    } else if (pcireg_cfgopen() != 0) {
75	usebios = 0;
76    } else {
77	return(0);
78    }
79    opened = 1;
80    return(1);
81}
82
83/*
84 * Read configuration space register
85 */
86u_int32_t
87pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
88{
89    return(usebios ?
90	   pcibios_cfgread(bus, slot, func, reg, bytes) :
91	   pcireg_cfgread(bus, slot, func, reg, bytes));
92}
93
94/*
95 * Write configuration space register
96 */
97void
98pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
99{
100    return(usebios ?
101	   pcibios_cfgwrite(bus, slot, func, reg, data, bytes) :
102	   pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
103}
104
105/*
106 * Config space access using BIOS functions
107 */
108static int
109pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
110{
111    struct bios_regs args;
112    u_int mask;
113
114    switch(bytes) {
115    case 1:
116	args.eax = PCIBIOS_READ_CONFIG_BYTE;
117	mask = 0xff;
118	break;
119    case 2:
120	args.eax = PCIBIOS_READ_CONFIG_WORD;
121	mask = 0xffff;
122	break;
123    case 4:
124	args.eax = PCIBIOS_READ_CONFIG_DWORD;
125	mask = 0xffffffff;
126	break;
127    default:
128	return(-1);
129    }
130    args.ebx = (bus << 8) | (slot << 3) | func;
131    args.edi = reg;
132    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
133    /* check call results? */
134    return(args.ecx & mask);
135}
136
137static void
138pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
139{
140    struct bios_regs args;
141
142    switch(bytes) {
143    case 1:
144	args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
145	break;
146    case 2:
147	args.eax = PCIBIOS_WRITE_CONFIG_WORD;
148	break;
149    case 4:
150	args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
151	break;
152    default:
153	return;
154    }
155    args.ebx = (bus << 8) | (slot << 3) | func;
156    args.ecx = data;
157    args.edi = reg;
158    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
159}
160
161/*
162 * Determine whether there is a PCI BIOS present
163 */
164static int
165pcibios_cfgopen(void)
166{
167    /* check for a found entrypoint */
168    return(PCIbios.entry != 0);
169}
170
171/*
172 * Configuration space access using direct register operations
173 */
174
175/* enable configuration space accesses and return data port address */
176static int
177pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
178{
179    int dataport = 0;
180
181    if (bus <= PCI_BUSMAX
182	&& slot < devmax
183	&& func <= PCI_FUNCMAX
184	&& reg <= PCI_REGMAX
185	&& bytes != 3
186	&& (unsigned) bytes <= 4
187	&& (reg & (bytes -1)) == 0) {
188	switch (cfgmech) {
189	case 1:
190	    outl(CONF1_ADDR_PORT, (1 << 31)
191		 | (bus << 16) | (slot << 11)
192		 | (func << 8) | (reg & ~0x03));
193	    dataport = CONF1_DATA_PORT + (reg & 0x03);
194	    break;
195	case 2:
196	    outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
197	    outb(CONF2_FORWARD_PORT, bus);
198	    dataport = 0xc000 | (slot << 8) | reg;
199	    break;
200	}
201    }
202    return (dataport);
203}
204
205/* disable configuration space accesses */
206static void
207pci_cfgdisable(void)
208{
209    switch (cfgmech) {
210    case 1:
211	outl(CONF1_ADDR_PORT, 0);
212	break;
213    case 2:
214	outb(CONF2_ENABLE_PORT, 0);
215	outb(CONF2_FORWARD_PORT, 0);
216	break;
217    }
218}
219
220static int
221pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
222{
223    int data = -1;
224    int port;
225
226    port = pci_cfgenable(bus, slot, func, reg, bytes);
227
228    if (port != 0) {
229	switch (bytes) {
230	case 1:
231	    data = inb(port);
232	    break;
233	case 2:
234	    data = inw(port);
235	    break;
236	case 4:
237	    data = inl(port);
238	    break;
239	}
240	pci_cfgdisable();
241    }
242    return (data);
243}
244
245static void
246pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
247{
248    int port;
249
250    port = pci_cfgenable(bus, slot, func, reg, bytes);
251    if (port != 0) {
252	switch (bytes) {
253	case 1:
254	    outb(port, data);
255	    break;
256	case 2:
257	    outw(port, data);
258	    break;
259	case 4:
260	    outl(port, data);
261	    break;
262	}
263	pci_cfgdisable();
264    }
265}
266
267/* check whether the configuration mechanism has been correctly identified */
268static int
269pci_cfgcheck(int maxdev)
270{
271    u_char device;
272
273    if (bootverbose)
274	printf("pci_cfgcheck:\tdevice ");
275
276    for (device = 0; device < maxdev; device++) {
277	unsigned id, class, header;
278	if (bootverbose)
279	    printf("%d ", device);
280
281	id = inl(pci_cfgenable(0, device, 0, 0, 4));
282	if (id == 0 || id == -1)
283	    continue;
284
285	class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
286	if (bootverbose)
287	    printf("[class=%06x] ", class);
288	if (class == 0 || (class & 0xf870ff) != 0)
289	    continue;
290
291	header = inb(pci_cfgenable(0, device, 0, 14, 1));
292	if (bootverbose)
293	    printf("[hdr=%02x] ", header);
294	if ((header & 0x7e) != 0)
295	    continue;
296
297	if (bootverbose)
298	    printf("is there (id=%08x)\n", id);
299
300	pci_cfgdisable();
301	return (1);
302    }
303    if (bootverbose)
304	printf("-- nothing found\n");
305
306    pci_cfgdisable();
307    return (0);
308}
309
310static int
311pcireg_cfgopen(void)
312{
313    unsigned long mode1res,oldval1;
314    unsigned char mode2res,oldval2;
315
316    oldval1 = inl(CONF1_ADDR_PORT);
317
318    if (bootverbose) {
319	printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
320	       oldval1);
321    }
322
323    if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
324
325	cfgmech = 1;
326	devmax = 32;
327
328	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
329	outb(CONF1_ADDR_PORT +3, 0);
330	mode1res = inl(CONF1_ADDR_PORT);
331	outl(CONF1_ADDR_PORT, oldval1);
332
333	if (bootverbose)
334	    printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
335		   mode1res, CONF1_ENABLE_CHK);
336
337	if (mode1res) {
338	    if (pci_cfgcheck(32))
339		return (cfgmech);
340	}
341
342	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
343	mode1res = inl(CONF1_ADDR_PORT);
344	outl(CONF1_ADDR_PORT, oldval1);
345
346	if (bootverbose)
347	    printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
348		   mode1res, CONF1_ENABLE_CHK1);
349
350	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
351	    if (pci_cfgcheck(32))
352		return (cfgmech);
353	}
354    }
355
356    oldval2 = inb(CONF2_ENABLE_PORT);
357
358    if (bootverbose) {
359	printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
360	       oldval2);
361    }
362
363    if ((oldval2 & 0xf0) == 0) {
364
365	cfgmech = 2;
366	devmax = 16;
367
368	outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
369	mode2res = inb(CONF2_ENABLE_PORT);
370	outb(CONF2_ENABLE_PORT, oldval2);
371
372	if (bootverbose)
373	    printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
374		   mode2res, CONF2_ENABLE_CHK);
375
376	if (mode2res == CONF2_ENABLE_RES) {
377	    if (bootverbose)
378		printf("pci_open(2a):\tnow trying mechanism 2\n");
379
380	    if (pci_cfgcheck(16))
381		return (cfgmech);
382	}
383    }
384
385    cfgmech = 0;
386    devmax = 0;
387    return (cfgmech);
388}
389
390