1*078f53c0Sderaadt /* $OpenBSD: mpbios_intr_fixup.c,v 1.7 2015/08/10 12:12:58 deraadt Exp $ */
2d85d1c6bSkettenis
3d85d1c6bSkettenis /*
4d85d1c6bSkettenis * Copyright (c) 2006 Mark Kettenis
5d85d1c6bSkettenis *
6d85d1c6bSkettenis * Permission to use, copy, modify, and distribute this software for any
7d85d1c6bSkettenis * purpose with or without fee is hereby granted, provided that the above
8d85d1c6bSkettenis * copyright notice and this permission notice appear in all copies.
9d85d1c6bSkettenis *
10d85d1c6bSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d85d1c6bSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d85d1c6bSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d85d1c6bSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d85d1c6bSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d85d1c6bSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d85d1c6bSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d85d1c6bSkettenis */
18d85d1c6bSkettenis
19d85d1c6bSkettenis #include <sys/param.h>
20d85d1c6bSkettenis #include <sys/systm.h>
21d85d1c6bSkettenis
22d85d1c6bSkettenis #include <dev/pci/pcireg.h>
23d85d1c6bSkettenis #include <dev/pci/pcivar.h>
24d85d1c6bSkettenis #include <dev/pci/pcidevs.h>
25d85d1c6bSkettenis
26d85d1c6bSkettenis #include <machine/i82093var.h>
27d85d1c6bSkettenis #include <machine/mpbiosvar.h>
28d85d1c6bSkettenis
29d85d1c6bSkettenis void mpbios_pin_fixup(int, int, int, int);
30d85d1c6bSkettenis const struct mpbios_icu_table *mpbios_icu_lookup(pcireg_t);
31d85d1c6bSkettenis
32d85d1c6bSkettenis void via8237_mpbios_fixup(pci_chipset_tag_t, pcitag_t);
33d85d1c6bSkettenis void nforce4_mpbios_fixup(pci_chipset_tag_t, pcitag_t);
34f0a0739fSkettenis void mcp04_mpbios_fixup(pci_chipset_tag_t, pcitag_t);
35d85d1c6bSkettenis
36d85d1c6bSkettenis const struct mpbios_icu_table {
37d85d1c6bSkettenis pci_vendor_id_t mpit_vendor;
38d85d1c6bSkettenis pci_product_id_t mpit_product;
39d85d1c6bSkettenis void (*mpit_mpbios_fixup)(pci_chipset_tag_t, pcitag_t);
40d85d1c6bSkettenis } mpbios_icu_table[] = {
41d85d1c6bSkettenis { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237_ISA,
42d85d1c6bSkettenis via8237_mpbios_fixup },
430e67baf3Snaddy { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_ISA1,
44512e0f57Skettenis nforce4_mpbios_fixup },
45512e0f57Skettenis { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_ISA2,
46f0a0739fSkettenis nforce4_mpbios_fixup },
471d66aa38Sderaadt { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_ISA,
48*078f53c0Sderaadt mcp04_mpbios_fixup },
49*078f53c0Sderaadt { 0, 0,
50*078f53c0Sderaadt NULL },
51d85d1c6bSkettenis };
52d85d1c6bSkettenis
53d85d1c6bSkettenis const struct mpbios_icu_table *
mpbios_icu_lookup(pcireg_t id)54d85d1c6bSkettenis mpbios_icu_lookup(pcireg_t id)
55d85d1c6bSkettenis {
56d85d1c6bSkettenis const struct mpbios_icu_table *mpit;
57d85d1c6bSkettenis
58d85d1c6bSkettenis for (mpit = mpbios_icu_table; mpit->mpit_mpbios_fixup != NULL; mpit++)
59d85d1c6bSkettenis if (PCI_VENDOR(id) == mpit->mpit_vendor &&
60d85d1c6bSkettenis PCI_PRODUCT(id) == mpit->mpit_product)
61d85d1c6bSkettenis return (mpit);
62d85d1c6bSkettenis
63d85d1c6bSkettenis return (NULL);
64d85d1c6bSkettenis }
65d85d1c6bSkettenis
66d85d1c6bSkettenis /*
67d85d1c6bSkettenis * NVIDIA nForce4 PCI-ISA bridge.
68d85d1c6bSkettenis */
69d85d1c6bSkettenis
70d85d1c6bSkettenis #define NFORCE4_PNPIRQ1 0x7c
71d85d1c6bSkettenis #define NFORCE4_PNPIRQ2 0x80
72314d1ed2Skettenis #define NFORCE4_USB2_SHIFT 12
73314d1ed2Skettenis #define NFORCE4_USB2_MASK (0xf << NFORCE4_USB2_SHIFT)
74d85d1c6bSkettenis #define NFORCE4_SATA1_SHIFT 28
75d85d1c6bSkettenis #define NFORCE4_SATA1_MASK (0xf << NFORCE4_SATA1_SHIFT)
76d85d1c6bSkettenis #define NFORCE4_SATA2_SHIFT 24
77d85d1c6bSkettenis #define NFORCE4_SATA2_MASK (0xf << NFORCE4_SATA2_SHIFT)
78d85d1c6bSkettenis #define NFORCE4_PNPIRQ3 0x84
79314d1ed2Skettenis #define NFORCE4_USB1_SHIFT 0
80314d1ed2Skettenis #define NFORCE4_USB1_MASK (0xf << NFORCE4_USB1_SHIFT)
81314d1ed2Skettenis #define NFORCE4_LAN_SHIFT 8
82314d1ed2Skettenis #define NFORCE4_LAN_MASK (0xf << NFORCE4_LAN_SHIFT)
83d85d1c6bSkettenis
84d85d1c6bSkettenis void
nforce4_mpbios_fixup(pci_chipset_tag_t pc,pcitag_t tag)85d85d1c6bSkettenis nforce4_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
86d85d1c6bSkettenis {
87d85d1c6bSkettenis pcireg_t reg;
88d85d1c6bSkettenis int bus, pin;
89d85d1c6bSkettenis
90d85d1c6bSkettenis pci_decompose_tag (pc, tag, &bus, NULL, NULL);
91d85d1c6bSkettenis
92d85d1c6bSkettenis reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ2);
93314d1ed2Skettenis pin = (reg & NFORCE4_USB2_MASK) >> NFORCE4_USB2_SHIFT;
94314d1ed2Skettenis if (pin != 0)
95314d1ed2Skettenis mpbios_pin_fixup(bus, 2, PCI_INTERRUPT_PIN_B, pin);
96d85d1c6bSkettenis pin = (reg & NFORCE4_SATA1_MASK) >> NFORCE4_SATA1_SHIFT;
97d85d1c6bSkettenis if (pin != 0)
98314d1ed2Skettenis mpbios_pin_fixup(bus, 7, PCI_INTERRUPT_PIN_A, pin);
99d85d1c6bSkettenis pin = (reg & NFORCE4_SATA2_MASK) >> NFORCE4_SATA2_SHIFT;
100d85d1c6bSkettenis if (pin != 0)
101314d1ed2Skettenis mpbios_pin_fixup(bus, 8, PCI_INTERRUPT_PIN_A, pin);
102314d1ed2Skettenis
103314d1ed2Skettenis reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ3);
104314d1ed2Skettenis pin = (reg & NFORCE4_USB1_MASK) >> NFORCE4_USB1_SHIFT;
105314d1ed2Skettenis if (pin != 0)
106314d1ed2Skettenis mpbios_pin_fixup(bus, 2, PCI_INTERRUPT_PIN_A, pin);
107314d1ed2Skettenis pin = (reg & NFORCE4_LAN_MASK) >> NFORCE4_LAN_SHIFT;
108314d1ed2Skettenis if (pin != 0)
109314d1ed2Skettenis mpbios_pin_fixup(bus, 10, PCI_INTERRUPT_PIN_A, pin);
110d85d1c6bSkettenis }
111d85d1c6bSkettenis
112d85d1c6bSkettenis /*
113f0a0739fSkettenis * NVIDIA MCP04 PCI-ISA bridge.
114f0a0739fSkettenis */
115f0a0739fSkettenis
116f0a0739fSkettenis void
mcp04_mpbios_fixup(pci_chipset_tag_t pc,pcitag_t tag)117f0a0739fSkettenis mcp04_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
118f0a0739fSkettenis {
119f0a0739fSkettenis pcireg_t reg;
120f0a0739fSkettenis int bus, pin;
121f0a0739fSkettenis
122f0a0739fSkettenis pci_decompose_tag (pc, tag, &bus, NULL, NULL);
123f0a0739fSkettenis
124f0a0739fSkettenis reg = pci_conf_read(pc, tag, NFORCE4_PNPIRQ2);
125f0a0739fSkettenis pin = (reg & NFORCE4_SATA1_MASK) >> NFORCE4_SATA1_SHIFT;
126f0a0739fSkettenis if (pin != 0)
127f0a0739fSkettenis mpbios_pin_fixup(bus, 16, PCI_INTERRUPT_PIN_A, pin);
128f0a0739fSkettenis pin = (reg & NFORCE4_SATA2_MASK) >> NFORCE4_SATA2_SHIFT;
129f0a0739fSkettenis if (pin != 0)
130f0a0739fSkettenis mpbios_pin_fixup(bus, 17, PCI_INTERRUPT_PIN_A, pin);
131f0a0739fSkettenis }
132f0a0739fSkettenis
133f0a0739fSkettenis /*
134d85d1c6bSkettenis * VIA VT8237 PCI-ISA bridge.
135d85d1c6bSkettenis */
136d85d1c6bSkettenis
137d85d1c6bSkettenis void
via8237_mpbios_fixup(pci_chipset_tag_t pc,pcitag_t tag)138d85d1c6bSkettenis via8237_mpbios_fixup(pci_chipset_tag_t pc, pcitag_t tag)
139d85d1c6bSkettenis {
140d85d1c6bSkettenis int bus;
141d85d1c6bSkettenis
142d85d1c6bSkettenis pci_decompose_tag (pc, tag, &bus, NULL, NULL);
143d85d1c6bSkettenis
144d85d1c6bSkettenis /* SATA is hardwired to APIC pin 20. */
145d85d1c6bSkettenis mpbios_pin_fixup(bus, 15, 2, 20);
146d85d1c6bSkettenis }
147d85d1c6bSkettenis
148d85d1c6bSkettenis void
mpbios_pin_fixup(int bus,int dev,int rawpin,int pin)149d85d1c6bSkettenis mpbios_pin_fixup(int bus, int dev, int rawpin, int pin)
150d85d1c6bSkettenis {
151d85d1c6bSkettenis struct mp_bus *mpb = &mp_busses[bus];
152d85d1c6bSkettenis struct mp_intr_map *mip;
153d85d1c6bSkettenis
154d85d1c6bSkettenis for (mip = mpb->mb_intrs; mip != NULL; mip = mip->next) {
155d85d1c6bSkettenis if (mip->bus_pin == ((dev << 2) | (rawpin - 1)) &&
156d85d1c6bSkettenis mip->ioapic_pin != pin) {
157d85d1c6bSkettenis
158d85d1c6bSkettenis if (mp_verbose) {
159d85d1c6bSkettenis
160d85d1c6bSkettenis printf("%s: int%d attached to %s",
161d85d1c6bSkettenis mip->ioapic->sc_pic.pic_dev.dv_xname,
162d85d1c6bSkettenis pin, mpb->mb_name);
163d85d1c6bSkettenis
164d85d1c6bSkettenis if (mpb->mb_idx != -1)
165d85d1c6bSkettenis printf("%d", mpb->mb_idx);
166d85d1c6bSkettenis
167d85d1c6bSkettenis (*(mpb->mb_intr_print))(mip->bus_pin);
168d85d1c6bSkettenis
169d85d1c6bSkettenis printf(" (fixup)\n");
170d85d1c6bSkettenis }
171d85d1c6bSkettenis
172d85d1c6bSkettenis mip->ioapic_pin = pin;
173d85d1c6bSkettenis mip->ioapic_ih &= ~APIC_INT_PIN_MASK;
174d85d1c6bSkettenis mip->ioapic_ih |= (pin << APIC_INT_PIN_SHIFT);
175d85d1c6bSkettenis if (mip->ioapic->sc_pins[pin].ip_map == NULL)
176d85d1c6bSkettenis mip->ioapic->sc_pins[pin].ip_map = mip;
177d85d1c6bSkettenis }
178d85d1c6bSkettenis }
179d85d1c6bSkettenis }
180d85d1c6bSkettenis
181d85d1c6bSkettenis void
mpbios_intr_fixup(void)182d85d1c6bSkettenis mpbios_intr_fixup(void)
183d85d1c6bSkettenis {
184d85d1c6bSkettenis const struct mpbios_icu_table *mpit = NULL;
185d85d1c6bSkettenis pci_chipset_tag_t pc = NULL;
186d85d1c6bSkettenis pcitag_t icutag;
187d85d1c6bSkettenis int device, maxdevs = pci_bus_maxdevs(pc, 0);
188d85d1c6bSkettenis
189d85d1c6bSkettenis /* Search configuration space for a known interrupt router. */
190d85d1c6bSkettenis for (device = 0; device < maxdevs; device++) {
191d85d1c6bSkettenis const struct pci_quirkdata *qd;
192d85d1c6bSkettenis int function, nfuncs;
193d85d1c6bSkettenis pcireg_t icuid;
194d85d1c6bSkettenis pcireg_t bhlcr;
195d85d1c6bSkettenis
196d85d1c6bSkettenis icutag = pci_make_tag(pc, 0, device, 0);
197d85d1c6bSkettenis icuid = pci_conf_read(pc, icutag, PCI_ID_REG);
198d85d1c6bSkettenis
199d85d1c6bSkettenis /* Invalid vendor ID value? */
200d85d1c6bSkettenis if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
201d85d1c6bSkettenis continue;
202d85d1c6bSkettenis
203d85d1c6bSkettenis qd = pci_lookup_quirkdata(PCI_VENDOR(icuid),
204d85d1c6bSkettenis PCI_PRODUCT(icuid));
205d85d1c6bSkettenis
206d85d1c6bSkettenis bhlcr = pci_conf_read(pc, icutag, PCI_BHLC_REG);
207d85d1c6bSkettenis if (PCI_HDRTYPE_MULTIFN(bhlcr) || (qd != NULL &&
208d85d1c6bSkettenis (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
209d85d1c6bSkettenis nfuncs = 8;
210d85d1c6bSkettenis else
211d85d1c6bSkettenis nfuncs = 1;
212d85d1c6bSkettenis
213d85d1c6bSkettenis for (function = 0; function < nfuncs; function++) {
214d85d1c6bSkettenis icutag = pci_make_tag(pc, 0, device, function);
215d85d1c6bSkettenis icuid = pci_conf_read(pc, icutag, PCI_ID_REG);
216d85d1c6bSkettenis
217d85d1c6bSkettenis /* Invalid vendor ID value? */
218d85d1c6bSkettenis if (PCI_VENDOR(icuid) == PCI_VENDOR_INVALID)
219d85d1c6bSkettenis continue;
220d85d1c6bSkettenis
221d85d1c6bSkettenis if ((mpit = mpbios_icu_lookup(icuid)))
222d85d1c6bSkettenis break;
223d85d1c6bSkettenis }
224d85d1c6bSkettenis
225d85d1c6bSkettenis if (mpit != NULL)
226d85d1c6bSkettenis break;
227d85d1c6bSkettenis }
228d85d1c6bSkettenis
229d85d1c6bSkettenis if (mpit)
230d85d1c6bSkettenis mpit->mpit_mpbios_fixup(pc, icutag);
231d85d1c6bSkettenis }
232