pci_irq.c revision 283927
1266125Sjhb/*- 2283927Sjhb * 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: stable/10/usr.sbin/bhyve/pci_irq.c 283927 2015-06-02 19:20:39Z 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 118270159Sgrehan 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 128270159Sgrehan assert(irq >= 0 && irq < nitems(irq_counts)); 129266125Sjhb assert(pirq_cold); 130270159Sgrehan assert(irq_counts[irq] != IRQ_DISABLED); 131270159Sgrehan 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 200270159Sgrehan 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 } 225270159Sgrehan 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