pci_cfgreg.c revision 67311
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 67311 2000-10-19 08:06:50Z 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#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 *
137 * XXX we don't do anything "right" with the function number in the PIR table
138 *     (because the consumer isn't currently passing it in).
139 */
140int
141pci_cfgintr(int bus, int device, int pin)
142{
143    struct PIR_entry	*pe;
144    int			i, irq;
145    struct bios_regs	args;
146
147    if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) ||
148      (pin < 1) || (pin > 4))
149	return(255);
150
151    /*
152     * Scan the entry table for a contender
153     */
154    for (i = 0, pe = pci_route_table; i < pci_route_count; i++, pe++) {
155	if ((bus != pe->pe_bus) || (device != pe->pe_device))
156	    continue;
157	if (!powerof2(pe->pe_intpin[pin - 1].irqs)) {
158	    printf("pci_cfgintr: %d:%d:%c is not routed to a unique interrupt\n",
159		   bus, device, 'A' + pin - 1);
160	    break;
161	}
162	irq = ffs(pe->pe_intpin[pin - 1].irqs) - 1;
163	printf("pci_cfgintr: %d:%d:%c routed to irq %d\n",
164	       bus, device, 'A' + pin - 1, irq);
165
166	/*
167	 * Ask the BIOS to route the interrupt
168	 */
169	args.eax = PCIBIOS_ROUTE_INTERRUPT;
170	args.ebx = (bus << 8) | (device << 3);
171	args.ecx = (irq << 8) | (0xa + pin - 1);	/* pin value is 0xa - 0xd */
172	bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
173
174	/* XXX if it fails, we should smack the router hardware directly */
175
176	return(irq);
177    }
178    return(255);
179}
180
181
182/*
183 * Config space access using BIOS functions
184 */
185static int
186pcibios_cfgread(int bus, int slot, int func, int reg, int bytes)
187{
188    struct bios_regs args;
189    u_int mask;
190
191    switch(bytes) {
192    case 1:
193	args.eax = PCIBIOS_READ_CONFIG_BYTE;
194	mask = 0xff;
195	break;
196    case 2:
197	args.eax = PCIBIOS_READ_CONFIG_WORD;
198	mask = 0xffff;
199	break;
200    case 4:
201	args.eax = PCIBIOS_READ_CONFIG_DWORD;
202	mask = 0xffffffff;
203	break;
204    default:
205	return(-1);
206    }
207    args.ebx = (bus << 8) | (slot << 3) | func;
208    args.edi = reg;
209    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
210    /* check call results? */
211    return(args.ecx & mask);
212}
213
214static void
215pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
216{
217    struct bios_regs args;
218
219    switch(bytes) {
220    case 1:
221	args.eax = PCIBIOS_WRITE_CONFIG_BYTE;
222	break;
223    case 2:
224	args.eax = PCIBIOS_WRITE_CONFIG_WORD;
225	break;
226    case 4:
227	args.eax = PCIBIOS_WRITE_CONFIG_DWORD;
228	break;
229    default:
230	return;
231    }
232    args.ebx = (bus << 8) | (slot << 3) | func;
233    args.ecx = data;
234    args.edi = reg;
235    bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL));
236}
237
238/*
239 * Determine whether there is a PCI BIOS present
240 */
241static int
242pcibios_cfgopen(void)
243{
244    /* check for a found entrypoint */
245    return(PCIbios.entry != 0);
246}
247
248/*
249 * Configuration space access using direct register operations
250 */
251
252/* enable configuration space accesses and return data port address */
253static int
254pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
255{
256    int dataport = 0;
257
258    if (bus <= PCI_BUSMAX
259	&& slot < devmax
260	&& func <= PCI_FUNCMAX
261	&& reg <= PCI_REGMAX
262	&& bytes != 3
263	&& (unsigned) bytes <= 4
264	&& (reg & (bytes -1)) == 0) {
265	switch (cfgmech) {
266	case 1:
267	    outl(CONF1_ADDR_PORT, (1 << 31)
268		 | (bus << 16) | (slot << 11)
269		 | (func << 8) | (reg & ~0x03));
270	    dataport = CONF1_DATA_PORT + (reg & 0x03);
271	    break;
272	case 2:
273	    outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
274	    outb(CONF2_FORWARD_PORT, bus);
275	    dataport = 0xc000 | (slot << 8) | reg;
276	    break;
277	}
278    }
279    return (dataport);
280}
281
282/* disable configuration space accesses */
283static void
284pci_cfgdisable(void)
285{
286    switch (cfgmech) {
287    case 1:
288	outl(CONF1_ADDR_PORT, 0);
289	break;
290    case 2:
291	outb(CONF2_ENABLE_PORT, 0);
292	outb(CONF2_FORWARD_PORT, 0);
293	break;
294    }
295}
296
297static int
298pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
299{
300    int data = -1;
301    int port;
302
303    port = pci_cfgenable(bus, slot, func, reg, bytes);
304
305    if (port != 0) {
306	switch (bytes) {
307	case 1:
308	    data = inb(port);
309	    break;
310	case 2:
311	    data = inw(port);
312	    break;
313	case 4:
314	    data = inl(port);
315	    break;
316	}
317	pci_cfgdisable();
318    }
319    return (data);
320}
321
322static void
323pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
324{
325    int port;
326
327    port = pci_cfgenable(bus, slot, func, reg, bytes);
328    if (port != 0) {
329	switch (bytes) {
330	case 1:
331	    outb(port, data);
332	    break;
333	case 2:
334	    outw(port, data);
335	    break;
336	case 4:
337	    outl(port, data);
338	    break;
339	}
340	pci_cfgdisable();
341    }
342}
343
344/* check whether the configuration mechanism has been correctly identified */
345static int
346pci_cfgcheck(int maxdev)
347{
348    u_char device;
349
350    if (bootverbose)
351	printf("pci_cfgcheck:\tdevice ");
352
353    for (device = 0; device < maxdev; device++) {
354	unsigned id, class, header;
355	if (bootverbose)
356	    printf("%d ", device);
357
358	id = inl(pci_cfgenable(0, device, 0, 0, 4));
359	if (id == 0 || id == -1)
360	    continue;
361
362	class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
363	if (bootverbose)
364	    printf("[class=%06x] ", class);
365	if (class == 0 || (class & 0xf870ff) != 0)
366	    continue;
367
368	header = inb(pci_cfgenable(0, device, 0, 14, 1));
369	if (bootverbose)
370	    printf("[hdr=%02x] ", header);
371	if ((header & 0x7e) != 0)
372	    continue;
373
374	if (bootverbose)
375	    printf("is there (id=%08x)\n", id);
376
377	pci_cfgdisable();
378	return (1);
379    }
380    if (bootverbose)
381	printf("-- nothing found\n");
382
383    pci_cfgdisable();
384    return (0);
385}
386
387static int
388pcireg_cfgopen(void)
389{
390    unsigned long mode1res,oldval1;
391    unsigned char mode2res,oldval2;
392
393    oldval1 = inl(CONF1_ADDR_PORT);
394
395    if (bootverbose) {
396	printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
397	       oldval1);
398    }
399
400    if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
401
402	cfgmech = 1;
403	devmax = 32;
404
405	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
406	outb(CONF1_ADDR_PORT +3, 0);
407	mode1res = inl(CONF1_ADDR_PORT);
408	outl(CONF1_ADDR_PORT, oldval1);
409
410	if (bootverbose)
411	    printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
412		   mode1res, CONF1_ENABLE_CHK);
413
414	if (mode1res) {
415	    if (pci_cfgcheck(32))
416		return (cfgmech);
417	}
418
419	outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
420	mode1res = inl(CONF1_ADDR_PORT);
421	outl(CONF1_ADDR_PORT, oldval1);
422
423	if (bootverbose)
424	    printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
425		   mode1res, CONF1_ENABLE_CHK1);
426
427	if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
428	    if (pci_cfgcheck(32))
429		return (cfgmech);
430	}
431    }
432
433    oldval2 = inb(CONF2_ENABLE_PORT);
434
435    if (bootverbose) {
436	printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
437	       oldval2);
438    }
439
440    if ((oldval2 & 0xf0) == 0) {
441
442	cfgmech = 2;
443	devmax = 16;
444
445	outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
446	mode2res = inb(CONF2_ENABLE_PORT);
447	outb(CONF2_ENABLE_PORT, oldval2);
448
449	if (bootverbose)
450	    printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
451		   mode2res, CONF2_ENABLE_CHK);
452
453	if (mode2res == CONF2_ENABLE_RES) {
454	    if (bootverbose)
455		printf("pci_open(2a):\tnow trying mechanism 2\n");
456
457	    if (pci_cfgcheck(16))
458		return (cfgmech);
459	}
460    }
461
462    cfgmech = 0;
463    devmax = 0;
464    return (cfgmech);
465}
466
467