pci_cfgreg.c revision 67185
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 67185 2000-10-16 07:25:08Z imp $
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#include <vm/vm.h>
39#include <vm/pmap.h>
40#include <machine/md_var.h>
41#include <pci/pcivar.h>
42#include <pci/pcireg.h>
43#include <isa/isavar.h>
44#include <machine/nexusvar.h>
45#include <machine/pci_cfgreg.h>
46#include <machine/segments.h>
47#include <machine/pc/bios.h>
48
49#include "pcib_if.h"
50
51static int cfgmech;
52static int devmax;
53static int usebios;
54
55static int	pcibios_cfgread(int bus, int slot, int func, int reg, int bytes);
56static void	pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
57static int	pcibios_cfgopen(void);
58static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
59static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
60static int	pcireg_cfgopen(void);
61
62static struct PIR_entry	*pci_route_table;
63static int		pci_route_count;
64
65/*
66 * Initialise access to PCI configuration space
67 */
68int
69pci_cfgregopen(void)
70{
71    static int			opened = 0;
72    u_long			sigaddr;
73    static struct PIR_table	*pt;
74    u_int8_t			ck, *cv;
75    int				i;
76
77    if (opened)
78	return(1);
79
80    if (pcibios_cfgopen() != 0) {
81	usebios = 1;
82    } else if (pcireg_cfgopen() != 0) {
83	usebios = 0;
84    } else {
85	return(0);
86    }
87
88    /*
89     * Look for the interrupt routing table.
90     */
91    /* XXX use PCI BIOS if it's available */
92
93    if ((pt == NULL) && ((sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0)) != 0)) {
94	pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
95	for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) {
96	    ck += cv[i];
97	}
98	if (ck == 0) {
99	    pci_route_table = &pt->pt_entry[0];
100	    pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry);
101	    printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table);
102	}
103    }
104
105    opened = 1;
106    return(1);
107}
108
109/*
110 * Read configuration space register
111 */
112u_int32_t
113pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
114{
115    return(usebios ?
116	   pcibios_cfgread(bus, slot, func, reg, bytes) :
117	   pcireg_cfgread(bus, slot, func, reg, bytes));
118}
119
120/*
121 * Write configuration space register
122 */
123void
124pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
125{
126    return(usebios ?
127	   pcibios_cfgwrite(bus, slot, func, reg, data, bytes) :
128	   pcireg_cfgwrite(bus, slot, func, reg, data, bytes));
129}
130
131/*
132 * Route a PCI interrupt
133 *
134 * XXX this needs to learn to actually route uninitialised interrupts as well
135 *     as just returning interrupts for stuff that's already initialised.
136 */
137int
138pci_cfgintr(int bus, int device, int pin)
139{
140    struct PIR_entry	*pe;
141    int			i;
142
143    if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || (pin < 1) || (pin > 4)) {
144	printf("bus %d pin %d device %d, returning 255\n", bus, pin, device);
145	return(255);
146    }
147
148    /*
149     * Scan the entry table for a contender
150     */
151    printf("bus %d device %d\n", bus, device);
152    for (i = 0, pe = pci_route_table; i < pci_route_count; i++, pe++) {
153	printf("pe_bus %d pe_device %d\n", pe->pe_bus, pe->pe_device);
154	if ((bus != pe->pe_bus) || (device != pe->pe_device))
155	    continue;
156	if (!powerof2(pe->pe_intpin[pin - 1].irqs)) {
157	    printf("pci_cfgintr: %d:%d:%c is not routed to a unique interrupt\n", bus, device, 'A' + pin - 1);
158	    break;
159	}
160	printf("pci_cfgintr: %d:%d:%c routed to irq %d\n", bus, device, 'A' + pin - 1, ffs(pe->pe_intpin[pin - 1].irqs));
161	return(ffs(pe->pe_intpin[pin - 1].irqs));
162    }
163    return(255);
164}
165
166
167/*
168 * Config space access using BIOS functions
169 */
170static int
171pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
172{
173    struct bios_regs args;
174    u_int mask;
175
176    switch(bytes) {
177    case 1:
178	args.eax = PCIBIOS_READ_CONFIG_BYTE;
179	mask = 0xff;
180	break;
181    case 2:
182	args.eax = PCIBIOS_READ_CONFIG_WORD;
183	mask = 0xffff;
184	break;
185    case 4:
186	args.eax = PCIBIOS_READ_CONFIG_DWORD;
187	mask = 0xffffffff;
188	break;
189    default:
190	return(-1);
191    }
192    args.ebx = (bus << 8) | (slot << 3) | func;
193    args.edi = reg;
194    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
195    /* check call results? */
196    return(args.ecx & mask);
197}
198
199static void
200pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
201{
202    struct bios_regs args;
203
204    switch(bytes) {
205    case 1:
206	args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
207	break;
208    case 2:
209	args.eax = PCIBIOS_WRITE_CONFIG_WORD;
210	break;
211    case 4:
212	args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
213	break;
214    default:
215	return;
216    }
217    args.ebx = (bus << 8) | (slot << 3) | func;
218    args.ecx = data;
219    args.edi = reg;
220    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
221}
222
223/*
224 * Determine whether there is a PCI BIOS present
225 */
226static int
227pcibios_cfgopen(void)
228{
229    /* check for a found entrypoint */
230    return(PCIbios.entry != 0);
231}
232
233/*
234 * Configuration space access using direct register operations
235 */
236
237/* enable configuration space accesses and return data port address */
238static int
239pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
240{
241    int dataport = 0;
242
243    if (bus <= PCI_BUSMAX
244	&& slot < devmax
245	&& func <= PCI_FUNCMAX
246	&& reg <= PCI_REGMAX
247	&& bytes != 3
248	&& (unsigned) bytes <= 4
249	&& (reg & (bytes -1)) == 0) {
250	switch (cfgmech) {
251	case 1:
252	    outl(CONF1_ADDR_PORT, (1 << 31)
253		 | (bus << 16) | (slot << 11)
254		 | (func << 8) | (reg & ~0x03));
255	    dataport = CONF1_DATA_PORT + (reg & 0x03);
256	    break;
257	case 2:
258	    outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
259	    outb(CONF2_FORWARD_PORT, bus);
260	    dataport = 0xc000 | (slot << 8) | reg;
261	    break;
262	}
263    }
264    return (dataport);
265}
266
267/* disable configuration space accesses */
268static void
269pci_cfgdisable(void)
270{
271    switch (cfgmech) {
272    case 1:
273	outl(CONF1_ADDR_PORT, 0);
274	break;
275    case 2:
276	outb(CONF2_ENABLE_PORT, 0);
277	outb(CONF2_FORWARD_PORT, 0);
278	break;
279    }
280}
281
282static int
283pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
284{
285    int data = -1;
286    int port;
287
288    port = pci_cfgenable(bus, slot, func, reg, bytes);
289
290    if (port != 0) {
291	switch (bytes) {
292	case 1:
293	    data = inb(port);
294	    break;
295	case 2:
296	    data = inw(port);
297	    break;
298	case 4:
299	    data = inl(port);
300	    break;
301	}
302	pci_cfgdisable();
303    }
304    return (data);
305}
306
307static void
308pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
309{
310    int port;
311
312    port = pci_cfgenable(bus, slot, func, reg, bytes);
313    if (port != 0) {
314	switch (bytes) {
315	case 1:
316	    outb(port, data);
317	    break;
318	case 2:
319	    outw(port, data);
320	    break;
321	case 4:
322	    outl(port, data);
323	    break;
324	}
325	pci_cfgdisable();
326    }
327}
328
329/* check whether the configuration mechanism has been correctly identified */
330static int
331pci_cfgcheck(int maxdev)
332{
333    u_char device;
334
335    if (bootverbose)
336	printf("pci_cfgcheck:\tdevice ");
337
338    for (device = 0; device < maxdev; device++) {
339	unsigned id, class, header;
340	if (bootverbose)
341	    printf("%d ", device);
342
343	id = inl(pci_cfgenable(0, device, 0, 0, 4));
344	if (id == 0 || id == -1)
345	    continue;
346
347	class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
348	if (bootverbose)
349	    printf("[class=%06x] ", class);
350	if (class == 0 || (class & 0xf870ff) != 0)
351	    continue;
352
353	header = inb(pci_cfgenable(0, device, 0, 14, 1));
354	if (bootverbose)
355	    printf("[hdr=%02x] ", header);
356	if ((header & 0x7e) != 0)
357	    continue;
358
359	if (bootverbose)
360	    printf("is there (id=%08x)\n", id);
361
362	pci_cfgdisable();
363	return (1);
364    }
365    if (bootverbose)
366	printf("-- nothing found\n");
367
368    pci_cfgdisable();
369    return (0);
370}
371
372static int
373pcireg_cfgopen(void)
374{
375    unsigned long mode1res,oldval1;
376    unsigned char mode2res,oldval2;
377
378    oldval1 = inl(CONF1_ADDR_PORT);
379
380    if (bootverbose) {
381	printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
382	       oldval1);
383    }
384
385    if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
386
387	cfgmech = 1;
388	devmax = 32;
389
390	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
391	outb(CONF1_ADDR_PORT +3, 0);
392	mode1res = inl(CONF1_ADDR_PORT);
393	outl(CONF1_ADDR_PORT, oldval1);
394
395	if (bootverbose)
396	    printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
397		   mode1res, CONF1_ENABLE_CHK);
398
399	if (mode1res) {
400	    if (pci_cfgcheck(32))
401		return (cfgmech);
402	}
403
404	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
405	mode1res = inl(CONF1_ADDR_PORT);
406	outl(CONF1_ADDR_PORT, oldval1);
407
408	if (bootverbose)
409	    printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
410		   mode1res, CONF1_ENABLE_CHK1);
411
412	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
413	    if (pci_cfgcheck(32))
414		return (cfgmech);
415	}
416    }
417
418    oldval2 = inb(CONF2_ENABLE_PORT);
419
420    if (bootverbose) {
421	printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
422	       oldval2);
423    }
424
425    if ((oldval2 & 0xf0) == 0) {
426
427	cfgmech = 2;
428	devmax = 16;
429
430	outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
431	mode2res = inb(CONF2_ENABLE_PORT);
432	outb(CONF2_ENABLE_PORT, oldval2);
433
434	if (bootverbose)
435	    printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
436		   mode2res, CONF2_ENABLE_CHK);
437
438	if (mode2res == CONF2_ENABLE_RES) {
439	    if (bootverbose)
440		printf("pci_open(2a):\tnow trying mechanism 2\n");
441
442	    if (pci_cfgcheck(16))
443		return (cfgmech);
444	}
445    }
446
447    cfgmech = 0;
448    devmax = 0;
449    return (cfgmech);
450}
451
452