// SPDX-License-Identifier: GPL-2.0-or-later /* * pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $) * * Copyright (C) 2001, 2002 Andy Grover <[email protected]> * Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]> * Copyright (C) 2002 Dominik Brodowski <[email protected]> * * TBD: * 1. Support more than one IRQ resource entry per link device (index). * 2. Implement start/stop mechanism and use ACPI Bus Driver facilities * for IRQ management (e.g. start()->_SRS). */ #define pr_fmt(fmt) … #include <linux/syscore_ops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/irq.h> #include "internal.h" #define ACPI_PCI_LINK_CLASS … #define ACPI_PCI_LINK_DEVICE_NAME … #define ACPI_PCI_LINK_MAX_POSSIBLE … static int acpi_pci_link_add(struct acpi_device *device, const struct acpi_device_id *not_used); static void acpi_pci_link_remove(struct acpi_device *device); static const struct acpi_device_id link_device_ids[] = …; static struct acpi_scan_handler pci_link_handler = …; /* * If a link is initialized, we never change its active and initialized * later even the link is disable. Instead, we just repick the active irq */ struct acpi_pci_link_irq { … }; struct acpi_pci_link { … }; static LIST_HEAD(acpi_link_list); static DEFINE_MUTEX(acpi_link_lock); static int sci_irq = …, sci_penalty; /* -------------------------------------------------------------------------- PCI Link Device Management -------------------------------------------------------------------------- */ /* * set context (link) possible list from resource list */ static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource, void *context) { … } static int acpi_pci_link_get_possible(struct acpi_pci_link *link) { … } static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource, void *context) { … } /* * Run _CRS and set link->irq.active * * return value: * 0 - success * !0 - failure */ static int acpi_pci_link_get_current(struct acpi_pci_link *link) { … } static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) { … } /* -------------------------------------------------------------------------- PCI Link IRQ Management -------------------------------------------------------------------------- */ /* * "acpi_irq_balance" (default in APIC mode) enables ACPI to use PIC Interrupt * Link Devices to move the PIRQs around to minimize sharing. * * "acpi_irq_nobalance" (default in PIC mode) tells ACPI not to move any PIC IRQs * that the BIOS has already set to active. This is necessary because * ACPI has no automatic means of knowing what ISA IRQs are used. Note that * if the BIOS doesn't set a Link Device active, ACPI needs to program it * even if acpi_irq_nobalance is set. * * A tables of penalties avoids directing PCI interrupts to well known * ISA IRQs. Boot params are available to over-ride the default table: * * List interrupts that are free for PCI use. * acpi_irq_pci=n[,m] * * List interrupts that should not be used for PCI: * acpi_irq_isa=n[,m] * * Note that PCI IRQ routers have a list of possible IRQs, * which may not include the IRQs this table says are available. * * Since this heuristic can't tell the difference between a link * that no device will attach to, vs. a link which may be shared * by multiple active devices -- it is not optimal. * * If interrupt performance is that important, get an IO-APIC system * with a pin dedicated to each device. Or for that matter, an MSI * enabled system. */ #define ACPI_MAX_ISA_IRQS … #define PIRQ_PENALTY_PCI_POSSIBLE … #define PIRQ_PENALTY_PCI_USING … #define PIRQ_PENALTY_ISA_TYPICAL … #define PIRQ_PENALTY_ISA_USED … #define PIRQ_PENALTY_ISA_ALWAYS … static int acpi_isa_irq_penalty[ACPI_MAX_ISA_IRQS] = …; static int acpi_irq_pci_sharing_penalty(int irq) { … } static int acpi_irq_get_penalty(int irq) { … } int __init acpi_irq_penalty_init(void) { … } static int acpi_irq_balance = …; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) { … } /* * acpi_pci_link_allocate_irq * success: return IRQ >= 0 * failure: return -1 */ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name) { … } /* * We don't change link's irq information here. After it is reenabled, we * continue use the info */ int acpi_pci_link_free_irq(acpi_handle handle) { … } /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ static int acpi_pci_link_add(struct acpi_device *device, const struct acpi_device_id *not_used) { … } static int acpi_pci_link_resume(struct acpi_pci_link *link) { … } static void irqrouter_resume(void) { … } static void acpi_pci_link_remove(struct acpi_device *device) { … } /* * modify acpi_isa_irq_penalty[] from cmdline */ static int __init acpi_irq_penalty_update(char *str, int used) { … } /* * We'd like PNP to call this routine for the * single ISA_USED value for each legacy device. * But instead it calls us with each POSSIBLE setting. * There is no ISA_POSSIBLE weight, so we simply use * the (small) PCI_USING penalty. */ void acpi_penalize_isa_irq(int irq, int active) { … } bool acpi_isa_irq_available(int irq) { … } void acpi_penalize_sci_irq(int irq, int trigger, int polarity) { … } /* * Over-ride default table to reserve additional IRQs for use by ISA * e.g. acpi_irq_isa=5 * Useful for telling ACPI how not to interfere with your ISA sound card. */ static int __init acpi_irq_isa(char *str) { … } __setup(…); /* * Over-ride default table to free additional IRQs for use by PCI * e.g. acpi_irq_pci=7,15 * Used for acpi_irq_balance to free up IRQs to reduce PCI IRQ sharing. */ static int __init acpi_irq_pci(char *str) { … } __setup(…); static int __init acpi_irq_nobalance_set(char *str) { … } __setup(…); static int __init acpi_irq_balance_set(char *str) { … } __setup(…); static struct syscore_ops irqrouter_syscore_ops = …; void __init acpi_pci_link_init(void) { … }