2 * Copyright (c) 2014 Hudson River Trading LLC
3 * Written by: John H. Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <machine/vmm.h>
49 * Implement an 8 pin PCI interrupt router compatible with the router
50 * present on Intel's ICH10 chip.
53 /* Fields in each PIRQ register. */
57 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
58 #define PERMITTED_IRQS 0xdef8
59 #define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
61 /* IRQ count to disable an IRQ. */
62 #define IRQ_DISABLED 0xff
71 static u_char irq_counts
[16];
72 static int pirq_cold
= 1;
75 * Returns true if this pin is enabled with a valid IRQ. Setting the
76 * register to a reserved IRQ causes interrupts to not be asserted as
77 * if the pin was disabled.
80 pirq_valid_irq(int reg
)
85 return (IRQ_PERMITTED(reg
& PIRQ_IRQ
));
92 assert(pin
> 0 && pin
<= nitems(pirqs
));
93 return (pirqs
[pin
- 1].reg
);
97 pirq_write(struct vmctx
*ctx
, int pin
, uint8_t val
)
101 assert(pin
> 0 && pin
<= nitems(pirqs
));
102 pirq
= &pirqs
[pin
- 1];
103 pthread_mutex_lock(&pirq
->lock
);
104 if (pirq
->reg
!= (val
& (PIRQ_DIS
| PIRQ_IRQ
))) {
105 if (pirq
->active_count
!= 0 && pirq_valid_irq(pirq
->reg
))
106 vm_isa_deassert_irq(ctx
, pirq
->reg
& PIRQ_IRQ
, -1);
107 pirq
->reg
= val
& (PIRQ_DIS
| PIRQ_IRQ
);
108 if (pirq
->active_count
!= 0 && pirq_valid_irq(pirq
->reg
))
109 vm_isa_assert_irq(ctx
, pirq
->reg
& PIRQ_IRQ
, -1);
111 pthread_mutex_unlock(&pirq
->lock
);
115 pci_irq_reserve(int irq
)
118 assert(irq
>= 0 && irq
< nitems(irq_counts
));
120 assert(irq_counts
[irq
] == 0 || irq_counts
[irq
] == IRQ_DISABLED
);
121 irq_counts
[irq
] = IRQ_DISABLED
;
128 assert(irq
>= 0 && irq
< nitems(irq_counts
));
130 assert(irq_counts
[irq
] != IRQ_DISABLED
);
135 pci_irq_init(struct vmctx
*ctx
)
139 for (i
= 0; i
< nitems(pirqs
); i
++) {
140 pirqs
[i
].reg
= PIRQ_DIS
;
141 pirqs
[i
].use_count
= 0;
142 pirqs
[i
].active_count
= 0;
143 pthread_mutex_init(&pirqs
[i
].lock
, NULL
);
145 for (i
= 0; i
< nitems(irq_counts
); i
++) {
146 if (IRQ_PERMITTED(i
))
149 irq_counts
[i
] = IRQ_DISABLED
;
154 pci_irq_assert(struct pci_devinst
*pi
)
158 if (pi
->pi_lintr
.pirq_pin
> 0) {
159 assert(pi
->pi_lintr
.pirq_pin
<= nitems(pirqs
));
160 pirq
= &pirqs
[pi
->pi_lintr
.pirq_pin
- 1];
161 pthread_mutex_lock(&pirq
->lock
);
162 pirq
->active_count
++;
163 if (pirq
->active_count
== 1 && pirq_valid_irq(pirq
->reg
)) {
164 vm_isa_assert_irq(pi
->pi_vmctx
, pirq
->reg
& PIRQ_IRQ
,
165 pi
->pi_lintr
.ioapic_irq
);
166 pthread_mutex_unlock(&pirq
->lock
);
169 pthread_mutex_unlock(&pirq
->lock
);
171 vm_ioapic_assert_irq(pi
->pi_vmctx
, pi
->pi_lintr
.ioapic_irq
);
175 pci_irq_deassert(struct pci_devinst
*pi
)
179 if (pi
->pi_lintr
.pirq_pin
> 0) {
180 assert(pi
->pi_lintr
.pirq_pin
<= nitems(pirqs
));
181 pirq
= &pirqs
[pi
->pi_lintr
.pirq_pin
- 1];
182 pthread_mutex_lock(&pirq
->lock
);
183 pirq
->active_count
--;
184 if (pirq
->active_count
== 0 && pirq_valid_irq(pirq
->reg
)) {
185 vm_isa_deassert_irq(pi
->pi_vmctx
, pirq
->reg
& PIRQ_IRQ
,
186 pi
->pi_lintr
.ioapic_irq
);
187 pthread_mutex_unlock(&pirq
->lock
);
190 pthread_mutex_unlock(&pirq
->lock
);
192 vm_ioapic_deassert_irq(pi
->pi_vmctx
, pi
->pi_lintr
.ioapic_irq
);
196 pirq_alloc_pin(struct vmctx
*ctx
)
198 int best_count
, best_irq
, best_pin
, irq
, pin
;
202 /* First, find the least-used PIRQ pin. */
204 best_count
= pirqs
[0].use_count
;
205 for (pin
= 1; pin
< nitems(pirqs
); pin
++) {
206 if (pirqs
[pin
].use_count
< best_count
) {
208 best_count
= pirqs
[pin
].use_count
;
211 pirqs
[best_pin
].use_count
++;
213 /* Second, route this pin to an IRQ. */
214 if (pirqs
[best_pin
].reg
== PIRQ_DIS
) {
217 for (irq
= 0; irq
< nitems(irq_counts
); irq
++) {
218 if (irq_counts
[irq
] == IRQ_DISABLED
)
220 if (best_irq
== -1 || irq_counts
[irq
] < best_count
) {
222 best_count
= irq_counts
[irq
];
225 assert(best_irq
>= 0);
226 irq_counts
[best_irq
]++;
227 pirqs
[best_pin
].reg
= best_irq
;
228 vm_isa_set_irq_trigger(ctx
, best_irq
, LEVEL_TRIGGER
);
231 return (best_pin
+ 1);
237 assert(pin
> 0 && pin
<= nitems(pirqs
));
238 return (pirqs
[pin
- 1].reg
& PIRQ_IRQ
);
241 /* XXX: Generate $PIR table. */
250 for (irq
= 0; irq
< nitems(irq_counts
); irq
++) {
251 if (!IRQ_PERMITTED(irq
))
254 asprintf(&irq_prs
, "%d", irq
);
257 asprintf(&irq_prs
, "%s,%d", old
, irq
);
263 * A helper method to validate a link register's value. This
264 * duplicates pirq_valid_irq().
267 dsdt_line("Method (PIRV, 1, NotSerialized)");
269 dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS
);
271 dsdt_line(" Return (0x00)");
273 dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ
);
274 dsdt_line(" If (LLess (Local0, 0x03))");
276 dsdt_line(" Return (0x00)");
278 dsdt_line(" If (LEqual (Local0, 0x08))");
280 dsdt_line(" Return (0x00)");
282 dsdt_line(" If (LEqual (Local0, 0x0D))");
284 dsdt_line(" Return (0x00)");
286 dsdt_line(" Return (0x01)");
289 for (pin
= 0; pin
< nitems(pirqs
); pin
++) {
291 dsdt_line("Device (LNK%c)", 'A' + pin
);
293 dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
294 dsdt_line(" Name (_UID, 0x%02X)", pin
+ 1);
295 dsdt_line(" Method (_STA, 0, NotSerialized)");
297 dsdt_line(" If (PIRV (PIR%c))", 'A' + pin
);
299 dsdt_line(" Return (0x0B)");
303 dsdt_line(" Return (0x09)");
306 dsdt_line(" Name (_PRS, ResourceTemplate ()");
308 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
309 dsdt_line(" {%s}", irq_prs
);
311 dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin
+ 1);
313 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
316 dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
318 dsdt_line(" Method (_CRS, 0, NotSerialized)");
320 dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin
,
321 PIRQ_DIS
| PIRQ_IRQ
);
322 dsdt_line(" If (PIRV (Local0))");
324 dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin
);
328 dsdt_line(" Store (0x00, CIR%c)", 'A' + pin
);
330 dsdt_line(" Return (CB%02X)", pin
+ 1);
332 dsdt_line(" Method (_DIS, 0, NotSerialized)");
334 dsdt_line(" Store (0x80, PIR%c)", 'A' + pin
);
336 dsdt_line(" Method (_SRS, 1, NotSerialized)");
338 dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin
);
339 dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin
);
340 dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin
);