diff options
Diffstat (limited to 'sys/dev/acpica/acpi_resource.c')
-rw-r--r-- | sys/dev/acpica/acpi_resource.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/sys/dev/acpica/acpi_resource.c b/sys/dev/acpica/acpi_resource.c new file mode 100644 index 0000000..20200fc --- /dev/null +++ b/sys/dev/acpica/acpi_resource.c @@ -0,0 +1,592 @@ +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * 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 <sys/param.h> +#include <sys/kernel.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include "acpi.h" + +#include <dev/acpica/acpivar.h> + +/* + * Hooks for the ACPI CA debugging infrastructure + */ +#define _COMPONENT ACPI_BUS +ACPI_MODULE_NAME("RESOURCE") + +/* + * Fetch a device's resources and associate them with the device. + * + * Note that it might be nice to also locate ACPI-specific resource items, such + * as GPE bits. + * + * We really need to split the resource-fetching code out from the + * resource-parsing code, since we may want to use the parsing + * code for _PRS someday. + */ +ACPI_STATUS +acpi_parse_resources(device_t dev, ACPI_HANDLE handle, struct acpi_parse_resource_set *set) +{ + ACPI_BUFFER buf; + ACPI_RESOURCE *res; + char *curr, *last; + ACPI_STATUS status; + void *context; + + ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); + + /* + * Special-case some devices that abuse _PRS/_CRS to mean + * something other than "I consume this resource". + * + * XXX do we really need this? It's only relevant once + * we start always-allocating these resources, and even + * then, the only special-cased device is likely to be + * the PCI interrupt link. + */ + + /* + * Fetch the device's current resources. + */ + buf.Length = ACPI_ALLOCATE_BUFFER; + if (ACPI_FAILURE((status = AcpiGetCurrentResources(handle, &buf)))) { + if (status != AE_NOT_FOUND) + printf("can't fetch resources for %s - %s\n", + acpi_name(handle), AcpiFormatException(status)); + return_ACPI_STATUS(status); + } + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "%s - got %ld bytes of resources\n", + acpi_name(handle), (long)buf.Length)); + set->set_init(dev, &context); + + /* + * Iterate through the resources + */ + curr = buf.Pointer; + last = (char *)buf.Pointer + buf.Length; + while (curr < last) { + res = (ACPI_RESOURCE *)curr; + curr += res->Length; + + /* + * Handle the individual resource types + */ + switch(res->Id) { + case ACPI_RSTYPE_END_TAG: + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "EndTag\n")); + curr = last; + break; + + case ACPI_RSTYPE_FIXED_IO: + if (res->Data.FixedIo.RangeLength <= 0) + break; + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedIo 0x%x/%d\n", + res->Data.FixedIo.BaseAddress, + res->Data.FixedIo.RangeLength)); + set->set_ioport(dev, context, + res->Data.FixedIo.BaseAddress, + res->Data.FixedIo.RangeLength); + break; + + case ACPI_RSTYPE_IO: + if (res->Data.Io.RangeLength <= 0) + break; + if (res->Data.Io.MinBaseAddress == res->Data.Io.MaxBaseAddress) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x/%d\n", + res->Data.Io.MinBaseAddress, + res->Data.Io.RangeLength)); + set->set_ioport(dev, context, + res->Data.Io.MinBaseAddress, + res->Data.Io.RangeLength); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Io 0x%x-0x%x/%d\n", + res->Data.Io.MinBaseAddress, + res->Data.Io.MaxBaseAddress, + res->Data.Io.RangeLength)); + set->set_iorange(dev, context, + res->Data.Io.MinBaseAddress, + res->Data.Io.MaxBaseAddress, + res->Data.Io.RangeLength, res->Data.Io.Alignment); + } + break; + + case ACPI_RSTYPE_FIXED_MEM32: + if (res->Data.FixedMemory32.RangeLength <= 0) + break; + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "FixedMemory32 0x%x/%d\n", + res->Data.FixedMemory32.RangeBaseAddress, + res->Data.FixedMemory32.RangeLength)); + set->set_memory(dev, context, res->Data.FixedMemory32.RangeBaseAddress, + res->Data.FixedMemory32.RangeLength); + break; + + case ACPI_RSTYPE_MEM32: + if (res->Data.Memory32.RangeLength <= 0) + break; + if (res->Data.Memory32.MinBaseAddress == res->Data.Memory32.MaxBaseAddress) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x/%d\n", + res->Data.Memory32.MinBaseAddress, + res->Data.Memory32.RangeLength)); + set->set_memory(dev, context, + res->Data.Memory32.MinBaseAddress, + res->Data.Memory32.RangeLength); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory32 0x%x-0x%x/%d\n", + res->Data.Memory32.MinBaseAddress, + res->Data.Memory32.MaxBaseAddress, + res->Data.Memory32.RangeLength)); + set->set_memoryrange(dev, context, + res->Data.Memory32.MinBaseAddress, + res->Data.Memory32.MaxBaseAddress, + res->Data.Memory32.RangeLength, + res->Data.Memory32.Alignment); + } + break; + + case ACPI_RSTYPE_MEM24: + if (res->Data.Memory24.RangeLength <= 0) + break; + if (res->Data.Memory24.MinBaseAddress == res->Data.Memory24.MaxBaseAddress) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x/%d\n", + res->Data.Memory24.MinBaseAddress, + res->Data.Memory24.RangeLength)); + set->set_memory(dev, context, res->Data.Memory24.MinBaseAddress, + res->Data.Memory24.RangeLength); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Memory24 0x%x-0x%x/%d\n", + res->Data.Memory24.MinBaseAddress, + res->Data.Memory24.MaxBaseAddress, + res->Data.Memory24.RangeLength)); + set->set_memoryrange(dev, context, + res->Data.Memory24.MinBaseAddress, + res->Data.Memory24.MaxBaseAddress, + res->Data.Memory24.RangeLength, + res->Data.Memory24.Alignment); + } + break; + + case ACPI_RSTYPE_IRQ: + /* + * from 1.0b 6.4.2 + * "This structure is repeated for each separate interrupt + * required" + */ + set->set_irq(dev, context, res->Data.Irq.Interrupts, + res->Data.Irq.NumberOfInterrupts); + break; + + case ACPI_RSTYPE_DMA: + /* + * from 1.0b 6.4.3 + * "This structure is repeated for each separate dma channel + * required" + */ + + set->set_drq(dev, context, res->Data.Dma.Channels, + res->Data.Dma.NumberOfChannels); + break; + + case ACPI_RSTYPE_START_DPF: + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "start dependant functions\n")); + set->set_start_dependant(dev, context, + res->Data.StartDpf.CompatibilityPriority); + break; + + case ACPI_RSTYPE_END_DPF: + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "end dependant functions\n")); + set->set_end_dependant(dev, context); + break; + + case ACPI_RSTYPE_ADDRESS32: + if (res->Data.Address32.AddressLength <= 0) + break; + if (res->Data.Address32.ProducerConsumer != ACPI_CONSUMER) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address32 %s producer\n", + (res->Data.Address32.ResourceType == ACPI_IO_RANGE) ? + "IO" : "Memory")); + break; + } + if ((res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE) || + (res->Data.Address32.ResourceType != ACPI_IO_RANGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, + "ignored Address32 for non-memory, non-I/O\n")); + break; + } + + if ((res->Data.Address32.MinAddressFixed == ACPI_ADDRESS_FIXED) && + (res->Data.Address32.MaxAddressFixed == ACPI_ADDRESS_FIXED)) { + if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x/%d\n", + res->Data.Address32.MinAddressRange, + res->Data.Address32.AddressLength)); + set->set_memory(dev, context, + res->Data.Address32.MinAddressRange, + res->Data.Address32.AddressLength); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x/%d\n", + res->Data.Address32.MinAddressRange, + res->Data.Address32.AddressLength)); + set->set_ioport(dev, context, + res->Data.Address32.MinAddressRange, + res->Data.Address32.AddressLength); + } + } else { + if (res->Data.Address32.ResourceType == ACPI_MEMORY_RANGE) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/Memory 0x%x-0x%x/%d\n", + res->Data.Address32.MinAddressRange, + res->Data.Address32.MaxAddressRange, + res->Data.Address32.AddressLength)); + set->set_memoryrange(dev, context, + res->Data.Address32.MinAddressRange, + res->Data.Address32.MaxAddressRange, + res->Data.Address32.AddressLength, + res->Data.Address32.Granularity); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address32/IO 0x%x-0x%x/%d\n", + res->Data.Address32.MinAddressRange, + res->Data.Address32.MaxAddressRange, + res->Data.Address32.AddressLength)); + set->set_iorange(dev, context, + res->Data.Address32.MinAddressRange, + res->Data.Address32.MaxAddressRange, + res->Data.Address32.AddressLength, + res->Data.Address32.Granularity); + } + } + break; + + case ACPI_RSTYPE_ADDRESS16: + if (res->Data.Address16.AddressLength <= 0) + break; + if (res->Data.Address16.ProducerConsumer != ACPI_CONSUMER) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "ignored Address16 %s producer\n", + (res->Data.Address16.ResourceType == ACPI_IO_RANGE) ? + "IO" : "Memory")); + break; + } + if ((res->Data.Address16.ResourceType != ACPI_MEMORY_RANGE) || + (res->Data.Address16.ResourceType != ACPI_IO_RANGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, + "ignored Address16 for non-memory, non-I/O\n")); + break; + } + + if ((res->Data.Address16.MinAddressFixed == ACPI_ADDRESS_FIXED) && + (res->Data.Address16.MaxAddressFixed == ACPI_ADDRESS_FIXED)) { + if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x/%d\n", + res->Data.Address16.MinAddressRange, + res->Data.Address16.AddressLength)); + set->set_memory(dev, context, + res->Data.Address16.MinAddressRange, + res->Data.Address16.AddressLength); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x/%d\n", + res->Data.Address16.MinAddressRange, + res->Data.Address16.AddressLength)); + set->set_ioport(dev, context, + res->Data.Address16.MinAddressRange, + res->Data.Address16.AddressLength); + } + } else { + if (res->Data.Address16.ResourceType == ACPI_MEMORY_RANGE) { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/Memory 0x%x-0x%x/%d\n", + res->Data.Address16.MinAddressRange, + res->Data.Address16.MaxAddressRange, + res->Data.Address16.AddressLength)); + set->set_memoryrange(dev, context, + res->Data.Address16.MinAddressRange, + res->Data.Address16.MaxAddressRange, + res->Data.Address16.AddressLength, + res->Data.Address16.Granularity); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "Address16/IO 0x%x-0x%x/%d\n", + res->Data.Address16.MinAddressRange, + res->Data.Address16.MaxAddressRange, + res->Data.Address16.AddressLength)); + set->set_iorange(dev, context, + res->Data.Address16.MinAddressRange, + res->Data.Address16.MaxAddressRange, + res->Data.Address16.AddressLength, + res->Data.Address16.Granularity); + } + } + break; + + case ACPI_RSTYPE_ADDRESS64: + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented Address64 resource\n")); + break; + + case ACPI_RSTYPE_EXT_IRQ: + /* XXX special handling? */ + set->set_irq(dev, context,res->Data.ExtendedIrq.Interrupts, + res->Data.ExtendedIrq.NumberOfInterrupts); + break; + + case ACPI_RSTYPE_VENDOR: + ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES, "unimplemented VendorSpecific resource\n")); + break; + default: + break; + } + } + AcpiOsFree(buf.Pointer); + set->set_done(dev, context); + return_ACPI_STATUS(AE_OK); +} + +/* + * Resource-set vectors used to attach _CRS-derived resources + * to an ACPI device. + */ +static void acpi_res_set_init(device_t dev, void **context); +static void acpi_res_set_done(device_t dev, void *context); +static void acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length); +static void acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high, + u_int32_t length, u_int32_t align); +static void acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length); +static void acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high, + u_int32_t length, u_int32_t align); +static void acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, + int count); +static void acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, + int count); +static void acpi_res_set_start_dependant(device_t dev, void *context, int preference); +static void acpi_res_set_end_dependant(device_t dev, void *context); + +struct acpi_parse_resource_set acpi_res_parse_set = { + acpi_res_set_init, + acpi_res_set_done, + acpi_res_set_ioport, + acpi_res_set_iorange, + acpi_res_set_memory, + acpi_res_set_memoryrange, + acpi_res_set_irq, + acpi_res_set_drq, + acpi_res_set_start_dependant, + acpi_res_set_end_dependant +}; + +struct acpi_res_context { + int ar_nio; + int ar_nmem; + int ar_nirq; + int ar_ndrq; +}; + +static void +acpi_res_set_init(device_t dev, void **context) +{ + struct acpi_res_context *cp; + + if ((cp = AcpiOsAllocate(sizeof(*cp))) != NULL) { + bzero(cp, sizeof(*cp)); + *context = cp; + } +} + +static void +acpi_res_set_done(device_t dev, void *context) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + AcpiOsFree(cp); +} + +static void +acpi_res_set_ioport(device_t dev, void *context, u_int32_t base, u_int32_t length) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + bus_set_resource(dev, SYS_RES_IOPORT, cp->ar_nio++, base, length); +} + +static void +acpi_res_set_iorange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + device_printf(dev, "I/O range not supported\n"); +} + +static void +acpi_res_set_memory(device_t dev, void *context, u_int32_t base, u_int32_t length) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + + bus_set_resource(dev, SYS_RES_MEMORY, cp->ar_nmem++, base, length); +} + +static void +acpi_res_set_memoryrange(device_t dev, void *context, u_int32_t low, u_int32_t high, u_int32_t length, u_int32_t align) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + device_printf(dev, "memory range not supported\n"); +} + +static void +acpi_res_set_irq(device_t dev, void *context, u_int32_t *irq, int count) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + if (irq == NULL) + return; + + /*This implements no resource relocation.*/ + if(count != 1) + return; + + bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1); +} + +static void +acpi_res_set_drq(device_t dev, void *context, u_int32_t *drq, int count) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + if (drq == NULL) + return; + + /*This implements no resource relocation.*/ + if(count != 1) + return; + + bus_set_resource(dev, SYS_RES_DRQ, cp->ar_ndrq++, *drq, 1); +} + +static void +acpi_res_set_start_dependant(device_t dev, void *context, int preference) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; + device_printf(dev, "dependant functions not supported\n"); +} + +static void +acpi_res_set_end_dependant(device_t dev, void *context) +{ + struct acpi_res_context *cp = (struct acpi_res_context *)context; + + if (cp == NULL) + return; +} + +/* + * Resource-owning placeholders. + * + * This code "owns" system resource objects that aren't + * otherwise useful to devices, and which shouldn't be + * considered "free". + * + * Note that some systems claim *all* of the physical address space + * with a PNP0C01 device, so we cannot correctly "own" system memory + * here (must be done in the SMAP handler on x86 systems, for + * example). + */ + +static int acpi_sysresource_probe(device_t dev); +static int acpi_sysresource_attach(device_t dev); + +static device_method_t acpi_sysresource_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, acpi_sysresource_probe), + DEVMETHOD(device_attach, acpi_sysresource_attach), + + {0, 0} +}; + +static driver_t acpi_sysresource_driver = { + "acpi_sysresource", + acpi_sysresource_methods, + 0, +}; + +static devclass_t acpi_sysresource_devclass; +DRIVER_MODULE(acpi_sysresource, acpi, acpi_sysresource_driver, acpi_sysresource_devclass, 0, 0); + +static int +acpi_sysresource_probe(device_t dev) +{ + if (acpi_disabled("sysresource")) + return(ENXIO); + if (acpi_MatchHid(dev, "PNP0C02")) { + device_set_desc(dev, "system resource"); + } else { + return(ENXIO); + } + device_quiet(dev); + return(-100); +} + +static int +acpi_sysresource_attach(device_t dev) +{ + struct resource *res; + int i, rid; + + /* + * Suck up all the resources that might have been assigned to us. + * Note that it's impossible to tell the difference between a + * resource that someone else has claimed, and one that doesn't + * exist. + */ + for (i = 0; i < 100; i++) { + rid = i; + res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, 0); + rid = i; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, 0); + rid = i; + res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE); + } + return(0); +} |