Index: dev/acpica/acpi_pcib.c =================================================================== RCS file: /home/ncvs/src/sys/dev/acpica/acpi_pcib.c,v retrieving revision 1.24 diff -u -r1.24 acpi_pcib.c --- dev/acpica/acpi_pcib.c 5 Sep 2002 17:08:35 -0000 1.24 +++ dev/acpica/acpi_pcib.c 10 Sep 2002 07:41:59 -0000 @@ -48,6 +48,8 @@ #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("PCI") +extern int acpi_pci_link_config(device_t, ACPI_BUFFER *, int); /* XXX */ + int acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno) { @@ -98,6 +100,7 @@ * XXX It would be nice to defer this and count on the nexus getting it * after the first pass, but this does not seem to be reliable. */ + acpi_pci_link_config(dev, prt, busno); return_VALUE(bus_generic_attach(dev)); } Index: modules/acpi/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/acpi/Makefile,v retrieving revision 1.25 diff -u -r1.25 Makefile --- modules/acpi/Makefile 27 Aug 2002 13:39:31 -0000 1.25 +++ modules/acpi/Makefile 7 Sep 2002 17:06:53 -0000 @@ -39,6 +39,7 @@ SRCS+= acpi_ec.c acpi_lid.c acpi_pci.c acpi_pcib.c acpi_pcib_acpi.c SRCS+= acpi_pcib_pci.c acpi_powerres.c acpi_resource.c acpi_thermal.c SRCS+= acpi_timer.c acpica_support.c +SRCS+= acpi_pci_link.c SRCS+= OsdDebug.c SRCS+= OsdHardware.c OsdInterrupt.c OsdMemory.c OsdSchedule.c SRCS+= OsdStream.c OsdSynch.c OsdTable.c OsdEnvironment.c --- /dev/null Tue Sep 10 20:00:01 2002 +++ dev/acpica/acpi_pci_link.c Tue Sep 10 19:02:59 2002 @@ -0,0 +1,477 @@ +/*- + * Copyright (c) 2002 Mitsuru IWASAKI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "opt_acpi.h" +#include +#include +#include +#include +#include +#include + +#include "acpi.h" + +#include + +int acpi_pci_link_config(device_t, ACPI_BUFFER *, int); /* XXX */ + +/* + * Hooks for the ACPI CA debugging infrastructure + */ +#define _COMPONENT ACPI_BUS +ACPI_MODULE_NAME("PCI_LINK") + +#define MAX_INTERRUPTS 16 + +struct acpi_pci_link_entry { + TAILQ_ENTRY(acpi_pci_link_entry) links; + device_t pcidev; + ACPI_HANDLE handle; + int busno; + ACPI_PCI_ROUTING_TABLE prt; + UINT32 status; + ACPI_RESOURCE possible_resources; + UINT8 current_irq; + UINT8 number_of_interrupts; + UINT8 interrupts[MAX_INTERRUPTS];; +}; + +TAILQ_HEAD(pci_link_entries, acpi_pci_link_entry); +static struct pci_link_entries pci_link_entries; + +#define ACPI_STA_PRESENT 0x00000001 +#define ACPI_STA_ENABLE 0x00000002 +#define ACPI_STA_SHOWINUI 0x00000004 +#define ACPI_STA_FUNCTIONAL 0x00000008 + +static void +acpi_pci_link_dump(struct acpi_pci_link_entry *link) +{ + UINT8 i; + ACPI_RESOURCE_IRQ *Irq; + + if (link == NULL) { + return; + } + + Irq = NULL; + + printf("%s irq %3d: ", acpi_name(link->handle), link->current_irq); + + printf("["); + for (i = 0; i < link->number_of_interrupts; i++) { + printf("%3d", link->interrupts[i]); + } + printf("] "); + + switch (link->possible_resources.Id) { + case ACPI_RSTYPE_IRQ: + Irq = &link->possible_resources.Data.Irq; + + switch (Irq->ActiveHighLow) { + case ACPI_ACTIVE_HIGH: + printf("high,"); + break; + + case ACPI_ACTIVE_LOW: + printf("low,"); + break; + + default: + printf("unkown,"); + break; + } + + switch (Irq->EdgeLevel) { + case ACPI_EDGE_SENSITIVE: + printf("edge,"); + break; + + case ACPI_LEVEL_SENSITIVE: + printf("level,"); + break; + + default: + printf("unkown,"); + break; + } + + switch (Irq->SharedExclusive) { + case ACPI_EXCLUSIVE: + printf("exclusive"); + break; + + case ACPI_SHARED: + printf("sharable"); + break; + + default: + printf("unkown"); + break; + } + + break; + + case ACPI_RSTYPE_EXT_IRQ: + /* TBD */ + break; + } + + printf(" %d:%d:%d", link->busno, + (int)((link->prt.Address & 0xffff0000) >> 16), (int)link->prt.Pin); + + printf("\n"); +} + +static ACPI_STATUS +acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources, + UINT8 *number_of_interrupts, UINT8 interrupts[]) +{ + UINT8 count; + UINT8 i; + UINT32 NumberOfInterrupts; + UINT32 *Interrupts; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + *number_of_interrupts = 0; + NumberOfInterrupts = 0; + Interrupts = NULL; + + if (resources->Id == ACPI_RSTYPE_START_DPF) + resources = ACPI_NEXT_RESOURCE(resources); + + if (resources->Id != ACPI_RSTYPE_IRQ && + resources->Id != ACPI_RSTYPE_EXT_IRQ) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Resource is not an IRQ entry\n")); + return_ACPI_STATUS (AE_ERROR); + } + + switch (resources->Id) { + case ACPI_RSTYPE_IRQ: + NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts; + Interrupts = resources->Data.Irq.Interrupts; + break; + + case ACPI_RSTYPE_EXT_IRQ: + NumberOfInterrupts = resources->Data.ExtendedIrq.NumberOfInterrupts; + Interrupts = resources->Data.ExtendedIrq.Interrupts; + break; + + } + + if (NumberOfInterrupts == 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n")); + return_ACPI_STATUS(AE_ERROR); + } + + count = 0; + for (i = 0; i < NumberOfInterrupts; i++) { + if (i >= MAX_INTERRUPTS) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs %d\n", i)); + break; + } + + if (Interrupts[i] == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", + Interrupts[i])); + continue; + } + interrupts[i] = Interrupts[i]; + count++; + } + *number_of_interrupts = count; + + return_ACPI_STATUS (AE_OK); +} + +static ACPI_STATUS +acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq) +{ + ACPI_STATUS error; + ACPI_BUFFER buf; + ACPI_RESOURCE *resources; + UINT8 number_of_interrupts; + UINT8 interrupts[MAX_INTERRUPTS];; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + *irq = 0; + buf.Pointer = NULL; + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(error = AcpiGetCurrentResources(link->handle, &buf)) || + buf.Pointer == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "couldn't get PCI interrupt link device _CRS data - %s\n", + AcpiFormatException(error))); + return_ACPI_STATUS (AE_ERROR); + } + + resources = (ACPI_RESOURCE *) buf.Pointer; + + number_of_interrupts = 0; + bzero(interrupts, sizeof(interrupts)); + acpi_pci_link_get_irq_resources(resources, &number_of_interrupts, interrupts); + + AcpiOsFree(buf.Pointer); + + if (number_of_interrupts == 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "PCI interrupt link device _CRS data is corrupted\n")); + return_ACPI_STATUS (AE_ERROR); + } + + *irq = interrupts[0]; + + return_ACPI_STATUS (AE_OK); +} + +static ACPI_STATUS +acpi_pci_link_add(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno) +{ + ACPI_HANDLE handle; + ACPI_DEVICE_INFO devinfo; + ACPI_STATUS error; + ACPI_BUFFER buf; + ACPI_RESOURCE *resources; + struct acpi_pci_link_entry *link; + UINT8 number_of_interrupts; + UINT8 interrupts[MAX_INTERRUPTS];; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if ((prt == NULL) || (prt->Source == NULL) || (prt->Source[0] == '\0')) { + return_ACPI_STATUS (AE_ERROR); + } + + if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(pcidev), prt->Source, &handle))) { + return_ACPI_STATUS (AE_ERROR); + } + + if (ACPI_FAILURE(error = AcpiGetObjectInfo(handle, &devinfo))) { + return_ACPI_STATUS (AE_ERROR); + } + + if ((devinfo.Valid & ACPI_VALID_HID) == 0 || + strcmp(devinfo.HardwareId, "PNP0C0F") != 0) { + return_ACPI_STATUS (AE_ERROR); + } + + link = malloc(sizeof(struct acpi_pci_link_entry), M_TEMP, M_NOWAIT); + if (link == NULL) { + return_ACPI_STATUS (AE_ERROR); + } + + bzero(link, sizeof(struct acpi_pci_link_entry)); + TAILQ_INSERT_TAIL(&pci_link_entries, link, links); + + link->handle = handle; + link->busno = busno; + bcopy(prt, &link->prt, sizeof(link->prt)); + if (devinfo.Valid & ACPI_VALID_STA) { + link->status = devinfo.CurrentStatus; + } + + buf.Pointer = NULL; + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE(error = AcpiGetPossibleResources(handle, &buf)) || + buf.Pointer == NULL) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "couldn't get PCI interrupt link device _PRS data - %s\n", + AcpiFormatException(error))); + goto out; + } + + resources = (ACPI_RESOURCE *) buf.Pointer; + bcopy(resources, &link->possible_resources, sizeof(link->possible_resources)); + + number_of_interrupts = 0; + bzero(interrupts, sizeof(interrupts)); + acpi_pci_link_get_irq_resources(resources, &number_of_interrupts, interrupts); + link->number_of_interrupts = number_of_interrupts; + bcopy(interrupts, link->interrupts, sizeof(link->interrupts)); + + acpi_pci_link_get_current_irq(link, &link->current_irq); + +out: + return_ACPI_STATUS (AE_OK); +} + +static int +acpi_pci_link_is_vaid_irq(struct acpi_pci_link_entry *link, UINT8 irq) +{ + UINT8 found; + UINT8 i; + + found = 0; + + if (irq == 0) { + return (0); + } + + for (i = 0; i < link->number_of_interrupts; i++) { + if (link->interrupts[i] == irq) { + found = 1; + break; + } + } + + if (!found) { + return (0); + } + + return (1); +} + +static ACPI_STATUS +acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq) +{ + ACPI_STATUS error; + ACPI_RESOURCE resbuf; + ACPI_BUFFER crsbuf; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + if (!acpi_pci_link_is_vaid_irq(link, irq)) { + return_ACPI_STATUS (AE_ERROR); + } + + bzero(&resbuf, sizeof(resbuf)); + crsbuf.Pointer = NULL; + resbuf.Id = ACPI_RSTYPE_IRQ; + resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ); + + switch (link->possible_resources.Id) { + case ACPI_RSTYPE_IRQ: + /* structure copy other fields */ + resbuf.Data.Irq = link->possible_resources.Data.Irq; + break; + + case ACPI_RSTYPE_EXT_IRQ: + /* XXX */ + resbuf.Data.Irq.EdgeLevel = ACPI_LEVEL_SENSITIVE; + resbuf.Data.Irq.ActiveHighLow = ACPI_ACTIVE_LOW; + resbuf.Data.Irq.SharedExclusive = ACPI_SHARED; + break; + + default: + return_ACPI_STATUS (AE_ERROR); + } + + resbuf.Data.Irq.NumberOfInterrupts = 1; + resbuf.Data.Irq.Interrupts[0] = irq; + + if (ACPI_FAILURE(error = acpi_AppendBufferResource(&crsbuf, &resbuf))) { + return_ACPI_STATUS (error); + } + if (ACPI_FAILURE(error = AcpiSetCurrentResources(link->handle, &crsbuf))) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "couldn't set PCI interrupt link device _SRS data - %s\n", + AcpiFormatException(error))); + return_ACPI_STATUS (error); + } + + acpi_pci_link_get_current_irq(link, &link->current_irq); + + return_ACPI_STATUS (AE_OK); +} + +int +acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno) +{ + struct acpi_pci_link_entry *link; + ACPI_PCI_ROUTING_TABLE *prt; + u_int8_t *prtp; + static int first_time =1; + + if (acpi_disabled("pci_link")) { + return (0); + } + + if (first_time) { + TAILQ_INIT(&pci_link_entries); + first_time = 0; + } + + prtp = prtbuf->Pointer; + if (prtp == NULL) /* didn't get routing table */ + return (-1); + + /* scan the PCI Routing Table */ + for (;;) { + prt = (ACPI_PCI_ROUTING_TABLE *)prtp; + + if (prt->Length == 0) /* end of table */ + break; + + acpi_pci_link_add(dev, prt, busno); + + /* skip to next entry */ + prtp += prt->Length; + } + + printf("---- initial configuration -------------------------\n"); + TAILQ_FOREACH(link, &pci_link_entries, links) { + acpi_pci_link_dump(link); + } + + TAILQ_FOREACH(link, &pci_link_entries, links) { + UINT8 irq; + char *irqstr, *op; + char pcilinkhint[32]; + + snprintf(pcilinkhint, sizeof(pcilinkhint), + "hw.acpi.pcilink.%d.%d.%d.irq", link->busno, + (int)((link->prt.Address & 0xffff0000) >> 16), + (int)link->prt.Pin); + irqstr = getenv(pcilinkhint); + if (irqstr == NULL) + continue; + irq = strtoul(irqstr, &op, 0); + if (*op != '\0') + continue; + + if (acpi_pci_link_is_vaid_irq(link, irq)) { + acpi_pci_link_set_irq(link, irq); + } + } + + TAILQ_FOREACH(link, &pci_link_entries, links) { + if (!acpi_pci_link_is_vaid_irq(link, link->current_irq)) { + acpi_pci_link_set_irq(link, link->interrupts[0]); + } + } + + printf("---- arbitrated configuration ----------------------\n"); + TAILQ_FOREACH(link, &pci_link_entries, links) { + acpi_pci_link_dump(link); + } + + return (0); +} +