2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 Hudson River Trading LLC
5 * Written by: John H. Baldwin <jhb@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following 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.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <machine/vmm.h>
51 * Implement an 8 pin PCI interrupt router compatible with the router
52 * present on Intel's ICH10 chip.
55 /* Fields in each PIRQ register. */
59 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
60 #define PERMITTED_IRQS 0xdef8
61 #define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
63 /* IRQ count to disable an IRQ. */
64 #define IRQ_DISABLED 0xff
74 #define NIRQ_COUNTS 16
75 static u_char irq_counts
[NIRQ_COUNTS
];
76 static int pirq_cold
= 1;
79 * Returns true if this pin is enabled with a valid IRQ. Setting the
80 * register to a reserved IRQ causes interrupts to not be asserted as
81 * if the pin was disabled.
84 pirq_valid_irq(int reg
)
89 return (IRQ_PERMITTED(reg
& PIRQ_IRQ
));
96 assert(pin
> 0 && pin
<= NPIRQS
);
97 return (pirqs
[pin
- 1].reg
);
101 pirq_write(struct vmctx
*ctx
, int pin
, uint8_t val
)
105 assert(pin
> 0 && pin
<= NPIRQS
);
106 pirq
= &pirqs
[pin
- 1];
107 pthread_mutex_lock(&pirq
->lock
);
108 if (pirq
->reg
!= (val
& (PIRQ_DIS
| PIRQ_IRQ
))) {
109 if (pirq
->active_count
!= 0 && pirq_valid_irq(pirq
->reg
))
110 vm_isa_deassert_irq(ctx
, pirq
->reg
& PIRQ_IRQ
, -1);
111 pirq
->reg
= val
& (PIRQ_DIS
| PIRQ_IRQ
);
112 if (pirq
->active_count
!= 0 && pirq_valid_irq(pirq
->reg
))
113 vm_isa_assert_irq(ctx
, pirq
->reg
& PIRQ_IRQ
, -1);
115 pthread_mutex_unlock(&pirq
->lock
);
119 pci_irq_reserve(int irq
)
122 assert(irq
>= 0 && irq
< NIRQ_COUNTS
);
124 assert(irq_counts
[irq
] == 0 || irq_counts
[irq
] == IRQ_DISABLED
);
125 irq_counts
[irq
] = IRQ_DISABLED
;
132 assert(irq
>= 0 && irq
< NIRQ_COUNTS
);
134 assert(irq_counts
[irq
] != IRQ_DISABLED
);
139 pci_irq_init(struct vmctx
*ctx __unused
)
143 for (i
= 0; i
< NPIRQS
; i
++) {
144 pirqs
[i
].reg
= PIRQ_DIS
;
145 pirqs
[i
].use_count
= 0;
146 pirqs
[i
].active_count
= 0;
147 pthread_mutex_init(&pirqs
[i
].lock
, NULL
);
149 for (i
= 0; i
< NIRQ_COUNTS
; i
++) {
150 if (IRQ_PERMITTED(i
))
153 irq_counts
[i
] = IRQ_DISABLED
;
158 pci_irq_assert(struct pci_devinst
*pi
)
163 pin
= pi
->pi_lintr
.pirq_pin
;
165 assert(pin
<= NPIRQS
);
166 pirq
= &pirqs
[pin
- 1];
167 pthread_mutex_lock(&pirq
->lock
);
168 pirq
->active_count
++;
169 if (pirq
->active_count
== 1 && pirq_valid_irq(pirq
->reg
)) {
170 vm_isa_assert_irq(pi
->pi_vmctx
, pirq
->reg
& PIRQ_IRQ
,
171 pi
->pi_lintr
.ioapic_irq
);
172 pthread_mutex_unlock(&pirq
->lock
);
175 pthread_mutex_unlock(&pirq
->lock
);
177 vm_ioapic_assert_irq(pi
->pi_vmctx
, pi
->pi_lintr
.ioapic_irq
);
181 pci_irq_deassert(struct pci_devinst
*pi
)
186 pin
= pi
->pi_lintr
.pirq_pin
;
188 assert(pin
<= NPIRQS
);
189 pirq
= &pirqs
[pin
- 1];
190 pthread_mutex_lock(&pirq
->lock
);
191 pirq
->active_count
--;
192 if (pirq
->active_count
== 0 && pirq_valid_irq(pirq
->reg
)) {
193 vm_isa_deassert_irq(pi
->pi_vmctx
, pirq
->reg
& PIRQ_IRQ
,
194 pi
->pi_lintr
.ioapic_irq
);
195 pthread_mutex_unlock(&pirq
->lock
);
198 pthread_mutex_unlock(&pirq
->lock
);
200 vm_ioapic_deassert_irq(pi
->pi_vmctx
, pi
->pi_lintr
.ioapic_irq
);
204 pirq_alloc_pin(struct pci_devinst
*pi
)
206 struct vmctx
*ctx
= pi
->pi_vmctx
;
207 int best_count
, best_irq
, best_pin
, irq
, pin
;
212 /* For external bootrom use fixed mapping. */
213 best_pin
= (4 + pi
->pi_slot
+ pi
->pi_lintr
.pin
) % 8;
215 /* Find the least-used PIRQ pin. */
217 best_count
= pirqs
[0].use_count
;
218 for (pin
= 1; pin
< NPIRQS
; pin
++) {
219 if (pirqs
[pin
].use_count
< best_count
) {
221 best_count
= pirqs
[pin
].use_count
;
225 pirqs
[best_pin
].use_count
++;
227 /* Second, route this pin to an IRQ. */
228 if (pirqs
[best_pin
].reg
== PIRQ_DIS
) {
231 for (irq
= 0; irq
< NIRQ_COUNTS
; irq
++) {
232 if (irq_counts
[irq
] == IRQ_DISABLED
)
234 if (best_irq
== -1 || irq_counts
[irq
] < best_count
) {
236 best_count
= irq_counts
[irq
];
239 assert(best_irq
>= 0);
240 irq_counts
[best_irq
]++;
241 pirqs
[best_pin
].reg
= best_irq
;
242 vm_isa_set_irq_trigger(ctx
, best_irq
, LEVEL_TRIGGER
);
245 return (best_pin
+ 1);
251 assert(pin
> 0 && pin
<= NPIRQS
);
252 return (pirqs
[pin
- 1].reg
& PIRQ_IRQ
);
255 /* XXX: Generate $PIR table. */
264 for (irq
= 0; irq
< NIRQ_COUNTS
; irq
++) {
265 if (!IRQ_PERMITTED(irq
))
268 asprintf(&irq_prs
, "%d", irq
);
271 asprintf(&irq_prs
, "%s,%d", old
, irq
);
277 * A helper method to validate a link register's value. This
278 * duplicates pirq_valid_irq().
281 dsdt_line("Method (PIRV, 1, NotSerialized)");
283 dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS
);
285 dsdt_line(" Return (0x00)");
287 dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ
);
288 dsdt_line(" If (LLess (Local0, 0x03))");
290 dsdt_line(" Return (0x00)");
292 dsdt_line(" If (LEqual (Local0, 0x08))");
294 dsdt_line(" Return (0x00)");
296 dsdt_line(" If (LEqual (Local0, 0x0D))");
298 dsdt_line(" Return (0x00)");
300 dsdt_line(" Return (0x01)");
303 for (pin
= 0; pin
< NPIRQS
; pin
++) {
305 dsdt_line("Device (LNK%c)", 'A' + pin
);
307 dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
308 dsdt_line(" Name (_UID, 0x%02X)", pin
+ 1);
309 dsdt_line(" Method (_STA, 0, NotSerialized)");
311 dsdt_line(" If (PIRV (PIR%c))", 'A' + pin
);
313 dsdt_line(" Return (0x0B)");
317 dsdt_line(" Return (0x09)");
320 dsdt_line(" Name (_PRS, ResourceTemplate ()");
322 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
323 dsdt_line(" {%s}", irq_prs
);
325 dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin
+ 1);
327 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
330 dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
332 dsdt_line(" Method (_CRS, 0, NotSerialized)");
334 dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin
,
335 PIRQ_DIS
| PIRQ_IRQ
);
336 dsdt_line(" If (PIRV (Local0))");
338 dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin
);
342 dsdt_line(" Store (0x00, CIR%c)", 'A' + pin
);
344 dsdt_line(" Return (CB%02X)", pin
+ 1);
346 dsdt_line(" Method (_DIS, 0, NotSerialized)");
348 dsdt_line(" Store (0x80, PIR%c)", 'A' + pin
);
350 dsdt_line(" Method (_SRS, 1, NotSerialized)");
352 dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin
);
353 dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin
);
354 dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin
);