pci_irq.c revision 270159
1139823Simp/*-
210965Swollman * Copyright (c) 2014 Advanced Computing Technologies LLC
31541Srgrimes * Written by: John H. Baldwin <jhb@FreeBSD.org>
41541Srgrimes * All rights reserved.
51541Srgrimes *
61541Srgrimes * Redistribution and use in source and binary forms, with or without
71541Srgrimes * modification, are permitted provided that the following conditions
81541Srgrimes * are met:
91541Srgrimes * 1. Redistributions of source code must retain the above copyright
101541Srgrimes *    notice, this list of conditions and the following disclaimer.
111541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer in the
131541Srgrimes *    documentation and/or other materials provided with the distribution.
141541Srgrimes *
151541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
161541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
171541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
191541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
211541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
221541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
231541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
241541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
251541Srgrimes * SUCH DAMAGE.
261541Srgrimes */
271541Srgrimes
281541Srgrimes
2910965Swollman#include <sys/cdefs.h>
3050477Speter__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pci_irq.c 270159 2014-08-19 01:20:24Z grehan $");
311541Srgrimes
321541Srgrimes#include <sys/param.h>
33125680Sbms#include <machine/vmm.h>
3455679Sshin
3556041Sshin#include <assert.h>
36101106Srwatson#include <pthread.h>
3729514Sjoerg#include <stdbool.h>
38130989Sps#include <stdio.h>
3929514Sjoerg#include <stdlib.h>
401541Srgrimes#include <vmmapi.h>
411541Srgrimes
4276166Smarkm#include "acpi.h"
4347547Sdg#include "inout.h"
4476166Smarkm#include "pci_emul.h"
451541Srgrimes#include "pci_irq.h"
4676166Smarkm#include "pci_lpc.h"
471541Srgrimes
481541Srgrimes/*
491541Srgrimes * Implement an 8 pin PCI interrupt router compatible with the router
5076166Smarkm * present on Intel's ICH10 chip.
511541Srgrimes */
521541Srgrimes
531541Srgrimes/* Fields in each PIRQ register. */
541541Srgrimes#define	PIRQ_DIS	0x80
551541Srgrimes#define	PIRQ_IRQ	0x0f
561541Srgrimes
571541Srgrimes/* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
5862587Sitojun#define	PERMITTED_IRQS	0xdef8
59152592Sandre#define	IRQ_PERMITTED(irq)	(((1U << (irq)) & PERMITTED_IRQS) != 0)
6055679Sshin
6155679Sshin/* IRQ count to disable an IRQ. */
6262587Sitojun#define	IRQ_DISABLED	0xff
6355679Sshin
6455679Sshinstatic struct pirq {
651541Srgrimes	uint8_t	reg;
661541Srgrimes	int	use_count;
671541Srgrimes	int	active_count;
681541Srgrimes	pthread_mutex_t lock;
691541Srgrimes} pirqs[8];
701541Srgrimes
711541Srgrimesstatic u_char irq_counts[16];
722788Sdgstatic int pirq_cold = 1;
731541Srgrimes
742788Sdg/*
751541Srgrimes * Returns true if this pin is enabled with a valid IRQ.  Setting the
7656041Sshin * register to a reserved IRQ causes interrupts to not be asserted as
7756041Sshin * if the pin was disabled.
7856041Sshin */
7956041Sshinstatic bool
80105199Ssampirq_valid_irq(int reg)
81105199Ssam{
82105199Ssam
83105199Ssam	if (reg & PIRQ_DIS)
84105199Ssam		return (false);
8558698Sjlemon	return (IRQ_PERMITTED(reg & PIRQ_IRQ));
8658698Sjlemon}
87163606Srwatson
88163606Srwatsonuint8_t
891541Srgrimespirq_read(int pin)
901541Srgrimes{
911541Srgrimes
921541Srgrimes	assert(pin > 0 && pin <= nitems(pirqs));
9398204Ssilby	return (pirqs[pin - 1].reg);
9447547Sdg}
9547547Sdg
961541Srgrimesvoid
9750673Sjlemonpirq_write(struct vmctx *ctx, int pin, uint8_t val)
9850673Sjlemon{
9950673Sjlemon	struct pirq *pirq;
10047547Sdg
10187903Ssilby	assert(pin > 0 && pin <= nitems(pirqs));
10250673Sjlemon	pirq = &pirqs[pin - 1];
10350673Sjlemon	pthread_mutex_lock(&pirq->lock);
10450673Sjlemon	if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
10563048Sjayanth		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
10660067Sjlemon			vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
107133874Srwatson		pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
108109492Shsu		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
109162110Sandre			vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
110162110Sandre	}
111162110Sandre	pthread_mutex_unlock(&pirq->lock);
112162110Sandre}
1131541Srgrimes
1141541Srgrimesvoid
1151541Srgrimespci_irq_reserve(int irq)
1161541Srgrimes{
11798704Sluigi
1181541Srgrimes	assert(irq >= 0 && irq < nitems(irq_counts));
11998704Sluigi	assert(pirq_cold);
120124849Sandre	assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
1211541Srgrimes	irq_counts[irq] = IRQ_DISABLED;
122125698Sbms}
123125680Sbms
124125783Sbmsvoid
12598704Sluigipci_irq_use(int irq)
12655679Sshin{
12798704Sluigi
12898704Sluigi	assert(irq >= 0 && irq < nitems(irq_counts));
1296283Swollman	assert(pirq_cold);
13036335Sfenner	assert(irq_counts[irq] != IRQ_DISABLED);
1311541Srgrimes	irq_counts[irq]++;
132130989Sps}
133136151Sps
134130989Spsvoid
135162110Sandrepci_irq_init(struct vmctx *ctx)
13687193Sdillon{
13760067Sjlemon	int i;
13887193Sdillon
13955679Sshin	for (i = 0; i < nitems(pirqs); i++) {
14098704Sluigi		pirqs[i].reg = PIRQ_DIS;
14155679Sshin		pirqs[i].use_count = 0;
1421541Srgrimes		pirqs[i].active_count = 0;
14355679Sshin		pthread_mutex_init(&pirqs[i].lock, NULL);
14455679Sshin	}
14555679Sshin	for (i = 0; i < nitems(irq_counts); i++) {
146122325Ssam		if (IRQ_PERMITTED(i))
147101713Sjennifer			irq_counts[i] = 0;
1481541Srgrimes		else
1491541Srgrimes			irq_counts[i] = IRQ_DISABLED;
1501541Srgrimes	}
1511541Srgrimes}
1521541Srgrimes
1531541Srgrimesvoid
15484564Sjayanthpci_irq_assert(struct pci_devinst *pi)
15550673Sjlemon{
1561541Srgrimes	struct pirq *pirq;
1571541Srgrimes
1581541Srgrimes	if (pi->pi_lintr.pirq_pin > 0) {
1591541Srgrimes		assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
160133874Srwatson		pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
16150673Sjlemon		pthread_mutex_lock(&pirq->lock);
16250673Sjlemon		pirq->active_count++;
163133874Srwatson		if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
16498704Sluigi			vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
16555679Sshin			    pi->pi_lintr.ioapic_irq);
16698704Sluigi			pthread_mutex_unlock(&pirq->lock);
16798704Sluigi			return;
16898704Sluigi		}
16998704Sluigi		pthread_mutex_unlock(&pirq->lock);
17098704Sluigi	}
17198704Sluigi	vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
17298704Sluigi}
17398704Sluigi
17450673Sjlemonvoid
17584564Sjayanthpci_irq_deassert(struct pci_devinst *pi)
17684564Sjayanth{
17784564Sjayanth	struct pirq *pirq;
17884564Sjayanth
17984564Sjayanth	if (pi->pi_lintr.pirq_pin > 0) {
18084564Sjayanth		assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
18184564Sjayanth		pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
1821541Srgrimes		pthread_mutex_lock(&pirq->lock);
183130989Sps		pirq->active_count--;
184130989Sps		if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
185130989Sps			vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
186130989Sps			    pi->pi_lintr.ioapic_irq);
187130989Sps			pthread_mutex_unlock(&pirq->lock);
188130989Sps			return;
189130989Sps		}
1901541Srgrimes		pthread_mutex_unlock(&pirq->lock);
1911541Srgrimes	}
192124849Sandre	vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
193124849Sandre}
1941541Srgrimes
1951541Srgrimesint
1961541Srgrimespirq_alloc_pin(struct vmctx *ctx)
197130989Sps{
198130989Sps	int best_count, best_irq, best_pin, irq, pin;
199130989Sps
200130989Sps	pirq_cold = 0;
201130989Sps
202130989Sps	/* First, find the least-used PIRQ pin. */
203133874Srwatson	best_pin = 0;
204130989Sps	best_count = pirqs[0].use_count;
205130989Sps	for (pin = 1; pin < nitems(pirqs); pin++) {
206130989Sps		if (pirqs[pin].use_count < best_count) {
207136151Sps			best_pin = pin;
208130989Sps			best_count = pirqs[pin].use_count;
209130989Sps		}
210132418Sjayanth	}
211136151Sps	pirqs[best_pin].use_count++;
212136151Sps
213136151Sps	/* Second, route this pin to an IRQ. */
214136151Sps	if (pirqs[best_pin].reg == PIRQ_DIS) {
215136151Sps		best_irq = -1;
216136151Sps		best_count = 0;
217132417Sjayanth		for (irq = 0; irq < nitems(irq_counts); irq++) {
218132417Sjayanth			if (irq_counts[irq] == IRQ_DISABLED)
219132417Sjayanth				continue;
220133874Srwatson			if (best_irq == -1 || irq_counts[irq] < best_count) {
221133874Srwatson				best_irq = irq;
222132417Sjayanth				best_count = irq_counts[irq];
223132417Sjayanth			}
224132417Sjayanth		}
225133874Srwatson		assert(best_irq >= 0);
226132417Sjayanth		irq_counts[best_irq]++;
227133874Srwatson		pirqs[best_pin].reg = best_irq;
228133874Srwatson		vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
229132417Sjayanth	}
230132417Sjayanth
231132417Sjayanth	return (best_pin + 1);
232132417Sjayanth}
233132417Sjayanth
234132417Sjayanthint
235136151Spspirq_irq(int pin)
236136151Sps{
237132417Sjayanth	assert(pin > 0 && pin <= nitems(pirqs));
238136151Sps	return (pirqs[pin - 1].reg & PIRQ_IRQ);
239130989Sps}
240133874Srwatson
241132417Sjayanth/* XXX: Generate $PIR table. */
242130989Sps
243137066Srwatsonstatic void
244137066Srwatsonpirq_dsdt(void)
245130989Sps{
246133874Srwatson	char *irq_prs, *old;
247132417Sjayanth	int irq, pin;
248130989Sps
249130989Sps	irq_prs = NULL;
250132417Sjayanth	for (irq = 0; irq < nitems(irq_counts); irq++) {
251130989Sps		if (!IRQ_PERMITTED(irq))
2526283Swollman			continue;
2536283Swollman		if (irq_prs == NULL)
2546283Swollman			asprintf(&irq_prs, "%d", irq);
2556283Swollman		else {
2566283Swollman			old = irq_prs;
2576283Swollman			asprintf(&irq_prs, "%s,%d", old, irq);
2586283Swollman			free(old);
2596283Swollman		}
260136327Srwatson	}
2616283Swollman
2621541Srgrimes	/*
2631541Srgrimes	 * A helper method to validate a link register's value.  This
2641541Srgrimes	 * duplicates pirq_valid_irq().
2651541Srgrimes	 */
2661541Srgrimes	dsdt_line("");
267146463Sps	dsdt_line("Method (PIRV, 1, NotSerialized)");
268124849Sandre	dsdt_line("{");
2691541Srgrimes	dsdt_line("  If (And (Arg0, 0x%02X))", PIRQ_DIS);
2701541Srgrimes	dsdt_line("  {");
2711541Srgrimes	dsdt_line("    Return (0x00)");
2721541Srgrimes	dsdt_line("  }");
2731541Srgrimes	dsdt_line("  And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
27445439Sjulian	dsdt_line("  If (LLess (Local0, 0x03))");
2751541Srgrimes	dsdt_line("  {");
2761541Srgrimes	dsdt_line("    Return (0x00)");
2771541Srgrimes	dsdt_line("  }");
2781541Srgrimes	dsdt_line("  If (LEqual (Local0, 0x08))");
2791541Srgrimes	dsdt_line("  {");
2801541Srgrimes	dsdt_line("    Return (0x00)");
2811541Srgrimes	dsdt_line("  }");
2821541Srgrimes	dsdt_line("  If (LEqual (Local0, 0x0D))");
2831541Srgrimes	dsdt_line("  {");
2841541Srgrimes	dsdt_line("    Return (0x00)");
2851541Srgrimes	dsdt_line("  }");
2861541Srgrimes	dsdt_line("  Return (0x01)");
287124849Sandre	dsdt_line("}");
2881541Srgrimes
28950673Sjlemon	for (pin = 0; pin < nitems(pirqs); pin++) {
2901541Srgrimes		dsdt_line("");
2911541Srgrimes		dsdt_line("Device (LNK%c)", 'A' + pin);
2921541Srgrimes		dsdt_line("{");
2931541Srgrimes		dsdt_line("  Name (_HID, EisaId (\"PNP0C0F\"))");
294104815Sdillon		dsdt_line("  Name (_UID, 0x%02X)", pin + 1);
295133874Srwatson		dsdt_line("  Method (_STA, 0, NotSerialized)");
296104815Sdillon		dsdt_line("  {");
297124849Sandre		dsdt_line("    If (PIRV (PIR%c))", 'A' + pin);
298104815Sdillon		dsdt_line("    {");
299104815Sdillon		dsdt_line("       Return (0x0B)");
300104815Sdillon		dsdt_line("    }");
301104815Sdillon		dsdt_line("    Else");
302104815Sdillon		dsdt_line("    {");
303104815Sdillon		dsdt_line("       Return (0x09)");
304104815Sdillon		dsdt_line("    }");
305133874Srwatson		dsdt_line("  }");
306130989Sps		dsdt_line("  Name (_PRS, ResourceTemplate ()");
307133874Srwatson		dsdt_line("  {");
308104815Sdillon		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
309136151Sps		dsdt_line("      {%s}", irq_prs);
310136151Sps		dsdt_line("  })");
311136151Sps		dsdt_line("  Name (CB%02X, ResourceTemplate ()", pin + 1);
312136151Sps		dsdt_line("  {");
313136151Sps		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
3141541Srgrimes		dsdt_line("      {}");
315136151Sps		dsdt_line("  })");
316136151Sps		dsdt_line("  CreateWordField (CB%02X, 0x01, CIR%c)",
317136151Sps		    pin + 1, 'A' + pin);
318136151Sps		dsdt_line("  Method (_CRS, 0, NotSerialized)");
319136151Sps		dsdt_line("  {");
320138199Sps		dsdt_line("    And (PIR%c, 0x%02X, Local0)", 'A' + pin,
321138199Sps		    PIRQ_DIS | PIRQ_IRQ);
322140138Sps		dsdt_line("    If (PIRV (Local0))");
323140138Sps		dsdt_line("    {");
324140138Sps		dsdt_line("      ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
325140138Sps		dsdt_line("    }");
326140138Sps		dsdt_line("    Else");
327140138Sps		dsdt_line("    {");
328140138Sps		dsdt_line("      Store (0x00, CIR%c)", 'A' + pin);
329140138Sps		dsdt_line("    }");
330140138Sps		dsdt_line("    Return (CB%02X)", pin + 1);
331140138Sps		dsdt_line("  }");
332140138Sps		dsdt_line("  Method (_DIS, 0, NotSerialized)");
333140138Sps		dsdt_line("  {");
334140138Sps		dsdt_line("    Store (0x80, PIR%c)", 'A' + pin);
335140138Sps		dsdt_line("  }");
336140138Sps		dsdt_line("  Method (_SRS, 1, NotSerialized)");
337140138Sps		dsdt_line("  {");
338136151Sps		dsdt_line("    CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
339136151Sps		dsdt_line("    FindSetRightBit (SIR%c, Local0)", 'A' + pin);
340136151Sps		dsdt_line("    Store (Decrement (Local0), PIR%c)", 'A' + pin);
3416283Swollman		dsdt_line("  }");
3426283Swollman		dsdt_line("}");
3436283Swollman	}
3446283Swollman	free(irq_prs);
3456283Swollman}
3466283SwollmanLPC_DSDT(pirq_dsdt);
347155961Sqingli