xref: /netbsd-src/sys/arch/i386/pci/pcibios.c (revision d47bcd296c8b39243dd81e9cc75ea86330d4eeaf)
1*d47bcd29Schs /*	$NetBSD: pcibios.c,v 1.40 2019/11/10 21:16:28 chs Exp $	*/
2e345f0f1Sthorpej 
3e345f0f1Sthorpej /*-
4e345f0f1Sthorpej  * Copyright (c) 1999 The NetBSD Foundation, Inc.
5e345f0f1Sthorpej  * All rights reserved.
6e345f0f1Sthorpej  *
7e345f0f1Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8e345f0f1Sthorpej  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9e345f0f1Sthorpej  * NASA Ames Research Center.
10e345f0f1Sthorpej  *
11e345f0f1Sthorpej  * Redistribution and use in source and binary forms, with or without
12e345f0f1Sthorpej  * modification, are permitted provided that the following conditions
13e345f0f1Sthorpej  * are met:
14e345f0f1Sthorpej  * 1. Redistributions of source code must retain the above copyright
15e345f0f1Sthorpej  *    notice, this list of conditions and the following disclaimer.
16e345f0f1Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
17e345f0f1Sthorpej  *    notice, this list of conditions and the following disclaimer in the
18e345f0f1Sthorpej  *    documentation and/or other materials provided with the distribution.
19e345f0f1Sthorpej  *
20e345f0f1Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21e345f0f1Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22e345f0f1Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23e345f0f1Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24e345f0f1Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25e345f0f1Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26e345f0f1Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27e345f0f1Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28e345f0f1Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29e345f0f1Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30e345f0f1Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
31e345f0f1Sthorpej  */
32e345f0f1Sthorpej 
33e345f0f1Sthorpej /*
34e345f0f1Sthorpej  * Copyright (c) 1999, by UCHIYAMA Yasushi
35e345f0f1Sthorpej  * All rights reserved.
36e345f0f1Sthorpej  *
37e345f0f1Sthorpej  * Redistribution and use in source and binary forms, with or without
38e345f0f1Sthorpej  * modification, are permitted provided that the following conditions
39e345f0f1Sthorpej  * are met:
40e345f0f1Sthorpej  * 1. Redistributions of source code must retain the above copyright
41e345f0f1Sthorpej  *    notice, this list of conditions and the following disclaimer.
42e345f0f1Sthorpej  * 2. The name of the developer may NOT be used to endorse or promote products
43e345f0f1Sthorpej  *    derived from this software without specific prior written permission.
44e345f0f1Sthorpej  *
45e345f0f1Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46e345f0f1Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47e345f0f1Sthorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48e345f0f1Sthorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49e345f0f1Sthorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50e345f0f1Sthorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51e345f0f1Sthorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52e345f0f1Sthorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53e345f0f1Sthorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54e345f0f1Sthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55e345f0f1Sthorpej  * SUCH DAMAGE.
56e345f0f1Sthorpej  */
57e345f0f1Sthorpej 
58e345f0f1Sthorpej /*
59e345f0f1Sthorpej  * Interface to the PCI BIOS and PCI Interrupt Routing table.
60e345f0f1Sthorpej  */
61e345f0f1Sthorpej 
6295c969f2Slukem #include <sys/cdefs.h>
63*d47bcd29Schs __KERNEL_RCSID(0, "$NetBSD: pcibios.c,v 1.40 2019/11/10 21:16:28 chs Exp $");
6495c969f2Slukem 
65e345f0f1Sthorpej #include "opt_pcibios.h"
660919f4d2Ssekiya #include "opt_pcifixup.h"
67e345f0f1Sthorpej 
68e345f0f1Sthorpej #include <sys/param.h>
69e345f0f1Sthorpej #include <sys/systm.h>
70e345f0f1Sthorpej #include <sys/device.h>
71e345f0f1Sthorpej #include <sys/malloc.h>
72e345f0f1Sthorpej 
73e345f0f1Sthorpej #include <dev/isa/isareg.h>
74e345f0f1Sthorpej #include <machine/isa_machdep.h>
75e345f0f1Sthorpej 
76e345f0f1Sthorpej #include <dev/pci/pcireg.h>
77e345f0f1Sthorpej #include <dev/pci/pcivar.h>
78e3c38828Such #include <dev/pci/pcidevs.h>
79e345f0f1Sthorpej 
80e345f0f1Sthorpej #include <i386/pci/pcibios.h>
81087e54e5Ssekiya 
82087e54e5Ssekiya #if 	defined(PCIBIOS_INTR_FIXUP) || defined(PCIBIOS_ADDR_FIXUP) || \
83087e54e5Ssekiya 	defined(PCIBIOS_BUS_FIXUP)
84087e54e5Ssekiya #error The options PCIBIOS_INTR_FIXUP, PCIBIOS_ADDR_FIXUP, and PCIBIOS_BUS_FIXUP have been obsoleted by PCI_INTR_FIXUP, PCI_ADDR_FIXUP, and PCI_BUS_FIXUP.  Please adjust your kernel configuration file.
85087e54e5Ssekiya #endif
86087e54e5Ssekiya 
870919f4d2Ssekiya #ifdef PCI_INTR_FIXUP
88e345f0f1Sthorpej #include <i386/pci/pci_intr_fixup.h>
89e345f0f1Sthorpej #endif
90e345f0f1Sthorpej 
91e345f0f1Sthorpej #include <machine/bios32.h>
92e345f0f1Sthorpej 
93698b2b11Ssoda #ifdef PCIBIOSVERBOSE
94698b2b11Ssoda int	pcibiosverbose = 1;
95698b2b11Ssoda #endif
96698b2b11Ssoda 
97e345f0f1Sthorpej int pcibios_present;
98e345f0f1Sthorpej 
99e345f0f1Sthorpej struct pcibios_pir_header pcibios_pir_header;
100e345f0f1Sthorpej struct pcibios_intr_routing *pcibios_pir_table;
101e345f0f1Sthorpej int pcibios_pir_table_nentries;
102e345f0f1Sthorpej int pcibios_max_bus;
103e345f0f1Sthorpej 
104e345f0f1Sthorpej struct bios32_entry pcibios_entry;
105e345f0f1Sthorpej 
106e8085086Skochi void	pcibios_pir_init(void);
107e345f0f1Sthorpej 
10868da4482Sperry int	pcibios_get_status(uint32_t *, uint32_t *, uint32_t *,
10968da4482Sperry 	    uint32_t *, uint32_t *, uint32_t *, uint32_t *);
110e8085086Skochi int	pcibios_get_intr_routing(struct pcibios_intr_routing *,
11168da4482Sperry 	    int *, uint16_t *);
112e345f0f1Sthorpej 
11368da4482Sperry int	pcibios_return_code(uint16_t, const char *);
114e345f0f1Sthorpej 
115e8085086Skochi void	pcibios_print_exclirq(void);
1164eac397fSchristos 
1174eac397fSchristos #ifdef PCIBIOS_LIBRETTO_FIXUP
1184eac397fSchristos /* for Libretto L2/L3 hack */
1194eac397fSchristos static void	pcibios_fixup_pir_table(void);
1204eac397fSchristos static void	pcibios_fixup_pir_table_mask(struct pcibios_linkmap *);
1214eac397fSchristos 
1224eac397fSchristos struct pcibios_linkmap pir_mask[] = {
1234eac397fSchristos 	{ 2,	0x0040 },
1244eac397fSchristos 	{ 7,	0x0080 },
1254eac397fSchristos 	{ 8,	0x0020 },
1264eac397fSchristos 	{ 0,	0x0000 }
1274eac397fSchristos };
1284eac397fSchristos #endif
1294eac397fSchristos 
130e93cdd69Saugustss #ifdef PCIBIOS_SHARP_MM20_FIXUP
131e93cdd69Saugustss static void pcibios_mm20_fixup(void);
132e93cdd69Saugustss #endif
133e93cdd69Saugustss 
134e345f0f1Sthorpej #ifdef PCIINTR_DEBUG
135e8085086Skochi void	pcibios_print_pir_table(void);
136e345f0f1Sthorpej #endif
137e345f0f1Sthorpej 
138e345f0f1Sthorpej #define	PCI_IRQ_TABLE_START	0xf0000
139e345f0f1Sthorpej #define	PCI_IRQ_TABLE_END	0xfffff
140e345f0f1Sthorpej 
141e345f0f1Sthorpej void
pcibios_init(void)14235adf403Sperry pcibios_init(void)
143e345f0f1Sthorpej {
144e345f0f1Sthorpej 	struct bios32_entry_info ei;
14568da4482Sperry 	uint32_t rev_maj, rev_min, mech1, mech2, scmech1, scmech2;
146e345f0f1Sthorpej 
147e345f0f1Sthorpej 	if (bios32_service(BIOS32_MAKESIG('$', 'P', 'C', 'I'),
148e345f0f1Sthorpej 	    &pcibios_entry, &ei) == 0) {
149e345f0f1Sthorpej 		/*
150e345f0f1Sthorpej 		 * No PCI BIOS found; will fall back on old
151e345f0f1Sthorpej 		 * mechanism.
152e345f0f1Sthorpej 		 */
153e345f0f1Sthorpej 		return;
154e345f0f1Sthorpej 	}
155e345f0f1Sthorpej 
156e345f0f1Sthorpej 	/*
157e345f0f1Sthorpej 	 * We've located the PCI BIOS service; get some information
158e345f0f1Sthorpej 	 * about it.
159e345f0f1Sthorpej 	 */
160e345f0f1Sthorpej 	if (pcibios_get_status(&rev_maj, &rev_min, &mech1, &mech2,
161e345f0f1Sthorpej 	    &scmech1, &scmech2, &pcibios_max_bus) != PCIBIOS_SUCCESS) {
162e345f0f1Sthorpej 		/*
163e345f0f1Sthorpej 		 * We can't use the PCI BIOS; will fall back on old
164e345f0f1Sthorpej 		 * mechanism.
165e345f0f1Sthorpej 		 */
166e345f0f1Sthorpej 		return;
167e345f0f1Sthorpej 	}
168e345f0f1Sthorpej 
169da702eb9Sjym 	aprint_normal("PCI BIOS rev. %d.%d found at %#" PRIxPADDR "\n",
170a9beff95Sthorpej 	    rev_maj, rev_min >> 4, ei.bei_entry);
171a9beff95Sthorpej 	aprint_verbose("pcibios: config mechanism %s%s, special cycles %s%s, "
172e345f0f1Sthorpej 	    "last bus %d\n",
173e345f0f1Sthorpej 	    mech1 ? "[1]" : "[x]",
174e345f0f1Sthorpej 	    mech2 ? "[2]" : "[x]",
175e345f0f1Sthorpej 	    scmech1 ? "[1]" : "[x]",
176e345f0f1Sthorpej 	    scmech2 ? "[2]" : "[x]",
177e345f0f1Sthorpej 	    pcibios_max_bus);
178e345f0f1Sthorpej 
179e345f0f1Sthorpej 	/*
180e345f0f1Sthorpej 	 * The PCI BIOS tells us the config mechanism; fill it in now
181e345f0f1Sthorpej 	 * so that pci_mode_detect() doesn't have to look for it.
182e345f0f1Sthorpej 	 */
18307a5af08Sdyoung 	pci_mode_set(mech1 ? 1 : 2);
184e345f0f1Sthorpej 
185e345f0f1Sthorpej 	pcibios_present = 1;
186e345f0f1Sthorpej 
187e345f0f1Sthorpej 	/*
188e345f0f1Sthorpej 	 * Find the PCI IRQ Routing table.
189e345f0f1Sthorpej 	 */
190e345f0f1Sthorpej 	pcibios_pir_init();
191e345f0f1Sthorpej 
1920919f4d2Ssekiya #ifdef PCI_INTR_FIXUP
193e345f0f1Sthorpej 	if (pcibios_pir_table != NULL) {
194e345f0f1Sthorpej 		int rv;
19568da4482Sperry 		uint16_t pciirq;
196e345f0f1Sthorpej 
197e345f0f1Sthorpej 		/*
198e345f0f1Sthorpej 		 * Fixup interrupt routing.
199e345f0f1Sthorpej 		 */
20030b2d68dSdyoung 		rv = pci_intr_fixup(NULL, x86_bus_space_io, &pciirq);
201e345f0f1Sthorpej 		switch (rv) {
202e345f0f1Sthorpej 		case -1:
203e345f0f1Sthorpej 			/* Non-fatal error. */
204a9beff95Sthorpej 			aprint_error("Warning: unable to fix up PCI interrupt "
205e345f0f1Sthorpej 			    "routing\n");
206e345f0f1Sthorpej 			break;
207e345f0f1Sthorpej 
208e345f0f1Sthorpej 		case 1:
209e345f0f1Sthorpej 			/* Fatal error. */
210e345f0f1Sthorpej 			panic("pcibios_init: interrupt fixup failed");
211e345f0f1Sthorpej 			break;
212e345f0f1Sthorpej 		}
213e345f0f1Sthorpej 
214e345f0f1Sthorpej 		/*
215e345f0f1Sthorpej 		 * XXX Clear `pciirq' from the ISA interrupt allocation
216e345f0f1Sthorpej 		 * XXX mask.
217e345f0f1Sthorpej 		 */
218e345f0f1Sthorpej 	}
219e345f0f1Sthorpej #endif
220e345f0f1Sthorpej }
221e345f0f1Sthorpej 
222e345f0f1Sthorpej void
pcibios_pir_init(void)22335adf403Sperry pcibios_pir_init(void)
224e345f0f1Sthorpej {
225b616cd6bSkochi 	char *devinfo;
226e345f0f1Sthorpej 	paddr_t pa;
22753524e44Schristos 	char *p;
228e345f0f1Sthorpej 	unsigned char cksum;
22968da4482Sperry 	uint16_t tablesize;
23068da4482Sperry 	uint8_t rev_maj, rev_min;
231e345f0f1Sthorpej 	int i;
232e345f0f1Sthorpej 
233e345f0f1Sthorpej 	for (pa = PCI_IRQ_TABLE_START; pa < PCI_IRQ_TABLE_END; pa += 16) {
23453524e44Schristos 		p = (void *)ISA_HOLE_VADDR(pa);
2357da411d6Schristos 		if (*(int *)p != BIOS32_MAKESIG('$', 'P', 'I', 'R')) {
2367da411d6Schristos 			/*
23727c7714fSdrochner 			 * XXX: Some laptops (Toshiba/Libretto L series)
2387da411d6Schristos 			 * use _PIR instead of $PIR. So we try that too.
2397da411d6Schristos 			 */
2407da411d6Schristos 			if (*(int *)p != BIOS32_MAKESIG('_', 'P', 'I', 'R'))
241e345f0f1Sthorpej 				continue;
2427da411d6Schristos 		}
243e345f0f1Sthorpej 
244e345f0f1Sthorpej 		rev_min = *(p + 4);
245e345f0f1Sthorpej 		rev_maj = *(p + 5);
24668da4482Sperry 		tablesize = *(uint16_t *)(p + 6);
247e345f0f1Sthorpej 
248e345f0f1Sthorpej 		cksum = 0;
249e345f0f1Sthorpej 		for (i = 0; i < tablesize; i++)
250e345f0f1Sthorpej 			cksum += *(unsigned char *)(p + i);
251e345f0f1Sthorpej 
252a9beff95Sthorpej 		aprint_normal(
253da702eb9Sjym 		    "PCI IRQ Routing Table rev. %d.%d found at %#" PRIxPADDR
254da702eb9Sjym 		    ", size %d bytes (%d entries)\n", rev_maj, rev_min, pa,
255e345f0f1Sthorpej 		    tablesize, (tablesize - 32) / 16);
256e345f0f1Sthorpej 
257e345f0f1Sthorpej 		if (cksum != 0) {
258a9beff95Sthorpej 			aprint_error("pcibios_pir_init: bad IRQ table checksum\n");
259e345f0f1Sthorpej 			continue;
260e345f0f1Sthorpej 		}
261e345f0f1Sthorpej 
262e345f0f1Sthorpej 		if (tablesize < 32 || (tablesize % 16) != 0) {
263a9beff95Sthorpej 			aprint_error("pcibios_pir_init: bad IRQ table size\n");
264e345f0f1Sthorpej 			continue;
265e345f0f1Sthorpej 		}
266e345f0f1Sthorpej 
267e345f0f1Sthorpej 		if (rev_maj != 1 || rev_min != 0) {
268a9beff95Sthorpej 			aprint_error("pcibios_pir_init: unsupported IRQ table "
269e345f0f1Sthorpej 			    "version\n");
270e345f0f1Sthorpej 			continue;
271e345f0f1Sthorpej 		}
272e345f0f1Sthorpej 
273e345f0f1Sthorpej 		/*
274e345f0f1Sthorpej 		 * We can handle this table!  Make a copy of it.
275e345f0f1Sthorpej 		 */
276e345f0f1Sthorpej 		memcpy(&pcibios_pir_header, p, 32);
277e345f0f1Sthorpej 		pcibios_pir_table = malloc(tablesize - 32, M_DEVBUF,
278*d47bcd29Schs 		    M_WAITOK);
279e345f0f1Sthorpej 		memcpy(pcibios_pir_table, p + 32, tablesize - 32);
280e345f0f1Sthorpej 		pcibios_pir_table_nentries = (tablesize - 32) / 16;
281e345f0f1Sthorpej 
282a9beff95Sthorpej 		aprint_verbose("PCI Interrupt Router at %03d:%02d:%01d",
283e345f0f1Sthorpej 		    pcibios_pir_header.router_bus,
284698b2b11Ssoda 		    PIR_DEVFUNC_DEVICE(pcibios_pir_header.router_devfunc),
285698b2b11Ssoda 		    PIR_DEVFUNC_FUNCTION(pcibios_pir_header.router_devfunc));
286e345f0f1Sthorpej 		if (pcibios_pir_header.compat_router != 0) {
287*d47bcd29Schs 			devinfo = malloc(256, M_DEVBUF, M_WAITOK);
288b616cd6bSkochi 			pci_devinfo(pcibios_pir_header.compat_router,
289b616cd6bSkochi 			    0, 0, devinfo, 256);
290a9beff95Sthorpej 			aprint_verbose(" (%s compatible)", devinfo);
291b616cd6bSkochi 			free(devinfo, M_DEVBUF);
292b616cd6bSkochi 		}
293a9beff95Sthorpej 		aprint_verbose("\n");
294e345f0f1Sthorpej 		pcibios_print_exclirq();
2954eac397fSchristos 
2964eac397fSchristos #ifdef PCIBIOS_LIBRETTO_FIXUP
2974eac397fSchristos 		/* for Libretto L2/L3 hack */
2984eac397fSchristos 		pcibios_fixup_pir_table();
2994eac397fSchristos #endif
300e93cdd69Saugustss #ifdef PCIBIOS_SHARP_MM20_FIXUP
301e93cdd69Saugustss 		pcibios_mm20_fixup();
302e93cdd69Saugustss #endif
303e345f0f1Sthorpej #ifdef PCIINTR_DEBUG
304e345f0f1Sthorpej 		pcibios_print_pir_table();
305e345f0f1Sthorpej #endif
306e345f0f1Sthorpej 		return;
307e345f0f1Sthorpej 	}
308e345f0f1Sthorpej 
309e345f0f1Sthorpej 	/*
310e345f0f1Sthorpej 	 * If there was no PIR table found, try using the PCI BIOS
311e345f0f1Sthorpej 	 * Get Interrupt Routing call.
312e345f0f1Sthorpej 	 *
313e345f0f1Sthorpej 	 * XXX The interface to this call sucks; just allocate enough
314e345f0f1Sthorpej 	 * XXX room for 32 entries.
315e345f0f1Sthorpej 	 */
316e345f0f1Sthorpej 	pcibios_pir_table_nentries = 32;
317e345f0f1Sthorpej 	pcibios_pir_table = malloc(pcibios_pir_table_nentries *
318*d47bcd29Schs 	    sizeof(*pcibios_pir_table), M_DEVBUF, M_WAITOK);
319e345f0f1Sthorpej 	if (pcibios_get_intr_routing(pcibios_pir_table,
320e345f0f1Sthorpej 	    &pcibios_pir_table_nentries,
321e345f0f1Sthorpej 	    &pcibios_pir_header.exclusive_irq) != PCIBIOS_SUCCESS) {
322a9beff95Sthorpej 		aprint_normal("No PCI IRQ Routing information available.\n");
323e345f0f1Sthorpej 		free(pcibios_pir_table, M_DEVBUF);
324e345f0f1Sthorpej 		pcibios_pir_table = NULL;
325e345f0f1Sthorpej 		pcibios_pir_table_nentries = 0;
326e345f0f1Sthorpej 		return;
327e345f0f1Sthorpej 	}
328a9beff95Sthorpej 	aprint_verbose("PCI BIOS has %d Interrupt Routing table entries\n",
329e345f0f1Sthorpej 	    pcibios_pir_table_nentries);
330e345f0f1Sthorpej 	pcibios_print_exclirq();
3314eac397fSchristos 
3324eac397fSchristos #ifdef PCIBIOS_LIBRETTO_FIXUP
3334eac397fSchristos 	/* for Libretto L2/L3 hack */
3344eac397fSchristos 	pcibios_fixup_pir_table();
3354eac397fSchristos #endif
336e93cdd69Saugustss #ifdef PCIBIOS_SHARP_MM20_FIXUP
337e93cdd69Saugustss 	pcibios_mm20_fixup();
338e93cdd69Saugustss #endif
339e345f0f1Sthorpej #ifdef PCIINTR_DEBUG
340e345f0f1Sthorpej 	pcibios_print_pir_table();
341e345f0f1Sthorpej #endif
342e345f0f1Sthorpej }
343e345f0f1Sthorpej 
344e345f0f1Sthorpej int
pcibios_get_status(uint32_t * rev_maj,uint32_t * rev_min,uint32_t * mech1,uint32_t * mech2,uint32_t * scmech1,uint32_t * scmech2,uint32_t * maxbus)34568da4482Sperry pcibios_get_status(uint32_t *rev_maj, uint32_t *rev_min,
34668da4482Sperry     uint32_t *mech1, uint32_t *mech2, uint32_t *scmech1, uint32_t *scmech2,
34768da4482Sperry     uint32_t *maxbus)
348e345f0f1Sthorpej {
34968da4482Sperry 	uint16_t ax, bx, cx;
35068da4482Sperry 	uint32_t edx;
351e345f0f1Sthorpej 	int rv;
352e345f0f1Sthorpej 
3535f1c88d7Sperry 	__asm volatile("lcall *(%%edi)				; \
354e345f0f1Sthorpej 			jc 1f						; \
355e345f0f1Sthorpej 			xor %%ah, %%ah					; \
356e345f0f1Sthorpej 		1:"
357e345f0f1Sthorpej 		: "=a" (ax), "=b" (bx), "=c" (cx), "=d" (edx)
358e345f0f1Sthorpej 		: "0" (0xb101), "D" (&pcibios_entry));
359e345f0f1Sthorpej 
360e345f0f1Sthorpej 	rv = pcibios_return_code(ax, "pcibios_get_status");
361e345f0f1Sthorpej 	if (rv != PCIBIOS_SUCCESS)
362e345f0f1Sthorpej 		return (rv);
363e345f0f1Sthorpej 
364e345f0f1Sthorpej 	if (edx != BIOS32_MAKESIG('P', 'C', 'I', ' '))
365e345f0f1Sthorpej 		return (PCIBIOS_SERVICE_NOT_PRESENT);	/* XXX */
366e345f0f1Sthorpej 
367e345f0f1Sthorpej 	/*
368e345f0f1Sthorpej 	 * Fill in the various pieces if info we're looking for.
369e345f0f1Sthorpej 	 */
370e345f0f1Sthorpej 	*mech1 = ax & 1;
371e345f0f1Sthorpej 	*mech2 = ax & (1 << 1);
372e345f0f1Sthorpej 	*scmech1 = ax & (1 << 4);
373e345f0f1Sthorpej 	*scmech2 = ax & (1 << 5);
374e345f0f1Sthorpej 	*rev_maj = (bx >> 8) & 0xff;
375e345f0f1Sthorpej 	*rev_min = bx & 0xff;
376e345f0f1Sthorpej 	*maxbus = cx & 0xff;
377e345f0f1Sthorpej 
378e345f0f1Sthorpej 	return (PCIBIOS_SUCCESS);
379e345f0f1Sthorpej }
380e345f0f1Sthorpej 
381e345f0f1Sthorpej int
pcibios_get_intr_routing(struct pcibios_intr_routing * table,int * nentries,uint16_t * exclirq)382e8085086Skochi pcibios_get_intr_routing(struct pcibios_intr_routing *table,
38368da4482Sperry     int *nentries, uint16_t *exclirq)
384e345f0f1Sthorpej {
38568da4482Sperry 	uint16_t ax, bx;
386e345f0f1Sthorpej 	int rv;
387e345f0f1Sthorpej 	struct {
38868da4482Sperry 		uint16_t size;
38953524e44Schristos 		void *offset;
39068da4482Sperry 		uint16_t segment;
391b6a2ef75Sperry 	} __packed args;
392e345f0f1Sthorpej 
393e345f0f1Sthorpej 	args.size = *nentries * sizeof(*table);
39453524e44Schristos 	args.offset = (void *)table;
395e345f0f1Sthorpej 	args.segment = GSEL(GDATA_SEL, SEL_KPL);
396e345f0f1Sthorpej 
397e345f0f1Sthorpej 	memset(table, 0, args.size);
398e345f0f1Sthorpej 
3995f1c88d7Sperry 	__asm volatile("lcall *(%%esi)				; \
400e345f0f1Sthorpej 			jc 1f						; \
401e345f0f1Sthorpej 			xor %%ah, %%ah					; \
402e345f0f1Sthorpej 		1:	movw %w2, %%ds					; \
403e345f0f1Sthorpej 			movw %w2, %%es"
404e345f0f1Sthorpej 		: "=a" (ax), "=b" (bx)
405e345f0f1Sthorpej 		: "r" GSEL(GDATA_SEL, SEL_KPL), "0" (0xb10e), "1" (0),
406e345f0f1Sthorpej 		  "D" (&args), "S" (&pcibios_entry));
407e345f0f1Sthorpej 
408e345f0f1Sthorpej 	rv = pcibios_return_code(ax, "pcibios_get_intr_routing");
409e345f0f1Sthorpej 	if (rv != PCIBIOS_SUCCESS)
410e345f0f1Sthorpej 		return (rv);
411e345f0f1Sthorpej 
412e345f0f1Sthorpej 	*nentries = args.size / sizeof(*table);
413e345f0f1Sthorpej 	*exclirq = bx;
414e345f0f1Sthorpej 
415e345f0f1Sthorpej 	return (PCIBIOS_SUCCESS);
416e345f0f1Sthorpej }
417e345f0f1Sthorpej 
418e345f0f1Sthorpej int
pcibios_return_code(uint16_t ax,const char * func)41968da4482Sperry pcibios_return_code(uint16_t ax, const char *func)
420e345f0f1Sthorpej {
421e345f0f1Sthorpej 	const char *errstr;
422e345f0f1Sthorpej 	int rv = ax >> 8;
423e345f0f1Sthorpej 
424e345f0f1Sthorpej 	switch (rv) {
425e345f0f1Sthorpej 	case PCIBIOS_SUCCESS:
426e345f0f1Sthorpej 		return (PCIBIOS_SUCCESS);
427e345f0f1Sthorpej 
428e345f0f1Sthorpej 	case PCIBIOS_SERVICE_NOT_PRESENT:
429e345f0f1Sthorpej 		errstr = "service not present";
430e345f0f1Sthorpej 		break;
431e345f0f1Sthorpej 
432e345f0f1Sthorpej 	case PCIBIOS_FUNCTION_NOT_SUPPORTED:
433e345f0f1Sthorpej 		errstr = "function not supported";
434e345f0f1Sthorpej 		break;
435e345f0f1Sthorpej 
436e345f0f1Sthorpej 	case PCIBIOS_BAD_VENDOR_ID:
437e345f0f1Sthorpej 		errstr = "bad vendor ID";
438e345f0f1Sthorpej 		break;
439e345f0f1Sthorpej 
440e345f0f1Sthorpej 	case PCIBIOS_DEVICE_NOT_FOUND:
441e345f0f1Sthorpej 		errstr = "device not found";
442e345f0f1Sthorpej 		break;
443e345f0f1Sthorpej 
444e345f0f1Sthorpej 	case PCIBIOS_BAD_REGISTER_NUMBER:
445e345f0f1Sthorpej 		errstr = "bad register number";
446e345f0f1Sthorpej 		break;
447e345f0f1Sthorpej 
448e345f0f1Sthorpej 	case PCIBIOS_SET_FAILED:
449e345f0f1Sthorpej 		errstr = "set failed";
450e345f0f1Sthorpej 		break;
451e345f0f1Sthorpej 
452e345f0f1Sthorpej 	case PCIBIOS_BUFFER_TOO_SMALL:
453e345f0f1Sthorpej 		errstr = "buffer too small";
454e345f0f1Sthorpej 		break;
455e345f0f1Sthorpej 
456e345f0f1Sthorpej 	default:
457a9beff95Sthorpej 		aprint_error("%s: unknown return code 0x%x\n", func, rv);
458e345f0f1Sthorpej 		return (rv);
459e345f0f1Sthorpej 	}
460e345f0f1Sthorpej 
461a9beff95Sthorpej 	aprint_error("%s: %s\n", func, errstr);
462e345f0f1Sthorpej 	return (rv);
463e345f0f1Sthorpej }
464e345f0f1Sthorpej 
465e345f0f1Sthorpej void
pcibios_print_exclirq(void)46635adf403Sperry pcibios_print_exclirq(void)
467e345f0f1Sthorpej {
468e345f0f1Sthorpej 	int i;
469e345f0f1Sthorpej 
470e345f0f1Sthorpej 	if (pcibios_pir_header.exclusive_irq) {
471a9beff95Sthorpej 		aprint_verbose("PCI Exclusive IRQs:");
472e345f0f1Sthorpej 		for (i = 0; i < 16; i++) {
473e345f0f1Sthorpej 			if (pcibios_pir_header.exclusive_irq & (1 << i))
474a9beff95Sthorpej 				aprint_verbose(" %d", i);
475e345f0f1Sthorpej 		}
476a9beff95Sthorpej 		aprint_verbose("\n");
477e345f0f1Sthorpej 	}
478e345f0f1Sthorpej }
479e345f0f1Sthorpej 
4804eac397fSchristos #ifdef PCIBIOS_LIBRETTO_FIXUP
4814eac397fSchristos /* for Libretto L2/L3 hack */
4824eac397fSchristos static void
pcibios_fixup_pir_table(void)48335adf403Sperry pcibios_fixup_pir_table(void)
4844eac397fSchristos {
4854eac397fSchristos 	struct pcibios_linkmap *m;
4864eac397fSchristos 
4874eac397fSchristos 	for (m = pir_mask; m->link != 0; m++)
4884eac397fSchristos 		pcibios_fixup_pir_table_mask(m);
4894eac397fSchristos }
4904eac397fSchristos 
4914eac397fSchristos void
pcibios_fixup_pir_table_mask(struct pcibios_linkmap * mask)49235adf403Sperry pcibios_fixup_pir_table_mask(struct pcibios_linkmap *mask)
4934eac397fSchristos {
4944eac397fSchristos 	int i, j;
4954eac397fSchristos 
4964eac397fSchristos 	for (i = 0; i < pcibios_pir_table_nentries; i++) {
4974eac397fSchristos 		for (j = 0; j < 4; j++) {
4984eac397fSchristos 			if (pcibios_pir_table[i].linkmap[j].link == mask->link) {
4994eac397fSchristos 				pcibios_pir_table[i].linkmap[j].bitmap
5004eac397fSchristos 				    &= mask->bitmap;
5014eac397fSchristos 			}
5024eac397fSchristos 		}
5034eac397fSchristos 	}
5044eac397fSchristos }
5054eac397fSchristos #endif
5064eac397fSchristos 
507e345f0f1Sthorpej #ifdef PCIINTR_DEBUG
508e345f0f1Sthorpej void
pcibios_print_pir_table(void)50935adf403Sperry pcibios_print_pir_table(void)
510e345f0f1Sthorpej {
511e345f0f1Sthorpej 	int i, j;
512e345f0f1Sthorpej 
513e345f0f1Sthorpej 	for (i = 0; i < pcibios_pir_table_nentries; i++) {
514e345f0f1Sthorpej 		printf("PIR Entry %d:\n", i);
515e345f0f1Sthorpej 		printf("\tBus: %d  Device: %d\n",
516e345f0f1Sthorpej 		    pcibios_pir_table[i].bus,
517698b2b11Ssoda 		    PIR_DEVFUNC_DEVICE(pcibios_pir_table[i].device));
518e345f0f1Sthorpej 		for (j = 0; j < 4; j++) {
519e345f0f1Sthorpej 			printf("\t\tINT%c: link 0x%02x bitmap 0x%04x\n",
520e345f0f1Sthorpej 			    'A' + j,
521e345f0f1Sthorpej 			    pcibios_pir_table[i].linkmap[j].link,
522e345f0f1Sthorpej 			    pcibios_pir_table[i].linkmap[j].bitmap);
523e345f0f1Sthorpej 		}
524e345f0f1Sthorpej 	}
525e345f0f1Sthorpej }
526e345f0f1Sthorpej #endif
527e3c38828Such 
528e93cdd69Saugustss #ifdef PCIBIOS_SHARP_MM20_FIXUP
529e93cdd69Saugustss /*
530e93cdd69Saugustss  * This is a gross hack to get the interrupt from the EHCI controller
531e93cdd69Saugustss  * working on a Sharp MM20.  The BIOS is just incredibly buggy.
532e93cdd69Saugustss  *
533e93cdd69Saugustss  * The story thus far:
534e93cdd69Saugustss  * The modern way to route the interrupt is to use ACPI.  But using
535e93cdd69Saugustss  * ACPI fails with an error message about an uninitialized local
536e93cdd69Saugustss  * variable in the AML code.  (It works in Windows, but fails in NetBSD
537e93cdd69Saugustss  * and Linux.)
538e93cdd69Saugustss  *
539e93cdd69Saugustss  * The second attempt is to use PCI Interrupt Routing table.  But this
540e93cdd69Saugustss  * fails because the table does not contain any information about the
541e93cdd69Saugustss  * interrupt from the EHCI controller.  This is probably due to the fact
542e93cdd69Saugustss  * that the table is compatible with ALi M1543, but the MM20 has an ALi M1563.
543e93cdd69Saugustss  * The M1563 has additional interrupt lines.  The ali1543.c code also
544e93cdd69Saugustss  * cannot handle the M1653's extended interrupts.  And fixing this is
545e93cdd69Saugustss  * difficult since getting a data sheet from ALi requires signing an NDA.
546e93cdd69Saugustss  *
547e93cdd69Saugustss  * The third attempt is to use a BIOS call to route the interrupt
548e93cdd69Saugustss  * (as FreeBSD does) with manually generated information.  But the BIOS call
549e93cdd69Saugustss  * fails because the BIOS code is not quite position independent.  It makes
550e93cdd69Saugustss  * some assumption about where the code segment register points.
551e93cdd69Saugustss  *
552e93cdd69Saugustss  * So the solution is to use the third attempt, but with a patched version
553e93cdd69Saugustss  * of the BIOS.
554e93cdd69Saugustss  *    -- lennart@augustsson.net
555e93cdd69Saugustss  */
556e93cdd69Saugustss 
557e93cdd69Saugustss #define	BIOS32_START	0xe0000
558e93cdd69Saugustss #define	BIOS32_SIZE	0x20000
559e93cdd69Saugustss 
560e93cdd69Saugustss static char pcibios_shadow[BIOS32_SIZE];
561e93cdd69Saugustss static struct bios32_entry pcibios_entry_shadow;
562e93cdd69Saugustss 
563e93cdd69Saugustss /*
564e93cdd69Saugustss  * Copy BIOS and zap offending instruction.
565e93cdd69Saugustss  * The bad instruction is
566e93cdd69Saugustss  *    mov    %cs:0x63c(%ebx),%ah
567e93cdd69Saugustss  * NetBSD does not have the code segment set up for this to work.
568e93cdd69Saugustss  * Using the value 0xff for the table entry seems to work.
569e93cdd69Saugustss  * The replacement is
570e93cdd69Saugustss  *    mov $0xff,%ah; nop; nop; nop; nop; nop
571e93cdd69Saugustss  */
572e93cdd69Saugustss static void
pcibios_copy_bios(void)573e93cdd69Saugustss pcibios_copy_bios(void)
574e93cdd69Saugustss {
57568da4482Sperry 	uint8_t *bad_instr;
576e93cdd69Saugustss 
577e93cdd69Saugustss 	memcpy(pcibios_shadow, ISA_HOLE_VADDR(BIOS32_START), BIOS32_SIZE);
578e93cdd69Saugustss 	pcibios_entry_shadow = pcibios_entry;
579e93cdd69Saugustss 	pcibios_entry_shadow.offset =
580e93cdd69Saugustss 	    (void*)((u_long)pcibios_shadow +
581e93cdd69Saugustss 		    (u_long)pcibios_entry.offset -
582e93cdd69Saugustss 		    (u_long)ISA_HOLE_VADDR(BIOS32_START));
583e93cdd69Saugustss 
58468da4482Sperry 	bad_instr = (uint8_t *)pcibios_entry_shadow.offset + 0x499;
585e93cdd69Saugustss 	if (*bad_instr != 0x2e)
586e93cdd69Saugustss 		panic("bad bios");
587e93cdd69Saugustss 	bad_instr[0] = 0xb4; bad_instr[1] = 0xff; /* mov $0xff,%ah */
588e93cdd69Saugustss 	bad_instr[2] = 0x90;		/* nop */
589e93cdd69Saugustss 	bad_instr[3] = 0x90;		/* nop */
590e93cdd69Saugustss 	bad_instr[4] = 0x90;		/* nop */
591e93cdd69Saugustss 	bad_instr[5] = 0x90;		/* nop */
592e93cdd69Saugustss 	bad_instr[6] = 0x90;		/* nop */
593e93cdd69Saugustss }
594e93cdd69Saugustss 
595e93cdd69Saugustss /*
596e93cdd69Saugustss  * Call BIOS to route an interrupt.
597e93cdd69Saugustss  * The PCI device is identified by bus,device,func.
598e93cdd69Saugustss  * The interrupt is on pin PIN (A-D) and interrupt IRQ.
599e93cdd69Saugustss  * BIOS knows the magic for the interrupt controller.
600e93cdd69Saugustss  */
601e93cdd69Saugustss static int
pcibios_biosroute(int bus,int device,int func,int pin,int irq)602e93cdd69Saugustss pcibios_biosroute(int bus, int device, int func, int pin, int irq)
603e93cdd69Saugustss {
60468da4482Sperry 	uint16_t ax, bx, cx;
605e93cdd69Saugustss 	int rv;
606e93cdd69Saugustss 
607a9beff95Sthorpej 	aprint_debug("pcibios_biosroute: b,d,f=%d,%d,%d pin=%x irq=%d\n",
608e93cdd69Saugustss 	       bus, device, func, pin+0xa, irq);
609e93cdd69Saugustss 
610e93cdd69Saugustss 	bx = (bus << 8) | (device << 3) | func;
611e93cdd69Saugustss 	cx = (irq << 8) | (0xa + pin);
612e93cdd69Saugustss 
6135f1c88d7Sperry 	__asm volatile("lcall *(%%esi)				; \
614e93cdd69Saugustss 			jc 1f						; \
615e93cdd69Saugustss 			xor %%ah, %%ah					; \
616e93cdd69Saugustss 		1:	movw %w1, %%ds					; \
617e93cdd69Saugustss 			movw %w1, %%es"
618e93cdd69Saugustss 			 : "=a" (ax)
619e93cdd69Saugustss 			 : "r" GSEL(GDATA_SEL, SEL_KPL), "0" (0xb10f),
620e93cdd69Saugustss 			   "b" (bx), "c" (cx),
621e93cdd69Saugustss 		           "S" (&pcibios_entry_shadow));
622e93cdd69Saugustss 
623e93cdd69Saugustss 	rv = pcibios_return_code(ax, "pcibios_biosroute");
624e93cdd69Saugustss 
625e93cdd69Saugustss 	return rv;
626e93cdd69Saugustss }
627e93cdd69Saugustss 
628a66509ebSaugustss #define MM20_PCI_BUS 0
629a66509ebSaugustss #define MM20_PCI_EHCI_DEV 15
630a66509ebSaugustss #define MM20_PCI_EHCI_FUNC 3
631a66509ebSaugustss #define MM20_PCI_EHCI_PIN 3
632a66509ebSaugustss #define MM20_PCI_EHCI_INTR 11
633a66509ebSaugustss #define MM20_PCI_ISA_DEV 3
634a66509ebSaugustss #define MM20_PCI_ISA_FUNC 0
635a66509ebSaugustss 
636e93cdd69Saugustss static void
pcibios_mm20_fixup(void)637e93cdd69Saugustss pcibios_mm20_fixup(void)
638e93cdd69Saugustss {
639a66509ebSaugustss 	pci_chipset_tag_t pc;
640a66509ebSaugustss 	pcitag_t tag;
641a66509ebSaugustss 
642e93cdd69Saugustss 	/* Copy BIOS */
643e93cdd69Saugustss 	pcibios_copy_bios();
644e93cdd69Saugustss 	/* Route the interrupt for the EHCI controller. */
645a66509ebSaugustss 	(void)pcibios_biosroute(MM20_PCI_BUS,
646a66509ebSaugustss 				MM20_PCI_EHCI_DEV,
647a66509ebSaugustss 				MM20_PCI_EHCI_FUNC,
648a66509ebSaugustss 				MM20_PCI_EHCI_PIN,
649a66509ebSaugustss 				MM20_PCI_EHCI_INTR);
650a66509ebSaugustss 
651a66509ebSaugustss 	/* Fake some tags. */
652a66509ebSaugustss 	pc = NULL;
653a66509ebSaugustss 	tag = pci_make_tag(pc, MM20_PCI_BUS, MM20_PCI_EHCI_DEV,
654a66509ebSaugustss 			   MM20_PCI_EHCI_FUNC);
655a66509ebSaugustss 	/* Set interrupt register in EHCI controller */
656f3a37fb5Sdyoung 	pci_conf_write(pc, tag, PCI_INTERRUPT_REG,
657f3a37fb5Sdyoung 	    0x50000400 + MM20_PCI_EHCI_INTR);
658a66509ebSaugustss 	tag = pci_make_tag(pc, MM20_PCI_BUS, MM20_PCI_ISA_DEV,
659a66509ebSaugustss 			   MM20_PCI_ISA_FUNC);
660a66509ebSaugustss 	/* Set some unknown registers in the ISA bridge. */
661a66509ebSaugustss 	pci_conf_write(pc, tag, 0x58, 0xd87f5300);
662a66509ebSaugustss 	pci_conf_write(pc, tag, 0x74, 0x00000009);
663e93cdd69Saugustss }
664e93cdd69Saugustss 
665e93cdd69Saugustss #endif /* PCIBIOS_SHARP_MM20_FIXUP */
666