1266125Sjhb/*-
2281887Sjhb * Copyright (c) 2014 Hudson River Trading LLC
3266125Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org>
4266125Sjhb * All rights reserved.
5266125Sjhb *
6266125Sjhb * Redistribution and use in source and binary forms, with or without
7266125Sjhb * modification, are permitted provided that the following conditions
8266125Sjhb * are met:
9266125Sjhb * 1. Redistributions of source code must retain the above copyright
10266125Sjhb *    notice, this list of conditions and the following disclaimer.
11266125Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12266125Sjhb *    notice, this list of conditions and the following disclaimer in the
13266125Sjhb *    documentation and/or other materials provided with the distribution.
14266125Sjhb *
15266125Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16266125Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17266125Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18266125Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19266125Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20266125Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21266125Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22266125Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23266125Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24266125Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25266125Sjhb * SUCH DAMAGE.
26266125Sjhb */
27266125Sjhb
28266125Sjhb
29266125Sjhb#include <sys/cdefs.h>
30266125Sjhb__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pci_irq.c 281887 2015-04-23 14:22:20Z jhb $");
31266125Sjhb
32266125Sjhb#include <sys/param.h>
33266125Sjhb#include <machine/vmm.h>
34266125Sjhb
35266125Sjhb#include <assert.h>
36266125Sjhb#include <pthread.h>
37266125Sjhb#include <stdbool.h>
38266125Sjhb#include <stdio.h>
39266125Sjhb#include <stdlib.h>
40266125Sjhb#include <vmmapi.h>
41266125Sjhb
42266125Sjhb#include "acpi.h"
43266125Sjhb#include "inout.h"
44266125Sjhb#include "pci_emul.h"
45266125Sjhb#include "pci_irq.h"
46266125Sjhb#include "pci_lpc.h"
47266125Sjhb
48266125Sjhb/*
49266125Sjhb * Implement an 8 pin PCI interrupt router compatible with the router
50266125Sjhb * present on Intel's ICH10 chip.
51266125Sjhb */
52266125Sjhb
53266125Sjhb/* Fields in each PIRQ register. */
54266125Sjhb#define	PIRQ_DIS	0x80
55266125Sjhb#define	PIRQ_IRQ	0x0f
56266125Sjhb
57266125Sjhb/* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
58266125Sjhb#define	PERMITTED_IRQS	0xdef8
59266125Sjhb#define	IRQ_PERMITTED(irq)	(((1U << (irq)) & PERMITTED_IRQS) != 0)
60266125Sjhb
61266125Sjhb/* IRQ count to disable an IRQ. */
62266125Sjhb#define	IRQ_DISABLED	0xff
63266125Sjhb
64266125Sjhbstatic struct pirq {
65266125Sjhb	uint8_t	reg;
66266125Sjhb	int	use_count;
67266125Sjhb	int	active_count;
68266125Sjhb	pthread_mutex_t lock;
69266125Sjhb} pirqs[8];
70266125Sjhb
71266125Sjhbstatic u_char irq_counts[16];
72266125Sjhbstatic int pirq_cold = 1;
73266125Sjhb
74266125Sjhb/*
75266125Sjhb * Returns true if this pin is enabled with a valid IRQ.  Setting the
76266125Sjhb * register to a reserved IRQ causes interrupts to not be asserted as
77266125Sjhb * if the pin was disabled.
78266125Sjhb */
79266125Sjhbstatic bool
80266125Sjhbpirq_valid_irq(int reg)
81266125Sjhb{
82266125Sjhb
83266125Sjhb	if (reg & PIRQ_DIS)
84266125Sjhb		return (false);
85266125Sjhb	return (IRQ_PERMITTED(reg & PIRQ_IRQ));
86266125Sjhb}
87266125Sjhb
88266125Sjhbuint8_t
89266125Sjhbpirq_read(int pin)
90266125Sjhb{
91266125Sjhb
92266125Sjhb	assert(pin > 0 && pin <= nitems(pirqs));
93266125Sjhb	return (pirqs[pin - 1].reg);
94266125Sjhb}
95266125Sjhb
96266125Sjhbvoid
97266125Sjhbpirq_write(struct vmctx *ctx, int pin, uint8_t val)
98266125Sjhb{
99266125Sjhb	struct pirq *pirq;
100266125Sjhb
101266125Sjhb	assert(pin > 0 && pin <= nitems(pirqs));
102266125Sjhb	pirq = &pirqs[pin - 1];
103266125Sjhb	pthread_mutex_lock(&pirq->lock);
104266125Sjhb	if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
105266125Sjhb		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
106266125Sjhb			vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
107266125Sjhb		pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
108266125Sjhb		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
109266125Sjhb			vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
110266125Sjhb	}
111266125Sjhb	pthread_mutex_unlock(&pirq->lock);
112266125Sjhb}
113266125Sjhb
114266125Sjhbvoid
115266125Sjhbpci_irq_reserve(int irq)
116266125Sjhb{
117266125Sjhb
118269896Sneel	assert(irq >= 0 && irq < nitems(irq_counts));
119266125Sjhb	assert(pirq_cold);
120266125Sjhb	assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
121266125Sjhb	irq_counts[irq] = IRQ_DISABLED;
122266125Sjhb}
123266125Sjhb
124266125Sjhbvoid
125266125Sjhbpci_irq_use(int irq)
126266125Sjhb{
127266125Sjhb
128269896Sneel	assert(irq >= 0 && irq < nitems(irq_counts));
129266125Sjhb	assert(pirq_cold);
130269896Sneel	assert(irq_counts[irq] != IRQ_DISABLED);
131269896Sneel	irq_counts[irq]++;
132266125Sjhb}
133266125Sjhb
134266125Sjhbvoid
135266125Sjhbpci_irq_init(struct vmctx *ctx)
136266125Sjhb{
137266125Sjhb	int i;
138266125Sjhb
139266125Sjhb	for (i = 0; i < nitems(pirqs); i++) {
140266125Sjhb		pirqs[i].reg = PIRQ_DIS;
141266125Sjhb		pirqs[i].use_count = 0;
142266125Sjhb		pirqs[i].active_count = 0;
143266125Sjhb		pthread_mutex_init(&pirqs[i].lock, NULL);
144266125Sjhb	}
145266125Sjhb	for (i = 0; i < nitems(irq_counts); i++) {
146266125Sjhb		if (IRQ_PERMITTED(i))
147266125Sjhb			irq_counts[i] = 0;
148266125Sjhb		else
149266125Sjhb			irq_counts[i] = IRQ_DISABLED;
150266125Sjhb	}
151266125Sjhb}
152266125Sjhb
153266125Sjhbvoid
154266125Sjhbpci_irq_assert(struct pci_devinst *pi)
155266125Sjhb{
156266125Sjhb	struct pirq *pirq;
157266125Sjhb
158266125Sjhb	if (pi->pi_lintr.pirq_pin > 0) {
159266125Sjhb		assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
160266125Sjhb		pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
161266125Sjhb		pthread_mutex_lock(&pirq->lock);
162266125Sjhb		pirq->active_count++;
163266125Sjhb		if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
164266125Sjhb			vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
165266125Sjhb			    pi->pi_lintr.ioapic_irq);
166266125Sjhb			pthread_mutex_unlock(&pirq->lock);
167266125Sjhb			return;
168266125Sjhb		}
169266125Sjhb		pthread_mutex_unlock(&pirq->lock);
170266125Sjhb	}
171266125Sjhb	vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
172266125Sjhb}
173266125Sjhb
174266125Sjhbvoid
175266125Sjhbpci_irq_deassert(struct pci_devinst *pi)
176266125Sjhb{
177266125Sjhb	struct pirq *pirq;
178266125Sjhb
179266125Sjhb	if (pi->pi_lintr.pirq_pin > 0) {
180266125Sjhb		assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
181266125Sjhb		pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
182266125Sjhb		pthread_mutex_lock(&pirq->lock);
183266125Sjhb		pirq->active_count--;
184266125Sjhb		if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
185266125Sjhb			vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
186266125Sjhb			    pi->pi_lintr.ioapic_irq);
187266125Sjhb			pthread_mutex_unlock(&pirq->lock);
188266125Sjhb			return;
189266125Sjhb		}
190266125Sjhb		pthread_mutex_unlock(&pirq->lock);
191266125Sjhb	}
192266125Sjhb	vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
193266125Sjhb}
194266125Sjhb
195266125Sjhbint
196266125Sjhbpirq_alloc_pin(struct vmctx *ctx)
197266125Sjhb{
198266125Sjhb	int best_count, best_irq, best_pin, irq, pin;
199266125Sjhb
200269896Sneel	pirq_cold = 0;
201266125Sjhb
202266125Sjhb	/* First, find the least-used PIRQ pin. */
203266125Sjhb	best_pin = 0;
204266125Sjhb	best_count = pirqs[0].use_count;
205266125Sjhb	for (pin = 1; pin < nitems(pirqs); pin++) {
206266125Sjhb		if (pirqs[pin].use_count < best_count) {
207266125Sjhb			best_pin = pin;
208266125Sjhb			best_count = pirqs[pin].use_count;
209266125Sjhb		}
210266125Sjhb	}
211266125Sjhb	pirqs[best_pin].use_count++;
212266125Sjhb
213266125Sjhb	/* Second, route this pin to an IRQ. */
214266125Sjhb	if (pirqs[best_pin].reg == PIRQ_DIS) {
215266125Sjhb		best_irq = -1;
216266125Sjhb		best_count = 0;
217266125Sjhb		for (irq = 0; irq < nitems(irq_counts); irq++) {
218266125Sjhb			if (irq_counts[irq] == IRQ_DISABLED)
219266125Sjhb				continue;
220266125Sjhb			if (best_irq == -1 || irq_counts[irq] < best_count) {
221266125Sjhb				best_irq = irq;
222266125Sjhb				best_count = irq_counts[irq];
223266125Sjhb			}
224266125Sjhb		}
225269896Sneel		assert(best_irq >= 0);
226266125Sjhb		irq_counts[best_irq]++;
227266125Sjhb		pirqs[best_pin].reg = best_irq;
228266125Sjhb		vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
229266125Sjhb	}
230266125Sjhb
231266125Sjhb	return (best_pin + 1);
232266125Sjhb}
233266125Sjhb
234266125Sjhbint
235266125Sjhbpirq_irq(int pin)
236266125Sjhb{
237266125Sjhb	assert(pin > 0 && pin <= nitems(pirqs));
238266125Sjhb	return (pirqs[pin - 1].reg & PIRQ_IRQ);
239266125Sjhb}
240266125Sjhb
241266125Sjhb/* XXX: Generate $PIR table. */
242266125Sjhb
243266125Sjhbstatic void
244266125Sjhbpirq_dsdt(void)
245266125Sjhb{
246266125Sjhb	char *irq_prs, *old;
247266125Sjhb	int irq, pin;
248266125Sjhb
249266125Sjhb	irq_prs = NULL;
250266125Sjhb	for (irq = 0; irq < nitems(irq_counts); irq++) {
251266125Sjhb		if (!IRQ_PERMITTED(irq))
252266125Sjhb			continue;
253266125Sjhb		if (irq_prs == NULL)
254266125Sjhb			asprintf(&irq_prs, "%d", irq);
255266125Sjhb		else {
256266125Sjhb			old = irq_prs;
257266125Sjhb			asprintf(&irq_prs, "%s,%d", old, irq);
258266125Sjhb			free(old);
259266125Sjhb		}
260266125Sjhb	}
261266125Sjhb
262266125Sjhb	/*
263266125Sjhb	 * A helper method to validate a link register's value.  This
264266125Sjhb	 * duplicates pirq_valid_irq().
265266125Sjhb	 */
266266125Sjhb	dsdt_line("");
267266125Sjhb	dsdt_line("Method (PIRV, 1, NotSerialized)");
268266125Sjhb	dsdt_line("{");
269266125Sjhb	dsdt_line("  If (And (Arg0, 0x%02X))", PIRQ_DIS);
270266125Sjhb	dsdt_line("  {");
271266125Sjhb	dsdt_line("    Return (0x00)");
272266125Sjhb	dsdt_line("  }");
273266125Sjhb	dsdt_line("  And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
274266125Sjhb	dsdt_line("  If (LLess (Local0, 0x03))");
275266125Sjhb	dsdt_line("  {");
276266125Sjhb	dsdt_line("    Return (0x00)");
277266125Sjhb	dsdt_line("  }");
278266125Sjhb	dsdt_line("  If (LEqual (Local0, 0x08))");
279266125Sjhb	dsdt_line("  {");
280266125Sjhb	dsdt_line("    Return (0x00)");
281266125Sjhb	dsdt_line("  }");
282266125Sjhb	dsdt_line("  If (LEqual (Local0, 0x0D))");
283266125Sjhb	dsdt_line("  {");
284266125Sjhb	dsdt_line("    Return (0x00)");
285266125Sjhb	dsdt_line("  }");
286266125Sjhb	dsdt_line("  Return (0x01)");
287266125Sjhb	dsdt_line("}");
288266125Sjhb
289266125Sjhb	for (pin = 0; pin < nitems(pirqs); pin++) {
290266125Sjhb		dsdt_line("");
291266125Sjhb		dsdt_line("Device (LNK%c)", 'A' + pin);
292266125Sjhb		dsdt_line("{");
293266125Sjhb		dsdt_line("  Name (_HID, EisaId (\"PNP0C0F\"))");
294266125Sjhb		dsdt_line("  Name (_UID, 0x%02X)", pin + 1);
295266125Sjhb		dsdt_line("  Method (_STA, 0, NotSerialized)");
296266125Sjhb		dsdt_line("  {");
297266125Sjhb		dsdt_line("    If (PIRV (PIR%c))", 'A' + pin);
298266125Sjhb		dsdt_line("    {");
299266125Sjhb		dsdt_line("       Return (0x0B)");
300266125Sjhb		dsdt_line("    }");
301266125Sjhb		dsdt_line("    Else");
302266125Sjhb		dsdt_line("    {");
303266125Sjhb		dsdt_line("       Return (0x09)");
304266125Sjhb		dsdt_line("    }");
305266125Sjhb		dsdt_line("  }");
306266125Sjhb		dsdt_line("  Name (_PRS, ResourceTemplate ()");
307266125Sjhb		dsdt_line("  {");
308266125Sjhb		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
309266125Sjhb		dsdt_line("      {%s}", irq_prs);
310266125Sjhb		dsdt_line("  })");
311266125Sjhb		dsdt_line("  Name (CB%02X, ResourceTemplate ()", pin + 1);
312266125Sjhb		dsdt_line("  {");
313266125Sjhb		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
314266125Sjhb		dsdt_line("      {}");
315266125Sjhb		dsdt_line("  })");
316266125Sjhb		dsdt_line("  CreateWordField (CB%02X, 0x01, CIR%c)",
317266125Sjhb		    pin + 1, 'A' + pin);
318266125Sjhb		dsdt_line("  Method (_CRS, 0, NotSerialized)");
319266125Sjhb		dsdt_line("  {");
320266125Sjhb		dsdt_line("    And (PIR%c, 0x%02X, Local0)", 'A' + pin,
321266125Sjhb		    PIRQ_DIS | PIRQ_IRQ);
322266125Sjhb		dsdt_line("    If (PIRV (Local0))");
323266125Sjhb		dsdt_line("    {");
324266125Sjhb		dsdt_line("      ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
325266125Sjhb		dsdt_line("    }");
326266125Sjhb		dsdt_line("    Else");
327266125Sjhb		dsdt_line("    {");
328266125Sjhb		dsdt_line("      Store (0x00, CIR%c)", 'A' + pin);
329266125Sjhb		dsdt_line("    }");
330266125Sjhb		dsdt_line("    Return (CB%02X)", pin + 1);
331266125Sjhb		dsdt_line("  }");
332266125Sjhb		dsdt_line("  Method (_DIS, 0, NotSerialized)");
333266125Sjhb		dsdt_line("  {");
334266125Sjhb		dsdt_line("    Store (0x80, PIR%c)", 'A' + pin);
335266125Sjhb		dsdt_line("  }");
336266125Sjhb		dsdt_line("  Method (_SRS, 1, NotSerialized)");
337266125Sjhb		dsdt_line("  {");
338266125Sjhb		dsdt_line("    CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
339266125Sjhb		dsdt_line("    FindSetRightBit (SIR%c, Local0)", 'A' + pin);
340266125Sjhb		dsdt_line("    Store (Decrement (Local0), PIR%c)", 'A' + pin);
341266125Sjhb		dsdt_line("  }");
342266125Sjhb		dsdt_line("}");
343266125Sjhb	}
344266125Sjhb	free(irq_prs);
345266125Sjhb}
346266125SjhbLPC_DSDT(pirq_dsdt);
347