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