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/dev/acpica/acpi_pci_link.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include "opt_acpi.h"
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/kernel.h>
   34 #include <sys/limits.h>
   35 #include <sys/malloc.h>
   36 #include <sys/module.h>
   37 
   38 #include <contrib/dev/acpica/acpi.h>
   39 #include <dev/acpica/acpivar.h>
   40 #include <dev/acpica/acpi_pcibvar.h>
   41 
   42 #include <machine/pci_cfgreg.h>
   43 #include <dev/pci/pcireg.h>
   44 #include <dev/pci/pcivar.h>
   45 #include "pcib_if.h"
   46 
   47 /* Hooks for the ACPI CA debugging infrastructure. */
   48 #define _COMPONENT      ACPI_BUS
   49 ACPI_MODULE_NAME("PCI_LINK")
   50 
   51 ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
   52 
   53 #define NUM_ISA_INTERRUPTS      16
   54 #define NUM_ACPI_INTERRUPTS     256
   55 
   56 /*
   57  * An ACPI PCI link device may contain multiple links.  Each link has its
   58  * own ACPI resource.  _PRT entries specify which link is being used via
   59  * the Source Index.
   60  *
   61  * XXX: A note about Source Indices and DPFs:  Currently we assume that
   62  * the DPF start and end tags are not counted towards the index that
   63  * Source Index corresponds to.  Also, we assume that when DPFs are in use
   64  * they various sets overlap in terms of Indices.  Here's an example
   65  * resource list indicating these assumptions:
   66  *
   67  * Resource             Index
   68  * --------             -----
   69  * I/O Port             0
   70  * Start DPF            -
   71  * IRQ                  1
   72  * MemIO                2
   73  * Start DPF            -
   74  * IRQ                  1
   75  * MemIO                2
   76  * End DPF              -
   77  * DMA Channel          3
   78  *
   79  * The XXX is because I'm not sure if this is a valid assumption to make.
   80  */
   81 
   82 /* States during DPF processing. */
   83 #define DPF_OUTSIDE     0
   84 #define DPF_FIRST       1
   85 #define DPF_IGNORE      2
   86 
   87 struct link;
   88 
   89 struct acpi_pci_link_softc {
   90         int     pl_num_links;
   91         int     pl_crs_bad;
   92         struct link *pl_links;
   93         device_t pl_dev;
   94 };
   95 
   96 struct link {
   97         struct acpi_pci_link_softc *l_sc;
   98         uint8_t l_bios_irq;
   99         uint8_t l_irq;
  100         uint8_t l_initial_irq;
  101         int     l_res_index;
  102         int     l_num_irqs;
  103         int     *l_irqs;
  104         int     l_references;
  105         int     l_routed:1;
  106         int     l_isa_irq:1;
  107         ACPI_RESOURCE l_prs_template;
  108 };
  109 
  110 struct link_count_request {
  111         int     in_dpf;
  112         int     count;
  113 };
  114 
  115 struct link_res_request {
  116         struct acpi_pci_link_softc *sc;
  117         int     in_dpf;
  118         int     res_index;
  119         int     link_index;
  120 };
  121 
  122 MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
  123 
  124 static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
  125 static int pci_link_bios_isa_irqs;
  126 
  127 static char *pci_link_ids[] = { "PNP0C0F", NULL };
  128 
  129 /*
  130  * Fetch the short name associated with an ACPI handle and save it in the
  131  * passed in buffer.
  132  */
  133 static ACPI_STATUS
  134 acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
  135 {
  136         ACPI_BUFFER buf;
  137 
  138         buf.Length = buflen;
  139         buf.Pointer = buffer;
  140         return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
  141 }
  142 
  143 static int
  144 acpi_pci_link_probe(device_t dev)
  145 {
  146         char descr[28], name[12];
  147 
  148         /*
  149          * We explicitly do not check _STA since not all systems set it to
  150          * sensible values.
  151          */
  152         if (acpi_disabled("pci_link") ||
  153             ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids) == NULL)
  154                 return (ENXIO);
  155 
  156         if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
  157             sizeof(name)))) {
  158                 snprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
  159                 device_set_desc_copy(dev, descr);
  160         } else
  161                 device_set_desc(dev, "ACPI PCI Link");
  162         device_quiet(dev);
  163         return (0);
  164 }
  165 
  166 static ACPI_STATUS
  167 acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
  168 {
  169         struct link_count_request *req;
  170 
  171         req = (struct link_count_request *)context;
  172         switch (res->Type) {
  173         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  174                 switch (req->in_dpf) {
  175                 case DPF_OUTSIDE:
  176                         /* We've started the first DPF. */
  177                         req->in_dpf = DPF_FIRST;
  178                         break;
  179                 case DPF_FIRST:
  180                         /* We've started the second DPF. */
  181                         req->in_dpf = DPF_IGNORE;
  182                         break;
  183                 }
  184                 break;
  185         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  186                 /* We are finished with DPF parsing. */
  187                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  188                     ("%s: end dpf when not parsing a dpf", __func__));
  189                 req->in_dpf = DPF_OUTSIDE;
  190                 break;
  191         case ACPI_RESOURCE_TYPE_IRQ:
  192         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  193                 /*
  194                  * Don't count resources if we are in a DPF set that we are
  195                  * ignoring.
  196                  */
  197                 if (req->in_dpf != DPF_IGNORE)
  198                         req->count++;
  199         }
  200         return (AE_OK);
  201 }
  202 
  203 static ACPI_STATUS
  204 link_add_crs(ACPI_RESOURCE *res, void *context)
  205 {
  206         struct link_res_request *req;
  207         struct link *link;
  208 
  209         ACPI_SERIAL_ASSERT(pci_link);
  210         req = (struct link_res_request *)context;
  211         switch (res->Type) {
  212         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  213                 switch (req->in_dpf) {
  214                 case DPF_OUTSIDE:
  215                         /* We've started the first DPF. */
  216                         req->in_dpf = DPF_FIRST;
  217                         break;
  218                 case DPF_FIRST:
  219                         /* We've started the second DPF. */
  220                         panic(
  221                 "%s: Multiple dependent functions within a current resource",
  222                             __func__);
  223                         break;
  224                 }
  225                 break;
  226         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  227                 /* We are finished with DPF parsing. */
  228                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  229                     ("%s: end dpf when not parsing a dpf", __func__));
  230                 req->in_dpf = DPF_OUTSIDE;
  231                 break;
  232         case ACPI_RESOURCE_TYPE_IRQ:
  233         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  234                 KASSERT(req->link_index < req->sc->pl_num_links,
  235                     ("%s: array boundary violation", __func__));
  236                 link = &req->sc->pl_links[req->link_index];
  237                 link->l_res_index = req->res_index;
  238                 req->link_index++;
  239                 req->res_index++;
  240 
  241                 /*
  242                  * Only use the current value if there's one IRQ.  Some
  243                  * systems return multiple IRQs (which is nonsense for _CRS)
  244                  * when the link hasn't been programmed.
  245                  */
  246                 if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
  247                         if (res->Data.Irq.InterruptCount == 1)
  248                                 link->l_irq = res->Data.Irq.Interrupts[0];
  249                 } else if (res->Data.ExtendedIrq.InterruptCount == 1)
  250                         link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
  251 
  252                 /*
  253                  * An IRQ of zero means that the link isn't routed.
  254                  */
  255                 if (link->l_irq == 0)
  256                         link->l_irq = PCI_INVALID_IRQ;
  257                 break;
  258         default:
  259                 req->res_index++;
  260         }
  261         return (AE_OK);
  262 }
  263 
  264 /*
  265  * Populate the set of possible IRQs for each device.
  266  */
  267 static ACPI_STATUS
  268 link_add_prs(ACPI_RESOURCE *res, void *context)
  269 {
  270         struct link_res_request *req;
  271         struct link *link;
  272         UINT8 *irqs = NULL;
  273         UINT32 *ext_irqs = NULL;
  274         int i, is_ext_irq = 1;
  275 
  276         ACPI_SERIAL_ASSERT(pci_link);
  277         req = (struct link_res_request *)context;
  278         switch (res->Type) {
  279         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  280                 switch (req->in_dpf) {
  281                 case DPF_OUTSIDE:
  282                         /* We've started the first DPF. */
  283                         req->in_dpf = DPF_FIRST;
  284                         break;
  285                 case DPF_FIRST:
  286                         /* We've started the second DPF. */
  287                         req->in_dpf = DPF_IGNORE;
  288                         break;
  289                 }
  290                 break;
  291         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  292                 /* We are finished with DPF parsing. */
  293                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  294                     ("%s: end dpf when not parsing a dpf", __func__));
  295                 req->in_dpf = DPF_OUTSIDE;
  296                 break;
  297         case ACPI_RESOURCE_TYPE_IRQ:
  298                 is_ext_irq = 0;
  299                 /* fall through */
  300         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  301                 /*
  302                  * Don't parse resources if we are in a DPF set that we are
  303                  * ignoring.
  304                  */
  305                 if (req->in_dpf == DPF_IGNORE)
  306                         break;
  307 
  308                 KASSERT(req->link_index < req->sc->pl_num_links,
  309                     ("%s: array boundary violation", __func__));
  310                 link = &req->sc->pl_links[req->link_index];
  311                 if (link->l_res_index == -1) {
  312                         KASSERT(req->sc->pl_crs_bad,
  313                             ("res_index should be set"));
  314                         link->l_res_index = req->res_index;
  315                 }
  316                 req->link_index++;
  317                 req->res_index++;
  318 
  319                 /*
  320                  * Stash a copy of the resource for later use when doing
  321                  * _SRS.
  322                  */
  323                 bcopy(res, &link->l_prs_template, sizeof(ACPI_RESOURCE));
  324                 if (is_ext_irq) {
  325                         link->l_num_irqs =
  326                             res->Data.ExtendedIrq.InterruptCount;
  327                         ext_irqs = res->Data.ExtendedIrq.Interrupts;
  328                 } else {
  329                         link->l_num_irqs = res->Data.Irq.InterruptCount;
  330                         irqs = res->Data.Irq.Interrupts;
  331                 }
  332                 if (link->l_num_irqs == 0)
  333                         break;
  334 
  335                 /*
  336                  * Save a list of the valid IRQs.  Also, if all of the
  337                  * valid IRQs are ISA IRQs, then mark this link as
  338                  * routed via an ISA interrupt.
  339                  */
  340                 link->l_isa_irq = TRUE;
  341                 link->l_irqs = malloc(sizeof(int) * link->l_num_irqs,
  342                     M_PCI_LINK, M_WAITOK | M_ZERO);
  343                 for (i = 0; i < link->l_num_irqs; i++) {
  344                         if (is_ext_irq) {
  345                                 link->l_irqs[i] = ext_irqs[i];
  346                                 if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
  347                                         link->l_isa_irq = FALSE;
  348                         } else {
  349                                 link->l_irqs[i] = irqs[i];
  350                                 if (irqs[i] >= NUM_ISA_INTERRUPTS)
  351                                         link->l_isa_irq = FALSE;
  352                         }
  353                 }
  354                 break;
  355         default:
  356                 if (req->in_dpf == DPF_IGNORE)
  357                         break;
  358                 if (req->sc->pl_crs_bad)
  359                         device_printf(req->sc->pl_dev,
  360                     "Warning: possible resource %d will be lost during _SRS\n",
  361                             req->res_index);
  362                 req->res_index++;
  363         }
  364         return (AE_OK);
  365 }
  366 
  367 static int
  368 link_valid_irq(struct link *link, int irq)
  369 {
  370         int i;
  371 
  372         ACPI_SERIAL_ASSERT(pci_link);
  373 
  374         /* Invalid interrupts are never valid. */
  375         if (!PCI_INTERRUPT_VALID(irq))
  376                 return (FALSE);
  377 
  378         /* Any interrupt in the list of possible interrupts is valid. */
  379         for (i = 0; i < link->l_num_irqs; i++)
  380                 if (link->l_irqs[i] == irq)
  381                          return (TRUE);
  382 
  383         /*
  384          * For links routed via an ISA interrupt, if the SCI is routed via
  385          * an ISA interrupt, the SCI is always treated as a valid IRQ.
  386          */
  387         if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
  388             irq < NUM_ISA_INTERRUPTS)
  389                 return (TRUE);
  390 
  391         /* If the interrupt wasn't found in the list it is not valid. */
  392         return (FALSE);
  393 }
  394 
  395 static void
  396 acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
  397 {
  398         struct link *link;
  399         char buf[16];
  400         int i, j;
  401 
  402         ACPI_SERIAL_ASSERT(pci_link);
  403         if (header) {
  404                 snprintf(buf, sizeof(buf), "%s:",
  405                     device_get_nameunit(sc->pl_dev));
  406                 printf("%-16.16s  Index  IRQ  Rtd  Ref  IRQs\n", buf);
  407         }
  408         for (i = 0; i < sc->pl_num_links; i++) {
  409                 link = &sc->pl_links[i];
  410                 printf("  %-14.14s  %5d  %3d   %c   %3d ", i == 0 ? tag : "", i,
  411                     link->l_irq, link->l_routed ? 'Y' : 'N',
  412                     link->l_references);
  413                 if (link->l_num_irqs == 0)
  414                         printf(" none");
  415                 else for (j = 0; j < link->l_num_irqs; j++)
  416                         printf(" %d", link->l_irqs[j]);
  417                 printf("\n");
  418         }
  419 }
  420 
  421 static int
  422 acpi_pci_link_attach(device_t dev)
  423 {
  424         struct acpi_pci_link_softc *sc;
  425         struct link_count_request creq;
  426         struct link_res_request rreq;
  427         ACPI_STATUS status;
  428         int i;
  429 
  430         sc = device_get_softc(dev);
  431         sc->pl_dev = dev;
  432         ACPI_SERIAL_BEGIN(pci_link);
  433 
  434         /*
  435          * Count the number of current resources so we know how big of
  436          * a link array to allocate.  On some systems, _CRS is broken,
  437          * so for those systems try to derive the count from _PRS instead.
  438          */
  439         creq.in_dpf = DPF_OUTSIDE;
  440         creq.count = 0;
  441         status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
  442             acpi_count_irq_resources, &creq);
  443         sc->pl_crs_bad = ACPI_FAILURE(status);
  444         if (sc->pl_crs_bad) {
  445                 creq.in_dpf = DPF_OUTSIDE;
  446                 creq.count = 0;
  447                 status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
  448                     acpi_count_irq_resources, &creq);
  449                 if (ACPI_FAILURE(status)) {
  450                         device_printf(dev,
  451                             "Unable to parse _CRS or _PRS: %s\n",
  452                             AcpiFormatException(status));
  453                         ACPI_SERIAL_END(pci_link);
  454                         return (ENXIO);
  455                 }
  456         }
  457         sc->pl_num_links = creq.count;
  458         if (creq.count == 0) {
  459                 ACPI_SERIAL_END(pci_link);
  460                 return (0);
  461         }
  462         sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
  463             M_PCI_LINK, M_WAITOK | M_ZERO);
  464 
  465         /* Initialize the child links. */
  466         for (i = 0; i < sc->pl_num_links; i++) {
  467                 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  468                 sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
  469                 sc->pl_links[i].l_sc = sc;
  470                 sc->pl_links[i].l_isa_irq = FALSE;
  471                 sc->pl_links[i].l_res_index = -1;
  472         }
  473 
  474         /* Try to read the current settings from _CRS if it is valid. */
  475         if (!sc->pl_crs_bad) {
  476                 rreq.in_dpf = DPF_OUTSIDE;
  477                 rreq.link_index = 0;
  478                 rreq.res_index = 0;
  479                 rreq.sc = sc;
  480                 status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
  481                     link_add_crs, &rreq);
  482                 if (ACPI_FAILURE(status)) {
  483                         device_printf(dev, "Unable to parse _CRS: %s\n",
  484                             AcpiFormatException(status));
  485                         goto fail;
  486                 }
  487         }
  488 
  489         /*
  490          * Try to read the possible settings from _PRS.  Note that if the
  491          * _CRS is toast, we depend on having a working _PRS.  However, if
  492          * _CRS works, then it is ok for _PRS to be missing.
  493          */
  494         rreq.in_dpf = DPF_OUTSIDE;
  495         rreq.link_index = 0;
  496         rreq.res_index = 0;
  497         rreq.sc = sc;
  498         status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
  499             link_add_prs, &rreq);
  500         if (ACPI_FAILURE(status) &&
  501             (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
  502                 device_printf(dev, "Unable to parse _PRS: %s\n",
  503                     AcpiFormatException(status));
  504                 goto fail;
  505         }
  506         if (bootverbose)
  507                 acpi_pci_link_dump(sc, 1, "Initial Probe");
  508 
  509         /* Verify initial IRQs if we have _PRS. */
  510         if (status != AE_NOT_FOUND)
  511                 for (i = 0; i < sc->pl_num_links; i++)
  512                         if (!link_valid_irq(&sc->pl_links[i],
  513                             sc->pl_links[i].l_irq))
  514                                 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  515         if (bootverbose)
  516                 acpi_pci_link_dump(sc, 0, "Validation");
  517 
  518         /* Save initial IRQs. */
  519         for (i = 0; i < sc->pl_num_links; i++)
  520                 sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
  521 
  522         /*
  523          * Try to disable this link.  If successful, set the current IRQ to
  524          * zero and flags to indicate this link is not routed.  If we can't
  525          * run _DIS (i.e., the method doesn't exist), assume the initial
  526          * IRQ was routed by the BIOS.
  527          */
  528         if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
  529             NULL)))
  530                 for (i = 0; i < sc->pl_num_links; i++)
  531                         sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  532         else
  533                 for (i = 0; i < sc->pl_num_links; i++)
  534                         if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
  535                                 sc->pl_links[i].l_routed = TRUE;
  536         if (bootverbose)
  537                 acpi_pci_link_dump(sc, 0, "After Disable");
  538         ACPI_SERIAL_END(pci_link);
  539         return (0);
  540 fail:
  541         ACPI_SERIAL_END(pci_link);
  542         for (i = 0; i < sc->pl_num_links; i++)
  543                 if (sc->pl_links[i].l_irqs != NULL)
  544                         free(sc->pl_links[i].l_irqs, M_PCI_LINK);
  545         free(sc->pl_links, M_PCI_LINK);
  546         return (ENXIO);
  547 }
  548 
  549 /* XXX: Note that this is identical to pci_pir_search_irq(). */
  550 static uint8_t
  551 acpi_pci_link_search_irq(int bus, int device, int pin)
  552 {
  553         uint32_t value;
  554         uint8_t func, maxfunc;
  555 
  556         /* See if we have a valid device at function 0. */
  557         value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
  558         if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
  559                 return (PCI_INVALID_IRQ);
  560         if (value & PCIM_MFDEV)
  561                 maxfunc = PCI_FUNCMAX;
  562         else
  563                 maxfunc = 0;
  564 
  565         /* Scan all possible functions at this device. */
  566         for (func = 0; func <= maxfunc; func++) {
  567                 value = pci_cfgregread(bus, device, func, PCIR_DEVVENDOR, 4);
  568                 if (value == 0xffffffff)
  569                         continue;
  570                 value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
  571 
  572                 /*
  573                  * See if it uses the pin in question.  Note that the passed
  574                  * in pin uses 0 for A, .. 3 for D whereas the intpin
  575                  * register uses 0 for no interrupt, 1 for A, .. 4 for D.
  576                  */
  577                 if (value != pin + 1)
  578                         continue;
  579                 value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
  580                 if (bootverbose)
  581                         printf(
  582                 "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
  583                             bus, device, pin + 'A', func, value);
  584                 if (value != PCI_INVALID_IRQ)
  585                         return (value);
  586         }
  587         return (PCI_INVALID_IRQ);
  588 }
  589 
  590 /*
  591  * Find the link structure that corresponds to the resource index passed in
  592  * via 'source_index'.
  593  */
  594 static struct link *
  595 acpi_pci_link_lookup(device_t dev, int source_index)
  596 {
  597         struct acpi_pci_link_softc *sc;
  598         int i;
  599 
  600         ACPI_SERIAL_ASSERT(pci_link);
  601         sc = device_get_softc(dev);
  602         for (i = 0; i < sc->pl_num_links; i++)
  603                 if (sc->pl_links[i].l_res_index == source_index)
  604                         return (&sc->pl_links[i]);
  605         return (NULL);
  606 }
  607 
  608 void
  609 acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
  610     int pin)
  611 {
  612         struct link *link;
  613         uint8_t bios_irq;
  614         uintptr_t bus;
  615 
  616         /*
  617          * Look up the PCI bus for the specified PCI bridge device.  Note
  618          * that the PCI bridge device might not have any children yet.
  619          * However, looking up its bus number doesn't require a valid child
  620          * device, so we just pass NULL.
  621          */
  622         if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
  623                 device_printf(pcib, "Unable to read PCI bus number");
  624                 panic("PCI bridge without a bus number");
  625         }
  626                 
  627         /* Bump the reference count. */
  628         ACPI_SERIAL_BEGIN(pci_link);
  629         link = acpi_pci_link_lookup(dev, index);
  630         if (link == NULL) {
  631                 device_printf(dev, "apparently invalid index %d\n", index);
  632                 ACPI_SERIAL_END(pci_link);
  633                 return;
  634         }
  635         link->l_references++;
  636         if (link->l_routed)
  637                 pci_link_interrupt_weights[link->l_irq]++;
  638 
  639         /*
  640          * The BIOS only routes interrupts via ISA IRQs using the ATPICs
  641          * (8259As).  Thus, if this link is routed via an ISA IRQ, go
  642          * look to see if the BIOS routed an IRQ for this link at the
  643          * indicated (bus, slot, pin).  If so, we prefer that IRQ for
  644          * this link and add that IRQ to our list of known-good IRQs.
  645          * This provides a good work-around for link devices whose _CRS
  646          * method is either broken or bogus.  We only use the value
  647          * returned by _CRS if we can't find a valid IRQ via this method
  648          * in fact.
  649          *
  650          * If this link is not routed via an ISA IRQ (because we are using
  651          * APIC for example), then don't bother looking up the BIOS IRQ
  652          * as if we find one it won't be valid anyway.
  653          */
  654         if (!link->l_isa_irq) {
  655                 ACPI_SERIAL_END(pci_link);
  656                 return;
  657         }
  658 
  659         /* Try to find a BIOS IRQ setting from any matching devices. */
  660         bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
  661         if (!PCI_INTERRUPT_VALID(bios_irq)) {
  662                 ACPI_SERIAL_END(pci_link);
  663                 return;
  664         }
  665 
  666         /* Validate the BIOS IRQ. */
  667         if (!link_valid_irq(link, bios_irq)) {
  668                 device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
  669                     bios_irq, (int)bus, slot, pin + 'A');
  670         } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
  671                 link->l_bios_irq = bios_irq;
  672                 if (bios_irq < NUM_ISA_INTERRUPTS)
  673                         pci_link_bios_isa_irqs |= (1 << bios_irq);
  674                 if (bios_irq != link->l_initial_irq &&
  675                     PCI_INTERRUPT_VALID(link->l_initial_irq))
  676                         device_printf(dev,
  677                             "BIOS IRQ %u does not match initial IRQ %u\n",
  678                             bios_irq, link->l_initial_irq);
  679         } else if (bios_irq != link->l_bios_irq)
  680                 device_printf(dev,
  681             "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
  682                     bios_irq, (int)bus, slot, pin + 'A',
  683                     link->l_bios_irq);
  684         ACPI_SERIAL_END(pci_link);
  685 }
  686 
  687 static ACPI_STATUS
  688 acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
  689 {
  690         ACPI_RESOURCE *resource, *end, newres, *resptr;
  691         ACPI_BUFFER crsbuf;
  692         ACPI_STATUS status;
  693         struct link *link;
  694         int i, in_dpf;
  695 
  696         /* Fetch the _CRS. */
  697         ACPI_SERIAL_ASSERT(pci_link);
  698         crsbuf.Pointer = NULL;
  699         crsbuf.Length = ACPI_ALLOCATE_BUFFER;
  700         status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), &crsbuf);
  701         if (ACPI_SUCCESS(status) && crsbuf.Pointer == NULL)
  702                 status = AE_NO_MEMORY;
  703         if (ACPI_FAILURE(status)) {
  704                 if (bootverbose)
  705                         device_printf(sc->pl_dev,
  706                             "Unable to fetch current resources: %s\n",
  707                             AcpiFormatException(status));
  708                 return (status);
  709         }
  710 
  711         /* Fill in IRQ resources via link structures. */
  712         srsbuf->Pointer = NULL;
  713         link = sc->pl_links;
  714         i = 0;
  715         in_dpf = DPF_OUTSIDE;
  716         resource = (ACPI_RESOURCE *)crsbuf.Pointer;
  717         end = (ACPI_RESOURCE *)((char *)crsbuf.Pointer + crsbuf.Length);
  718         for (;;) {
  719                 switch (resource->Type) {
  720                 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  721                         switch (in_dpf) {
  722                         case DPF_OUTSIDE:
  723                                 /* We've started the first DPF. */
  724                                 in_dpf = DPF_FIRST;
  725                                 break;
  726                         case DPF_FIRST:
  727                                 /* We've started the second DPF. */
  728                                 panic(
  729                 "%s: Multiple dependent functions within a current resource",
  730                                     __func__);
  731                                 break;
  732                         }
  733                         resptr = NULL;
  734                         break;
  735                 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  736                         /* We are finished with DPF parsing. */
  737                         KASSERT(in_dpf != DPF_OUTSIDE,
  738                             ("%s: end dpf when not parsing a dpf", __func__));
  739                         in_dpf = DPF_OUTSIDE;
  740                         resptr = NULL;
  741                         break;
  742                 case ACPI_RESOURCE_TYPE_IRQ:
  743                         MPASS(i < sc->pl_num_links);
  744                         MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ);
  745                         newres = link->l_prs_template;
  746                         resptr = &newres;
  747                         resptr->Data.Irq.InterruptCount = 1;
  748                         if (PCI_INTERRUPT_VALID(link->l_irq)) {
  749                                 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
  750                 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
  751                                     __func__, link->l_irq));
  752                                 resptr->Data.Irq.Interrupts[0] = link->l_irq;
  753                         } else
  754                                 resptr->Data.Irq.Interrupts[0] = 0;
  755                         link++;
  756                         i++;
  757                         break;
  758                 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  759                         MPASS(i < sc->pl_num_links);
  760                         MPASS(link->l_prs_template.Type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ);
  761                         newres = link->l_prs_template;
  762                         resptr = &newres;
  763                         resptr->Data.ExtendedIrq.InterruptCount = 1;
  764                         if (PCI_INTERRUPT_VALID(link->l_irq))
  765                                 resptr->Data.ExtendedIrq.Interrupts[0] =
  766                                     link->l_irq;
  767                         else
  768                                 resptr->Data.ExtendedIrq.Interrupts[0] = 0;
  769                         link++;
  770                         i++;
  771                         break;
  772                 default:
  773                         resptr = resource;
  774                 }
  775                 if (resptr != NULL) {
  776                         status = acpi_AppendBufferResource(srsbuf, resptr);
  777                         if (ACPI_FAILURE(status)) {
  778                                 device_printf(sc->pl_dev,
  779                                     "Unable to build resources: %s\n",
  780                                     AcpiFormatException(status));
  781                                 if (srsbuf->Pointer != NULL)
  782                                         AcpiOsFree(srsbuf->Pointer);
  783                                 AcpiOsFree(crsbuf.Pointer);
  784                                 return (status);
  785                         }
  786                 }
  787                 if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
  788                         break;
  789                 resource = ACPI_NEXT_RESOURCE(resource);
  790                 if (resource >= end)
  791                         break;
  792         }
  793         AcpiOsFree(crsbuf.Pointer);
  794         return (AE_OK);
  795 }
  796 
  797 static ACPI_STATUS
  798 acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
  799     ACPI_BUFFER *srsbuf)
  800 {
  801         ACPI_RESOURCE newres;
  802         ACPI_STATUS status;
  803         struct link *link;
  804         int i;
  805 
  806         /* Start off with an empty buffer. */
  807         srsbuf->Pointer = NULL;
  808         link = sc->pl_links;
  809         for (i = 0; i < sc->pl_num_links; i++) {
  810 
  811                 /* Add a new IRQ resource from each link. */
  812                 link = &sc->pl_links[i];
  813                 newres = link->l_prs_template;
  814                 if (newres.Type == ACPI_RESOURCE_TYPE_IRQ) {
  815 
  816                         /* Build an IRQ resource. */
  817                         newres.Data.Irq.InterruptCount = 1;
  818                         if (PCI_INTERRUPT_VALID(link->l_irq)) {
  819                                 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
  820                 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
  821                                     __func__, link->l_irq));
  822                                 newres.Data.Irq.Interrupts[0] = link->l_irq;
  823                         } else
  824                                 newres.Data.Irq.Interrupts[0] = 0;
  825                 } else {
  826 
  827                         /* Build an ExtIRQ resuorce. */
  828                         newres.Data.ExtendedIrq.InterruptCount = 1;
  829                         if (PCI_INTERRUPT_VALID(link->l_irq))
  830                                 newres.Data.ExtendedIrq.Interrupts[0] =
  831                                     link->l_irq;
  832                         else
  833                                 newres.Data.ExtendedIrq.Interrupts[0] = 0;
  834                 }
  835 
  836                 /* Add the new resource to the end of the _SRS buffer. */
  837                 status = acpi_AppendBufferResource(srsbuf, &newres);
  838                 if (ACPI_FAILURE(status)) {
  839                         device_printf(sc->pl_dev,
  840                             "Unable to build resources: %s\n",
  841                             AcpiFormatException(status));
  842                         if (srsbuf->Pointer != NULL)
  843                                 AcpiOsFree(srsbuf->Pointer);
  844                         return (status);
  845                 }
  846         }
  847         return (AE_OK);
  848 }
  849 
  850 static ACPI_STATUS
  851 acpi_pci_link_route_irqs(device_t dev)
  852 {
  853         struct acpi_pci_link_softc *sc;
  854         ACPI_RESOURCE *resource, *end;
  855         ACPI_BUFFER srsbuf;
  856         ACPI_STATUS status;
  857         struct link *link;
  858         int i;
  859 
  860         ACPI_SERIAL_ASSERT(pci_link);
  861         sc = device_get_softc(dev);
  862         if (sc->pl_crs_bad)
  863                 status = acpi_pci_link_srs_from_links(sc, &srsbuf);
  864         else
  865                 status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
  866 
  867         /* Write out new resources via _SRS. */
  868         status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
  869         if (ACPI_FAILURE(status)) {
  870                 device_printf(dev, "Unable to route IRQs: %s\n",
  871                     AcpiFormatException(status));
  872                 AcpiOsFree(srsbuf.Pointer);
  873                 return (status);
  874         }
  875 
  876         /*
  877          * Perform acpi_config_intr() on each IRQ resource if it was just
  878          * routed for the first time.
  879          */
  880         link = sc->pl_links;
  881         i = 0;
  882         resource = (ACPI_RESOURCE *)srsbuf.Pointer;
  883         end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
  884         for (;;) {
  885                 if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
  886                         break;
  887                 switch (resource->Type) {
  888                 case ACPI_RESOURCE_TYPE_IRQ:
  889                 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  890                         MPASS(i < sc->pl_num_links);
  891 
  892                         /*
  893                          * Only configure the interrupt and update the
  894                          * weights if this link has a valid IRQ and was
  895                          * previously unrouted.
  896                          */
  897                         if (!link->l_routed &&
  898                             PCI_INTERRUPT_VALID(link->l_irq)) {
  899                                 link->l_routed = TRUE;
  900                                 acpi_config_intr(dev, resource);
  901                                 pci_link_interrupt_weights[link->l_irq] +=
  902                                     link->l_references;
  903                         }
  904                         link++;
  905                         i++;
  906                         break;
  907                 }
  908                 resource = ACPI_NEXT_RESOURCE(resource);
  909                 if (resource >= end)
  910                         break;
  911         }
  912         AcpiOsFree(srsbuf.Pointer);
  913         return (AE_OK);
  914 }
  915 
  916 static int
  917 acpi_pci_link_resume(device_t dev)
  918 {
  919         struct acpi_pci_link_softc *sc;
  920         ACPI_STATUS status;
  921         int i, routed;
  922 
  923         /*
  924          * If all of our links are routed, then restore the link via _SRS,
  925          * otherwise, disable the link via _DIS.
  926          */
  927         ACPI_SERIAL_BEGIN(pci_link);
  928         sc = device_get_softc(dev);
  929         routed = 0;
  930         for (i = 0; i < sc->pl_num_links; i++)
  931                 if (sc->pl_links[i].l_routed)
  932                         routed++;
  933         if (routed == sc->pl_num_links)
  934                 status = acpi_pci_link_route_irqs(dev);
  935         else {
  936                 AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
  937                 status = AE_OK;
  938         }
  939         ACPI_SERIAL_END(pci_link);
  940         if (ACPI_FAILURE(status))
  941                 return (ENXIO);
  942         else
  943                 return (0);
  944 }
  945 
  946 /*
  947  * Pick an IRQ to use for this unrouted link.
  948  */
  949 static uint8_t
  950 acpi_pci_link_choose_irq(device_t dev, struct link *link)
  951 {
  952         char tunable_buffer[64], link_name[5];
  953         u_int8_t best_irq, pos_irq;
  954         int best_weight, pos_weight, i;
  955 
  956         KASSERT(!link->l_routed, ("%s: link already routed", __func__));
  957         KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
  958             ("%s: link already has an IRQ", __func__));
  959 
  960         /* Check for a tunable override. */
  961         if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
  962             sizeof(link_name)))) {
  963                 snprintf(tunable_buffer, sizeof(tunable_buffer),
  964                     "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
  965                 if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
  966                         if (!link_valid_irq(link, i))
  967                                 device_printf(dev,
  968                                     "Warning, IRQ %d is not listed as valid\n",
  969                                     i);
  970                         return (i);
  971                 }
  972                 snprintf(tunable_buffer, sizeof(tunable_buffer),
  973                     "hw.pci.link.%s.irq", link_name);
  974                 if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
  975                         if (!link_valid_irq(link, i))
  976                                 device_printf(dev,
  977                                     "Warning, IRQ %d is not listed as valid\n",
  978                                     i);
  979                         return (i);
  980                 }
  981         }
  982 
  983         /*
  984          * If we have a valid BIOS IRQ, use that.  We trust what the BIOS
  985          * says it routed over what _CRS says the link thinks is routed.
  986          */
  987         if (PCI_INTERRUPT_VALID(link->l_bios_irq))
  988                 return (link->l_bios_irq);
  989 
  990         /*
  991          * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
  992          * then use that.
  993          */
  994         if (PCI_INTERRUPT_VALID(link->l_initial_irq))
  995                 return (link->l_initial_irq);
  996 
  997         /*
  998          * Ok, we have no useful hints, so we have to pick from the
  999          * possible IRQs.  For ISA IRQs we only use interrupts that
 1000          * have already been used by the BIOS.
 1001          */
 1002         best_irq = PCI_INVALID_IRQ;
 1003         best_weight = INT_MAX;
 1004         for (i = 0; i < link->l_num_irqs; i++) {
 1005                 pos_irq = link->l_irqs[i];
 1006                 if (pos_irq < NUM_ISA_INTERRUPTS &&
 1007                     (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
 1008                         continue;
 1009                 pos_weight = pci_link_interrupt_weights[pos_irq];
 1010                 if (pos_weight < best_weight) {
 1011                         best_weight = pos_weight;
 1012                         best_irq = pos_irq;
 1013                 }
 1014         }
 1015 
 1016         /*
 1017          * If this is an ISA IRQ, try using the SCI if it is also an ISA
 1018          * interrupt as a fallback.
 1019          */
 1020         if (link->l_isa_irq) {
 1021                 pos_irq = AcpiGbl_FADT.SciInterrupt;
 1022                 pos_weight = pci_link_interrupt_weights[pos_irq];
 1023                 if (pos_weight < best_weight) {
 1024                         best_weight = pos_weight;
 1025                         best_irq = pos_irq;
 1026                 }
 1027         }
 1028 
 1029         if (PCI_INTERRUPT_VALID(best_irq)) {
 1030                 if (bootverbose)
 1031                         device_printf(dev, "Picked IRQ %u with weight %d\n",
 1032                             best_irq, best_weight);
 1033         } else
 1034                 device_printf(dev, "Unable to choose an IRQ\n");
 1035         return (best_irq);
 1036 }
 1037 
 1038 int
 1039 acpi_pci_link_route_interrupt(device_t dev, int index)
 1040 {
 1041         struct link *link;
 1042 
 1043         if (acpi_disabled("pci_link"))
 1044                 return (PCI_INVALID_IRQ);
 1045 
 1046         ACPI_SERIAL_BEGIN(pci_link);
 1047         link = acpi_pci_link_lookup(dev, index);
 1048         if (link == NULL)
 1049                 panic("%s: apparently invalid index %d", __func__, index);
 1050 
 1051         /*
 1052          * If this link device is already routed to an interrupt, just return
 1053          * the interrupt it is routed to.
 1054          */
 1055         if (link->l_routed) {
 1056                 KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
 1057                     ("%s: link is routed but has an invalid IRQ", __func__));
 1058                 ACPI_SERIAL_END(pci_link);
 1059                 return (link->l_irq);
 1060         }
 1061 
 1062         /* Choose an IRQ if we need one. */
 1063         if (!PCI_INTERRUPT_VALID(link->l_irq)) {
 1064                 link->l_irq = acpi_pci_link_choose_irq(dev, link);
 1065 
 1066                 /*
 1067                  * Try to route the interrupt we picked.  If it fails, then
 1068                  * assume the interrupt is not routed.
 1069                  */
 1070                 if (PCI_INTERRUPT_VALID(link->l_irq)) {
 1071                         acpi_pci_link_route_irqs(dev);
 1072                         if (!link->l_routed)
 1073                                 link->l_irq = PCI_INVALID_IRQ;
 1074                 }
 1075         }
 1076         ACPI_SERIAL_END(pci_link);
 1077 
 1078         return (link->l_irq);
 1079 }
 1080 
 1081 /*
 1082  * This is gross, but we abuse the identify routine to perform one-time
 1083  * SYSINIT() style initialization for the driver.
 1084  */
 1085 static void
 1086 acpi_pci_link_identify(driver_t *driver, device_t parent)
 1087 {
 1088 
 1089         /*
 1090          * If the SCI is an ISA IRQ, add it to the bitmask of known good
 1091          * ISA IRQs.
 1092          *
 1093          * XXX: If we are using the APIC, the SCI might have been
 1094          * rerouted to an APIC pin in which case this is invalid.  However,
 1095          * if we are using the APIC, we also shouldn't be having any PCI
 1096          * interrupts routed via ISA IRQs, so this is probably ok.
 1097          */
 1098         if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS)
 1099                 pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
 1100 }
 1101 
 1102 static device_method_t acpi_pci_link_methods[] = {
 1103         /* Device interface */
 1104         DEVMETHOD(device_identify,      acpi_pci_link_identify),
 1105         DEVMETHOD(device_probe,         acpi_pci_link_probe),
 1106         DEVMETHOD(device_attach,        acpi_pci_link_attach),
 1107         DEVMETHOD(device_resume,        acpi_pci_link_resume),
 1108 
 1109         {0, 0}
 1110 };
 1111 
 1112 static driver_t acpi_pci_link_driver = {
 1113         "pci_link",
 1114         acpi_pci_link_methods,
 1115         sizeof(struct acpi_pci_link_softc),
 1116 };
 1117 
 1118 static devclass_t pci_link_devclass;
 1119 
 1120 DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, pci_link_devclass, 0,
 1121     0);
 1122 MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);

Cache object: d7362f4c9bda77f264aa2311c340fabd


[ 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.