| #include "libcflat.h" |
| #include "acpi.h" |
| |
| #ifdef CONFIG_EFI |
| struct acpi_table_rsdp *efi_rsdp = NULL; |
| |
| void set_efi_rsdp(struct acpi_table_rsdp *rsdp) |
| { |
| efi_rsdp = rsdp; |
| } |
| |
| static struct acpi_table_rsdp *get_rsdp(void) |
| { |
| if (efi_rsdp == NULL) |
| printf("Can't find RSDP from UEFI, maybe set_efi_rsdp() was not called\n"); |
| |
| return efi_rsdp; |
| } |
| #else |
| static struct acpi_table_rsdp *get_rsdp(void) |
| { |
| struct acpi_table_rsdp *rsdp; |
| unsigned long addr; |
| |
| for (addr = 0xe0000; addr < 0x100000; addr += 16) { |
| rsdp = (void *)addr; |
| if (rsdp->signature == RSDP_SIGNATURE_8BYTE) |
| break; |
| } |
| |
| if (addr == 0x100000) |
| return NULL; |
| |
| return rsdp; |
| } |
| #endif /* CONFIG_EFI */ |
| |
| void *find_acpi_table_addr(u32 sig) |
| { |
| struct acpi_table_rsdt_rev1 *rsdt = NULL; |
| struct acpi_table_xsdt *xsdt = NULL; |
| struct acpi_table_rsdp *rsdp; |
| void *end; |
| int i; |
| |
| /* FACS is special... */ |
| if (sig == FACS_SIGNATURE) { |
| struct acpi_table_fadt *fadt; |
| |
| fadt = find_acpi_table_addr(FACP_SIGNATURE); |
| if (!fadt) |
| return NULL; |
| return (void *)(ulong) fadt->firmware_ctrl; |
| } |
| |
| rsdp = get_rsdp(); |
| if (rsdp == NULL) { |
| printf("Can't find RSDP\n"); |
| return NULL; |
| } |
| |
| if (sig == RSDP_SIGNATURE) |
| return rsdp; |
| |
| rsdt = (void *)(ulong) rsdp->rsdt_physical_address; |
| if (rsdt && rsdt->signature != RSDT_SIGNATURE) |
| rsdt = NULL; |
| |
| if (sig == RSDT_SIGNATURE) |
| return rsdt; |
| |
| if (rsdp->revision >= 2) { |
| xsdt = (void *)(ulong) rsdp->xsdt_physical_address; |
| if (xsdt && xsdt->signature != XSDT_SIGNATURE) |
| xsdt = NULL; |
| } |
| |
| if (sig == XSDT_SIGNATURE) |
| return xsdt; |
| |
| /* |
| * When the system implements APCI 2.0 and above and XSDT is valid we |
| * have use XSDT to find other ACPI tables, otherwise, we use RSDT. |
| */ |
| if (xsdt) { |
| end = (void *)xsdt + xsdt->length; |
| for (i = 0; (void *)&xsdt->table_offset_entry[i] < end; i++) { |
| struct acpi_table *t = (void *)(ulong) xsdt->table_offset_entry[i]; |
| |
| if (t && t->signature == sig) |
| return t; |
| } |
| } else if (rsdt) { |
| end = (void *)rsdt + rsdt->length; |
| for (i = 0; (void *)&rsdt->table_offset_entry[i] < end; i++) { |
| struct acpi_table *t = (void *)(ulong) rsdt->table_offset_entry[i]; |
| |
| if (t && t->signature == sig) |
| return t; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| int acpi_table_parse_madt(enum acpi_madt_type mtype, acpi_table_handler handler) |
| { |
| struct acpi_table_madt *madt; |
| struct acpi_subtable_header *header; |
| void *end; |
| int count = 0; |
| |
| madt = find_acpi_table_addr(MADT_SIGNATURE); |
| assert(madt); |
| |
| header = (void *)(ulong) madt + sizeof(struct acpi_table_madt); |
| end = (void *)((ulong) madt + madt->length); |
| |
| while ((void *)header < end) { |
| if (header->type == mtype) { |
| handler(header); |
| count++; |
| } |
| |
| header = (void *)(ulong) header + header->length; |
| } |
| |
| return count; |
| } |