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_pcib.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) 2000 Michael Smith
    3  * Copyright (c) 2000 BSDi
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  *      $FreeBSD: releng/5.0/sys/dev/acpica/acpi_pcib.c 106158 2002-10-29 19:08:55Z jhb $
   28  */
   29 #include "opt_acpi.h"
   30 #include <sys/param.h>
   31 #include <sys/bus.h>
   32 #include <sys/malloc.h>
   33 #include <sys/kernel.h>
   34 
   35 #include "acpi.h"
   36 
   37 #include <dev/acpica/acpivar.h>
   38 #include <dev/acpica/acpi_pcibvar.h>
   39 
   40 #include <machine/pci_cfgreg.h>
   41 #include <pci/pcivar.h>
   42 #include <pci/pcib_private.h>
   43 #include "pcib_if.h"
   44 
   45 /*
   46  * Hooks for the ACPI CA debugging infrastructure
   47  */
   48 #define _COMPONENT      ACPI_BUS
   49 ACPI_MODULE_NAME("PCI")
   50 
   51 int
   52 acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
   53 {
   54     device_t                    child;
   55     ACPI_STATUS                 status;
   56 
   57     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
   58 
   59     /*
   60      * Don't attach if we're not really there.
   61      *
   62      * XXX: This isn't entirely correct since we may be a PCI bus
   63      * on a hot-plug docking station, etc.
   64      */
   65     if (!acpi_DeviceIsPresent(dev))
   66         return_VALUE(ENXIO);
   67 
   68     /*
   69      * Get the PCI interrupt routing table for this bus.
   70      */
   71     prt->Length = ACPI_ALLOCATE_BUFFER;
   72     status = AcpiGetIrqRoutingTable(acpi_get_handle(dev), prt);
   73     if (ACPI_FAILURE(status))
   74         /* This is not an error, but it may reduce functionality. */
   75         device_printf(dev,
   76             "could not get PCI interrupt routing table for %s - %s\n",
   77             acpi_name(acpi_get_handle(dev)), AcpiFormatException(status));
   78 
   79     /*
   80      * Attach the PCI bus proper.
   81      */
   82     if ((child = device_add_child(dev, "pci", busno)) == NULL) {
   83         device_printf(device_get_parent(dev), "couldn't attach pci bus\n");
   84         return_VALUE(ENXIO);
   85     }
   86 
   87     /*
   88      * Now go scan the bus.
   89      */
   90     acpi_pci_link_config(dev, prt, busno);
   91     return_VALUE(bus_generic_attach(dev));
   92 }
   93 
   94 int
   95 acpi_pcib_resume(device_t dev, ACPI_BUFFER *prt, int busno)
   96 {
   97     acpi_pci_link_resume(dev, prt, busno);
   98     return (bus_generic_resume(dev));
   99 }
  100 
  101 /*
  102  * Route an interrupt for a child of the bridge.
  103  *
  104  * XXX clean up error messages
  105  *
  106  * XXX this function is somewhat bulky
  107  */
  108 int
  109 acpi_pcib_route_interrupt(device_t pcib, device_t dev, int pin,
  110     ACPI_BUFFER *prtbuf)
  111 {
  112     ACPI_PCI_ROUTING_TABLE      *prt;
  113     ACPI_HANDLE                 lnkdev;
  114     ACPI_BUFFER                 crsbuf, prsbuf;
  115     ACPI_RESOURCE               *crsres, *prsres, resbuf;
  116     ACPI_DEVICE_INFO            devinfo;
  117     ACPI_STATUS                 status;
  118     u_int8_t                    *prtp;
  119     int                         interrupt;
  120     int                         i;
  121 
  122     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  123     
  124     crsbuf.Pointer = NULL;
  125     prsbuf.Pointer = NULL;
  126     interrupt = 255;
  127 
  128     /* ACPI numbers pins 0-3, not 1-4 like the BIOS */
  129     pin--;
  130 
  131     prtp = prtbuf->Pointer;
  132     if (prtp == NULL)                   /* didn't get routing table */
  133         goto out;
  134 
  135     /* scan the table looking for this device */
  136     for (;;) {
  137         prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
  138 
  139         if (prt->Length == 0)           /* end of table */
  140             goto out;
  141 
  142         /*
  143          * Compare the slot number (high word of Address) and pin number
  144          * (note that ACPI uses 0 for INTA) to check for a match.
  145          *
  146          * Note that the low word of the Address field (function number)
  147          * is required by the specification to be 0xffff.  We don't risk
  148          * checking it here.
  149          */
  150         if ((((prt->Address & 0xffff0000) >> 16) == pci_get_slot(dev)) &&
  151             (prt->Pin == pin)) {
  152             if (bootverbose)
  153                 device_printf(pcib, "matched entry for %d.%d.INT%c (source %s)\n",
  154                               pci_get_bus(dev), pci_get_slot(dev), 'A' + pin, prt->Source);
  155             break;
  156         }
  157         
  158         /* skip to next entry */
  159         prtp += prt->Length;
  160     }
  161 
  162     /*
  163      * If source is empty/NULL, the source index is the global IRQ number.
  164      */
  165     if ((prt->Source == NULL) || (prt->Source[0] == '\0')) {
  166         if (bootverbose)
  167             device_printf(pcib, "device is hardwired to IRQ %d\n",
  168                 prt->SourceIndex);
  169         interrupt = prt->SourceIndex;
  170         goto out;
  171     }
  172     
  173     /*
  174      * We have to find the source device (PCI interrupt link device)
  175      */
  176     if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, prt->Source, &lnkdev))) {
  177         device_printf(pcib, "couldn't find PCI interrupt link device %s\n",
  178             prt->Source);
  179         goto out;
  180     }
  181 
  182     /*
  183      * Verify that this is a PCI link device, and that it's present.
  184      */
  185     if (ACPI_FAILURE(AcpiGetObjectInfo(lnkdev, &devinfo))) {
  186         device_printf(pcib, "couldn't validate PCI interrupt link device %s\n",
  187             prt->Source);
  188         goto out;
  189     }
  190     if (!(devinfo.Valid & ACPI_VALID_HID) || strcmp("PNP0C0F", devinfo.HardwareId)) {
  191         device_printf(pcib, "PCI interrupt link device %s has wrong _HID (%s)\n",
  192                       prt->Source, devinfo.HardwareId);
  193         goto out;
  194     }
  195     if (devinfo.Valid & ACPI_VALID_STA && (devinfo.CurrentStatus & 0x9) != 0x9) {
  196         device_printf(pcib, "PCI interrupt link device %s not present\n",
  197                       prt->Source);
  198         goto out;
  199     }
  200 
  201     /*
  202      * Get the current and possible resources for the interrupt link device.
  203      */
  204     crsbuf.Length = ACPI_ALLOCATE_BUFFER;
  205     if (ACPI_FAILURE(status = AcpiGetCurrentResources(lnkdev, &crsbuf))) {
  206         device_printf(pcib, "couldn't get PCI interrupt link device _CRS data - %s\n",
  207                       AcpiFormatException(status));
  208         goto out;       /* this is fatal */
  209     }
  210     prsbuf.Length = ACPI_ALLOCATE_BUFFER;
  211     if (ACPI_FAILURE(status = AcpiGetPossibleResources(lnkdev, &prsbuf))) {
  212         device_printf(pcib, "couldn't get PCI interrupt link device _PRS data - %s\n",
  213                       AcpiFormatException(status));
  214         /* this is not fatal, since it may be hardwired */
  215     }
  216     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._CRS\n",
  217         (long)crsbuf.Length, acpi_name(lnkdev)));
  218     ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "got %ld bytes for %s._PRS\n",
  219         (long)prsbuf.Length, acpi_name(lnkdev)));
  220 
  221     /*
  222      * The interrupt may already be routed, so check _CRS first.  We don't check the
  223      * 'decoding' bit in the _STA result, since there's nothing in the spec that 
  224      * mandates it be set, however some BIOS' will set it if the decode is active.
  225      *
  226      * The Source Index points to the particular resource entry we're interested in.
  227      */
  228     if (ACPI_FAILURE(acpi_FindIndexedResource(&crsbuf, prt->SourceIndex, &crsres))) {
  229         device_printf(pcib, "_CRS buffer corrupt, cannot route interrupt\n");
  230         goto out;
  231     }
  232 
  233     /* type-check the resource we've got */
  234     if (crsres->Id != ACPI_RSTYPE_IRQ) {    /* XXX ACPI_RSTYPE_EXT_IRQ */
  235         device_printf(pcib, "_CRS resource entry has unsupported type %d\n",
  236             crsres->Id);
  237         goto out;
  238     }
  239 
  240     /* if there's more than one interrupt, we are confused */
  241     if (crsres->Data.Irq.NumberOfInterrupts > 1) {
  242         device_printf(pcib, "device has too many interrupts (%d)\n",
  243             crsres->Data.Irq.NumberOfInterrupts);
  244         goto out;
  245     }
  246 
  247     /* 
  248      * If there's only one interrupt, and it's not zero, then we're already routed.
  249      *
  250      * Note that we could also check the 'decoding' bit in _STA, but can't depend on
  251      * it since it's not part of the spec.
  252      *
  253      * XXX check ASL examples to see if this is an acceptable set of tests
  254      */
  255     if ((crsres->Data.Irq.NumberOfInterrupts == 1) && (crsres->Data.Irq.Interrupts[0] != 0)) {
  256         device_printf(pcib, "slot %d INT%c is routed to irq %d\n",
  257             pci_get_slot(dev), 'A' + pin, crsres->Data.Irq.Interrupts[0]);
  258         interrupt = crsres->Data.Irq.Interrupts[0];
  259         goto out;
  260     }
  261     
  262     /* 
  263      * There isn't an interrupt, so we have to look at _PRS to get one.
  264      * Get the set of allowed interrupts from the _PRS resource indexed by SourceIndex.
  265      */
  266     if (prsbuf.Pointer == NULL) {
  267         device_printf(pcib, "device has no routed interrupt and no _PRS on PCI interrupt link device\n");
  268         goto out;
  269     }
  270     if (ACPI_FAILURE(acpi_FindIndexedResource(&prsbuf, prt->SourceIndex, &prsres))) {
  271         device_printf(pcib, "_PRS buffer corrupt, cannot route interrupt\n");
  272         goto out;
  273     }
  274 
  275     /* type-check the resource we've got */
  276     if (prsres->Id != ACPI_RSTYPE_IRQ) {    /* XXX ACPI_RSTYPE_EXT_IRQ */
  277         device_printf(pcib, "_PRS resource entry has unsupported type %d\n",
  278             prsres->Id);
  279         goto out;
  280     }
  281 
  282     /* there has to be at least one interrupt available */
  283     if (prsres->Data.Irq.NumberOfInterrupts < 1) {
  284         device_printf(pcib, "device has no interrupts\n");
  285         goto out;
  286     }
  287 
  288     /*
  289      * Pick an interrupt to use.  Note that a more scientific approach than just
  290      * taking the first one available would be desirable.
  291      *
  292      * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI doesn't
  293      * seem to offer a similar mechanism, so picking a "good" interrupt here is a
  294      * difficult task.
  295      *
  296      * Build a resource buffer and pass it to AcpiSetCurrentResources to route the
  297      * new interrupt.
  298      */
  299     device_printf(pcib, "possible interrupts:");
  300     for (i = 0; i < prsres->Data.Irq.NumberOfInterrupts; i++)
  301         printf("  %d", prsres->Data.Irq.Interrupts[i]);
  302     printf("\n");
  303 
  304     if (crsbuf.Pointer != NULL)                 /* should never happen */
  305         AcpiOsFree(crsbuf.Pointer);
  306     crsbuf.Pointer = NULL;
  307     resbuf.Id = ACPI_RSTYPE_IRQ;
  308     resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
  309     resbuf.Data.Irq = prsres->Data.Irq;         /* structure copy other fields */
  310     resbuf.Data.Irq.NumberOfInterrupts = 1;
  311     resbuf.Data.Irq.Interrupts[0] = prsres->Data.Irq.Interrupts[0];     /* just take first... */
  312     if (ACPI_FAILURE(status = acpi_AppendBufferResource(&crsbuf, &resbuf))) {
  313         device_printf(pcib, "couldn't route interrupt %d via %s, interrupt resource build failed - %s\n",
  314                       prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
  315         goto out;
  316     }
  317     if (ACPI_FAILURE(status = AcpiSetCurrentResources(lnkdev, &crsbuf))) {
  318         device_printf(pcib, "couldn't route interrupt %d via %s - %s\n",
  319                       prsres->Data.Irq.Interrupts[0], acpi_name(lnkdev), AcpiFormatException(status));
  320         goto out;
  321     }
  322     
  323     /* successful, return the interrupt we just routed */
  324     device_printf(pcib, "slot %d INT%c routed to irq %d via %s\n", 
  325         pci_get_slot(dev), 'A' + pin, prsres->Data.Irq.Interrupts[0],
  326         acpi_name(lnkdev));
  327     interrupt = prsres->Data.Irq.Interrupts[0];
  328 
  329  out:
  330     if (crsbuf.Pointer != NULL)
  331         AcpiOsFree(crsbuf.Pointer);
  332     if (prsbuf.Pointer != NULL)
  333         AcpiOsFree(prsbuf.Pointer);
  334 
  335     /* XXX APIC_IO interrupt mapping? */
  336     return_VALUE(interrupt);
  337 }
  338 

Cache object: ef53d2ccaf46bc14b747b32ffca52e63


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