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