The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/pci/pci_pir.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
    5  * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
    6  * Copyright (c) 2000, BSDi
    7  * All rights reserved.
    8  * Copyright (c) 2004, John Baldwin <jhb@FreeBSD.org>
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice unmodified, this list of conditions, and the following
   15  *    disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD: head/sys/i386/pci/pci_pir.c 344855 2019-03-06 22:11:45Z jhb $");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/kernel.h>
   39 #include <sys/malloc.h>
   40 #include <sys/module.h>
   41 #include <sys/sysctl.h>
   42 #include <vm/vm.h>
   43 #include <vm/pmap.h>
   44 #include <vm/vm_param.h>
   45 #include <machine/md_var.h>
   46 #include <dev/pci/pcivar.h>
   47 #include <dev/pci/pcireg.h>
   48 #include <machine/pci_cfgreg.h>
   49 #include <machine/segments.h>
   50 #include <machine/pc/bios.h>
   51 
   52 #define NUM_ISA_INTERRUPTS      16
   53 
   54 /*
   55  * A link device.  Loosely based on the ACPI PCI link device.  This doesn't
   56  * try to support priorities for different ISA interrupts.
   57  */
   58 struct pci_link {
   59         TAILQ_ENTRY(pci_link) pl_links;
   60         uint8_t         pl_id;
   61         uint8_t         pl_irq;
   62         uint16_t        pl_irqmask;
   63         int             pl_references;
   64         int             pl_routed;
   65 };
   66 
   67 struct pci_link_lookup {
   68         struct pci_link **pci_link_ptr;
   69         int             bus;
   70         int             device;
   71         int             pin;
   72 };
   73 
   74 struct pci_dev_lookup {
   75         uint8_t         link;
   76         int             bus;
   77         int             device;
   78         int             pin;
   79 };
   80 
   81 typedef void pir_entry_handler(struct PIR_entry *entry,
   82     struct PIR_intpin* intpin, void *arg);
   83 
   84 static void     pci_print_irqmask(u_int16_t irqs);
   85 static int      pci_pir_biosroute(int bus, int device, int func, int pin,
   86                     int irq);
   87 static int      pci_pir_choose_irq(struct pci_link *pci_link, int irqmask);
   88 static void     pci_pir_create_links(struct PIR_entry *entry,
   89                     struct PIR_intpin *intpin, void *arg);
   90 static void     pci_pir_dump_links(void);
   91 static struct pci_link *pci_pir_find_link(uint8_t link_id);
   92 static void     pci_pir_find_link_handler(struct PIR_entry *entry,
   93                     struct PIR_intpin *intpin, void *arg);
   94 static void     pci_pir_initial_irqs(struct PIR_entry *entry,
   95                     struct PIR_intpin *intpin, void *arg);
   96 static void     pci_pir_parse(void);
   97 static uint8_t  pci_pir_search_irq(int bus, int device, int pin);
   98 static int      pci_pir_valid_irq(struct pci_link *pci_link, int irq);
   99 static void     pci_pir_walk_table(pir_entry_handler *handler, void *arg);
  100 
  101 static MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures");
  102 
  103 static struct PIR_table *pci_route_table;
  104 static device_t pir_device;
  105 static int pci_route_count, pir_bios_irqs, pir_parsed;
  106 static TAILQ_HEAD(, pci_link) pci_links;
  107 static int pir_interrupt_weight[NUM_ISA_INTERRUPTS];
  108 
  109 /* sysctl vars */
  110 SYSCTL_DECL(_hw_pci);
  111 
  112 /* XXX this likely should live in a header file */
  113 /* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */
  114 #define PCI_IRQ_OVERRIDE_MASK 0xdef8
  115 
  116 static uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK;
  117 SYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RDTUN,
  118     &pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK,
  119     "Mask of allowed irqs to try to route when it has no good clue about\n"
  120     "which irqs it should use.");
  121 
  122 /*
  123  * Look for the interrupt routing table.
  124  *
  125  * We use PCI BIOS's PIR table if it's available. $PIR is the standard way
  126  * to do this.  Sadly, some machines are not standards conforming and have
  127  * _PIR instead.  We shrug and cope by looking for both.
  128  */
  129 void
  130 pci_pir_open(void)
  131 {
  132         struct PIR_table *pt;
  133         uint32_t sigaddr;
  134         int i;
  135         uint8_t ck, *cv;
  136 
  137         /* Don't try if we've already found a table. */
  138         if (pci_route_table != NULL)
  139                 return;
  140 
  141         /* Look for $PIR and then _PIR. */
  142         sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
  143         if (sigaddr == 0)
  144                 sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
  145         if (sigaddr == 0)
  146                 return;
  147 
  148         /* If we found something, check the checksum and length. */
  149         /* XXX - Use pmap_mapdev()? */
  150         pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
  151         if (pt->pt_header.ph_length <= sizeof(struct PIR_header))
  152                 return;
  153         for (cv = (u_int8_t *)pt, ck = 0, i = 0;
  154              i < (pt->pt_header.ph_length); i++)
  155                 ck += cv[i];
  156         if (ck != 0)
  157                 return;
  158 
  159         /* Ok, we've got a valid table. */
  160         pci_route_table = pt;
  161         pci_route_count = (pt->pt_header.ph_length -
  162             sizeof(struct PIR_header)) / 
  163             sizeof(struct PIR_entry);
  164 }
  165 
  166 /*
  167  * Find the pci_link structure for a given link ID.
  168  */
  169 static struct pci_link *
  170 pci_pir_find_link(uint8_t link_id)
  171 {
  172         struct pci_link *pci_link;
  173 
  174         TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
  175                 if (pci_link->pl_id == link_id)
  176                         return (pci_link);
  177         }
  178         return (NULL);
  179 }
  180 
  181 /*
  182  * Find the link device associated with a PCI device in the table.
  183  */
  184 static void
  185 pci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin,
  186     void *arg)
  187 {
  188         struct pci_link_lookup *lookup;
  189 
  190         lookup = (struct pci_link_lookup *)arg;
  191         if (entry->pe_bus == lookup->bus &&
  192             entry->pe_device == lookup->device &&
  193             intpin - entry->pe_intpin == lookup->pin)
  194                 *lookup->pci_link_ptr = pci_pir_find_link(intpin->link);
  195 }
  196 
  197 /*
  198  * Check to see if a possible IRQ setting is valid.
  199  */
  200 static int
  201 pci_pir_valid_irq(struct pci_link *pci_link, int irq)
  202 {
  203 
  204         if (!PCI_INTERRUPT_VALID(irq))
  205                 return (0);
  206         return (pci_link->pl_irqmask & (1 << irq));
  207 }
  208 
  209 /*
  210  * Walk the $PIR executing the worker function for each valid intpin entry
  211  * in the table.  The handler is passed a pointer to both the entry and
  212  * the intpin in the entry.
  213  */
  214 static void
  215 pci_pir_walk_table(pir_entry_handler *handler, void *arg)
  216 {
  217         struct PIR_entry *entry;
  218         struct PIR_intpin *intpin;
  219         int i, pin;
  220 
  221         entry = &pci_route_table->pt_entry[0];
  222         for (i = 0; i < pci_route_count; i++, entry++) {
  223                 intpin = &entry->pe_intpin[0];
  224                 for (pin = 0; pin < 4; pin++, intpin++)
  225                         if (intpin->link != 0)
  226                                 handler(entry, intpin, arg);
  227         }
  228 }
  229 
  230 static void
  231 pci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin,
  232     void *arg)
  233 {
  234         struct pci_link *pci_link;
  235 
  236         pci_link = pci_pir_find_link(intpin->link);
  237         if (pci_link != NULL) {
  238                 pci_link->pl_references++;
  239                 if (intpin->irqs != pci_link->pl_irqmask) {
  240                         if (bootverbose)
  241                                 printf(
  242         "$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n",
  243                                     entry->pe_bus, entry->pe_device,
  244                                     (intpin - entry->pe_intpin) + 'A',
  245                                     pci_link->pl_id);
  246                         pci_link->pl_irqmask &= intpin->irqs;
  247                 }
  248         } else {
  249                 pci_link = malloc(sizeof(struct pci_link), M_PIR, M_WAITOK);
  250                 pci_link->pl_id = intpin->link;
  251                 pci_link->pl_irqmask = intpin->irqs;
  252                 pci_link->pl_irq = PCI_INVALID_IRQ;
  253                 pci_link->pl_references = 1;
  254                 pci_link->pl_routed = 0;
  255                 TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links);
  256         }
  257 }
  258 
  259 /*
  260  * Look to see if any of the function on the PCI device at bus/device have
  261  * an interrupt routed to intpin 'pin' by the BIOS.
  262  */
  263 static uint8_t
  264 pci_pir_search_irq(int bus, int device, int pin)
  265 {
  266         uint32_t value;
  267         uint8_t func, maxfunc;
  268 
  269         /* See if we have a valid device at function 0. */
  270         value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
  271         if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
  272                 return (PCI_INVALID_IRQ);
  273         if (value & PCIM_MFDEV)
  274                 maxfunc = PCI_FUNCMAX;
  275         else
  276                 maxfunc = 0;
  277 
  278         /* Scan all possible functions at this device. */
  279         for (func = 0; func <= maxfunc; func++) {
  280                 value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
  281                 if (value == 0xffffffff)
  282                         continue;
  283                 value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
  284 
  285                 /*
  286                  * See if it uses the pin in question.  Note that the passed
  287                  * in pin uses 0 for A, .. 3 for D whereas the intpin
  288                  * register uses 0 for no interrupt, 1 for A, .. 4 for D.
  289                  */
  290                 if (value != pin + 1)
  291                         continue;
  292                 value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
  293                 if (bootverbose)
  294                         printf(
  295                 "$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n",
  296                             bus, device, pin + 'A', func, value);
  297                 if (value != PCI_INVALID_IRQ)
  298                         return (value);
  299         }
  300         return (PCI_INVALID_IRQ);
  301 }
  302 
  303 /*
  304  * Try to initialize IRQ based on this device's IRQ.
  305  */
  306 static void
  307 pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin,
  308     void *arg)
  309 {
  310         struct pci_link *pci_link;
  311         uint8_t irq, pin;
  312 
  313         pin = intpin - entry->pe_intpin;
  314         pci_link = pci_pir_find_link(intpin->link);
  315         irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin);
  316         if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq)
  317                 return;
  318 
  319         /* Don't trust any BIOS IRQs greater than 15. */
  320         if (irq >= NUM_ISA_INTERRUPTS) {
  321                 printf(
  322         "$PIR: Ignoring invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
  323                     irq, entry->pe_bus, entry->pe_device, pin + 'A',
  324                     pci_link->pl_id);
  325                 return;
  326         }
  327 
  328         /*
  329          * If we don't have an IRQ for this link yet, then we trust the
  330          * BIOS, even if it seems invalid from the $PIR entries.
  331          */
  332         if (pci_link->pl_irq == PCI_INVALID_IRQ) {
  333                 if (!pci_pir_valid_irq(pci_link, irq))
  334                         printf(
  335         "$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
  336                             irq, entry->pe_bus, entry->pe_device, pin + 'A',
  337                             pci_link->pl_id);
  338                 pci_link->pl_irq = irq;
  339                 pci_link->pl_routed = 1;
  340                 return;
  341         }
  342 
  343         /*
  344          * We have an IRQ and it doesn't match the current IRQ for this
  345          * link.  If the new IRQ is invalid, then warn about it and ignore
  346          * it.  If the old IRQ is invalid and the new IRQ is valid, then
  347          * prefer the new IRQ instead.  If both IRQs are valid, then just
  348          * use the first one.  Note that if we ever get into this situation
  349          * we are having to guess which setting the BIOS actually routed.
  350          * Perhaps we should just give up instead.
  351          */
  352         if (!pci_pir_valid_irq(pci_link, irq)) {
  353                 printf(
  354                 "$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n",
  355                     irq, entry->pe_bus, entry->pe_device, pin + 'A',
  356                     pci_link->pl_id);
  357         } else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) {
  358                 printf(
  359 "$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n", 
  360                     irq, entry->pe_bus, entry->pe_device, pin + 'A',
  361                     pci_link->pl_id, pci_link->pl_irq);
  362                 pci_link->pl_irq = irq;
  363                 pci_link->pl_routed = 1;
  364         } else
  365                 printf(
  366         "$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n",
  367                     irq, entry->pe_bus, entry->pe_device, pin + 'A',
  368                     pci_link->pl_id, pci_link->pl_irq);
  369 }
  370 
  371 /*
  372  * Parse $PIR to enumerate link devices and attempt to determine their
  373  * initial state.  This could perhaps be cleaner if we had drivers for the
  374  * various interrupt routers as they could read the initial IRQ for each
  375  * link.
  376  */
  377 static void
  378 pci_pir_parse(void)
  379 {
  380         char tunable_buffer[64];
  381         struct pci_link *pci_link;
  382         int i, irq;
  383 
  384         /* Only parse once. */
  385         if (pir_parsed)
  386                 return;
  387         pir_parsed = 1;
  388 
  389         /* Enumerate link devices. */
  390         TAILQ_INIT(&pci_links);
  391         pci_pir_walk_table(pci_pir_create_links, NULL);
  392         if (bootverbose) {
  393                 printf("$PIR: Links after initial probe:\n");
  394                 pci_pir_dump_links();
  395         }
  396 
  397         /*
  398          * Check to see if the BIOS has already routed any of the links by
  399          * checking each device connected to each link to see if it has a
  400          * valid IRQ.
  401          */
  402         pci_pir_walk_table(pci_pir_initial_irqs, NULL);
  403         if (bootverbose) {
  404                 printf("$PIR: Links after initial IRQ discovery:\n");
  405                 pci_pir_dump_links();
  406         }
  407 
  408         /*
  409          * Allow the user to override the IRQ for a given link device.  We
  410          * allow invalid IRQs to be specified but warn about them.  An IRQ
  411          * of 255 or 0 clears any preset IRQ.
  412          */
  413         i = 0;
  414         TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
  415                 snprintf(tunable_buffer, sizeof(tunable_buffer),
  416                     "hw.pci.link.%#x.irq", pci_link->pl_id);
  417                 if (getenv_int(tunable_buffer, &irq) == 0)
  418                         continue;
  419                 if (irq == 0)
  420                         irq = PCI_INVALID_IRQ;
  421                 if (irq != PCI_INVALID_IRQ &&
  422                     !pci_pir_valid_irq(pci_link, irq) && bootverbose)
  423                         printf(
  424                 "$PIR: Warning, IRQ %d for link %#x is not listed as valid\n",
  425                             irq, pci_link->pl_id);
  426                 pci_link->pl_routed = 0;
  427                 pci_link->pl_irq = irq;
  428                 i = 1;
  429         }
  430         if (bootverbose && i) {
  431                 printf("$PIR: Links after tunable overrides:\n");
  432                 pci_pir_dump_links();
  433         }
  434 
  435         /*
  436          * Build initial interrupt weights as well as bitmap of "known-good"
  437          * IRQs that the BIOS has already used for PCI link devices.
  438          */
  439         TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
  440                 if (!PCI_INTERRUPT_VALID(pci_link->pl_irq))
  441                         continue;
  442                 pir_bios_irqs |= 1 << pci_link->pl_irq;
  443                 pir_interrupt_weight[pci_link->pl_irq] +=
  444                     pci_link->pl_references;
  445         }
  446         if (bootverbose) {
  447                 printf("$PIR: IRQs used by BIOS: ");
  448                 pci_print_irqmask(pir_bios_irqs);
  449                 printf("\n");
  450                 printf("$PIR: Interrupt Weights:\n[ ");
  451                 for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
  452                         printf(" %3d", i);
  453                 printf(" ]\n[ ");
  454                 for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
  455                         printf(" %3d", pir_interrupt_weight[i]);
  456                 printf(" ]\n");
  457         }
  458 }
  459 
  460 /*
  461  * Use the PCI BIOS to route an interrupt for a given device.
  462  *
  463  * Input:
  464  * AX = PCIBIOS_ROUTE_INTERRUPT
  465  * BH = bus
  466  * BL = device [7:3] / function [2:0]
  467  * CH = IRQ
  468  * CL = Interrupt Pin (0x0A = A, ... 0x0D = D)
  469  */
  470 static int
  471 pci_pir_biosroute(int bus, int device, int func, int pin, int irq)
  472 {
  473         struct bios_regs args;
  474 
  475         args.eax = PCIBIOS_ROUTE_INTERRUPT;
  476         args.ebx = (bus << 8) | (device << 3) | func;
  477         args.ecx = (irq << 8) | (0xa + pin);
  478         return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)));
  479 }
  480 
  481 
  482 /*
  483  * Route a PCI interrupt using a link device from the $PIR.
  484  */
  485 int
  486 pci_pir_route_interrupt(int bus, int device, int func, int pin)
  487 {
  488         struct pci_link_lookup lookup;
  489         struct pci_link *pci_link;
  490         int error, irq;
  491 
  492         if (pci_route_table == NULL)
  493                 return (PCI_INVALID_IRQ);
  494 
  495         /* Lookup link device for this PCI device/pin. */
  496         pci_link = NULL;
  497         lookup.bus = bus;
  498         lookup.device = device;
  499         lookup.pin = pin - 1;
  500         lookup.pci_link_ptr = &pci_link;
  501         pci_pir_walk_table(pci_pir_find_link_handler, &lookup);
  502         if (pci_link == NULL) {
  503                 printf("$PIR: No matching entry for %d.%d.INT%c\n", bus,
  504                     device, pin - 1 + 'A');
  505                 return (PCI_INVALID_IRQ);
  506         }
  507 
  508         /*
  509          * Pick a new interrupt if we don't have one already.  We look
  510          * for an interrupt from several different sets.  First, if
  511          * this link only has one valid IRQ, use that.  Second, we
  512          * check the set of PCI only interrupts from the $PIR.  Third,
  513          * we check the set of known-good interrupts that the BIOS has
  514          * already used.  Lastly, we check the "all possible valid
  515          * IRQs" set.
  516          */
  517         if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
  518                 if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask))
  519                         irq = ffs(pci_link->pl_irqmask) - 1;
  520                 else
  521                         irq = pci_pir_choose_irq(pci_link,
  522                             pci_route_table->pt_header.ph_pci_irqs);
  523                 if (!PCI_INTERRUPT_VALID(irq))
  524                         irq = pci_pir_choose_irq(pci_link, pir_bios_irqs);
  525                 if (!PCI_INTERRUPT_VALID(irq))
  526                         irq = pci_pir_choose_irq(pci_link,
  527                             pci_irq_override_mask);
  528                 if (!PCI_INTERRUPT_VALID(irq)) {
  529                         if (bootverbose)
  530                                 printf(
  531                         "$PIR: Failed to route interrupt for %d:%d INT%c\n",
  532                                     bus, device, pin - 1 + 'A');
  533                         return (PCI_INVALID_IRQ);
  534                 }
  535                 pci_link->pl_irq = irq;
  536         }
  537 
  538         /* Ask the BIOS to route this IRQ if we haven't done so already. */
  539         if (!pci_link->pl_routed) {
  540                 error = pci_pir_biosroute(bus, device, func, pin - 1,
  541                     pci_link->pl_irq);
  542 
  543                 /* Ignore errors when routing a unique interrupt. */
  544                 if (error && !powerof2(pci_link->pl_irqmask)) {
  545                         printf("$PIR: ROUTE_INTERRUPT failed.\n");
  546                         return (PCI_INVALID_IRQ);
  547                 }
  548                 pci_link->pl_routed = 1;
  549 
  550                 /* Ensure the interrupt is set to level/low trigger. */
  551                 KASSERT(pir_device != NULL, ("missing pir device"));
  552                 BUS_CONFIG_INTR(pir_device, pci_link->pl_irq,
  553                     INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
  554         }
  555         if (bootverbose)
  556                 printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device,
  557                     pin - 1 + 'A', pci_link->pl_irq);
  558         return (pci_link->pl_irq);
  559 }
  560 
  561 /*
  562  * Try to pick an interrupt for the specified link from the interrupts
  563  * set in the mask.
  564  */
  565 static int
  566 pci_pir_choose_irq(struct pci_link *pci_link, int irqmask)
  567 {
  568         int i, irq, realmask;
  569 
  570         /* XXX: Need to have a #define of known bad IRQs to also mask out? */
  571         realmask = pci_link->pl_irqmask & irqmask;
  572         if (realmask == 0)
  573                 return (PCI_INVALID_IRQ);
  574 
  575         /* Find IRQ with lowest weight. */
  576         irq = PCI_INVALID_IRQ;
  577         for (i = 0; i < NUM_ISA_INTERRUPTS; i++) {
  578                 if (!(realmask & 1 << i))
  579                         continue;
  580                 if (irq == PCI_INVALID_IRQ ||
  581                     pir_interrupt_weight[i] < pir_interrupt_weight[irq])
  582                         irq = i;
  583         }
  584         if (bootverbose && PCI_INTERRUPT_VALID(irq)) {
  585                 printf("$PIR: Found IRQ %d for link %#x from ", irq,
  586                     pci_link->pl_id);
  587                 pci_print_irqmask(realmask);
  588                 printf("\n");
  589         }
  590         return (irq);
  591 }
  592 
  593 static void
  594 pci_print_irqmask(u_int16_t irqs)
  595 {
  596         int i, first;
  597 
  598         if (irqs == 0) {
  599                 printf("none");
  600                 return;
  601         }
  602         first = 1;
  603         for (i = 0; i < 16; i++, irqs >>= 1)
  604                 if (irqs & 1) {
  605                         if (!first)
  606                                 printf(" ");
  607                         else
  608                                 first = 0;
  609                         printf("%d", i);
  610                 }
  611 }
  612 
  613 /*
  614  * Display link devices.
  615  */
  616 static void
  617 pci_pir_dump_links(void)
  618 {
  619         struct pci_link *pci_link;
  620 
  621         printf("Link  IRQ  Rtd  Ref  IRQs\n");
  622         TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
  623                 printf("%#4x  %3d   %c   %3d  ", pci_link->pl_id,
  624                     pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N',
  625                     pci_link->pl_references);
  626                 pci_print_irqmask(pci_link->pl_irqmask);
  627                 printf("\n");
  628         }
  629 }
  630 
  631 /*
  632  * See if any interrupts for a given PCI bus are routed in the PIR.  Don't
  633  * even bother looking if the BIOS doesn't support routing anyways.  If we
  634  * are probing a PCI-PCI bridge, then require_parse will be true and we should
  635  * only succeed if a host-PCI bridge has already attached and parsed the PIR.
  636  */
  637 int
  638 pci_pir_probe(int bus, int require_parse)
  639 {
  640         int i;
  641 
  642         if (pci_route_table == NULL || (require_parse && !pir_parsed))
  643                 return (0);
  644         for (i = 0; i < pci_route_count; i++)
  645                 if (pci_route_table->pt_entry[i].pe_bus == bus)
  646                         return (1);
  647         return (0);
  648 }
  649 
  650 /*
  651  * The driver for the new-bus pseudo device pir0 for the $PIR table.
  652  */
  653 
  654 static int
  655 pir_probe(device_t dev)
  656 {
  657         char buf[64];
  658 
  659         snprintf(buf, sizeof(buf), "PCI Interrupt Routing Table: %d Entries",
  660             pci_route_count);
  661         device_set_desc_copy(dev, buf);
  662         return (0);
  663 }
  664 
  665 static int
  666 pir_attach(device_t dev)
  667 {
  668 
  669         pci_pir_parse();
  670         KASSERT(pir_device == NULL, ("Multiple pir devices"));
  671         pir_device = dev;
  672         return (0);
  673 }
  674 
  675 static void
  676 pir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin,
  677     void *arg)
  678 {
  679         struct pci_dev_lookup *pd;
  680 
  681         pd = (struct pci_dev_lookup *)arg;
  682         if (intpin->link != pd->link || pd->bus != -1)
  683                 return;
  684         pd->bus = entry->pe_bus;
  685         pd->device = entry->pe_device;
  686         pd->pin = intpin - entry->pe_intpin;
  687 }
  688 
  689 static int
  690 pir_resume(device_t dev)
  691 {
  692         struct pci_dev_lookup pd;
  693         struct pci_link *pci_link;
  694         int error;
  695 
  696         /* Ask the BIOS to re-route each link that was already routed. */
  697         TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
  698                 if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
  699                         KASSERT(!pci_link->pl_routed,
  700                             ("link %#x is routed but has invalid PCI IRQ",
  701                             pci_link->pl_id));
  702                         continue;
  703                 }
  704                 if (pci_link->pl_routed) {
  705                         pd.bus = -1;
  706                         pd.link = pci_link->pl_id;
  707                         pci_pir_walk_table(pir_resume_find_device, &pd);
  708                         KASSERT(pd.bus != -1,
  709                 ("did not find matching entry for link %#x in the $PIR table",
  710                             pci_link->pl_id));
  711                         if (bootverbose)
  712                                 device_printf(dev,
  713                             "Using %d.%d.INT%c to route link %#x to IRQ %d\n",
  714                                     pd.bus, pd.device, pd.pin + 'A',
  715                                     pci_link->pl_id, pci_link->pl_irq);
  716                         error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin,
  717                             pci_link->pl_irq);
  718                         if (error)
  719                                 device_printf(dev,
  720                             "ROUTE_INTERRUPT on resume for link %#x failed.\n",
  721                                     pci_link->pl_id);
  722                 }
  723         }
  724         return (0);
  725 }
  726 
  727 static device_method_t pir_methods[] = {
  728         /* Device interface */
  729         DEVMETHOD(device_probe,         pir_probe),
  730         DEVMETHOD(device_attach,        pir_attach),
  731         DEVMETHOD(device_resume,        pir_resume),
  732 
  733         { 0, 0 }
  734 };
  735 
  736 static driver_t pir_driver = {
  737         "pir",
  738         pir_methods,
  739         1,
  740 };
  741 
  742 static devclass_t pir_devclass;
  743 
  744 DRIVER_MODULE(pir, legacy, pir_driver, pir_devclass, 0, 0);

Cache object: a4e8832a678dd5be1344f8da3f0b5a68


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.