diff options
-rw-r--r-- | arch/x86/Kconfig | 9 | ||||
-rw-r--r-- | arch/x86/include/asm/acpi_table.h | 393 | ||||
-rw-r--r-- | arch/x86/lib/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/lib/acpi_table.c | 436 | ||||
-rw-r--r-- | arch/x86/lib/tables.c | 5 | ||||
-rw-r--r-- | scripts/Makefile.lib | 11 |
6 files changed, 855 insertions, 0 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 273f08f52dd..5e42d7d6a73 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -349,6 +349,15 @@ config GENERATE_MP_TABLE multiprocessing as well as symmetric I/O interrupt handling with the local APIC and I/O APIC. +config GENERATE_ACPI_TABLE + bool "Generate an ACPI (Advanced Configuration and Power Interface) table" + default n + help + The Advanced Configuration and Power Interface (ACPI) specification + provides an open standard for device configuration and management + by the operating system. It defines platform-independent interfaces + for configuration and power management monitoring. + endmenu config MAX_PIRQ_LINKS diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h new file mode 100644 index 00000000000..0853248f807 --- /dev/null +++ b/arch/x86/include/asm/acpi_table.h @@ -0,0 +1,393 @@ +/* + * Based on acpi.c from coreboot + * + * Copyright (C) 2015, Saket Sinha <saket.sinha89@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <asm/post.h> +#include <linux/string.h> + +#define RSDP_SIG "RSD PTR " /* RSDT pointer signature */ +#define ACPI_TABLE_CREATOR "UBOOT " /* Must be 8 bytes long! */ +#define OEM_ID "UBOOT " /* Must be 6 bytes long! */ +#define ASLC "INTL" /* Must be 4 bytes long! */ + +#define OEM_REVISION 42 +#define ASL_COMPILER_REVISION 42 + +/* IO ports to generate SMIs */ +#define APM_CNT 0xb2 +#define APM_CNT_CST_CONTROL 0x85 +#define APM_CNT_PST_CONTROL 0x80 +#define APM_CNT_ACPI_DISABLE 0x1e +#define APM_CNT_ACPI_ENABLE 0xe1 +#define APM_CNT_MBI_UPDATE 0xeb +#define APM_CNT_GNVS_UPDATE 0xea +#define APM_CNT_FINALIZE 0xcb +#define APM_CNT_LEGACY 0xcc +#define APM_ST 0xb3 + +/* Multiple Processor Interrupts */ +#define MP_IRQ_POLARITY_DEFAULT 0x0 +#define MP_IRQ_POLARITY_HIGH 0x1 +#define MP_IRQ_POLARITY_LOW 0x3 +#define MP_IRQ_POLARITY_MASK 0x3 +#define MP_IRQ_TRIGGER_DEFAULT 0x0 +#define MP_IRQ_TRIGGER_EDGE 0x4 +#define MP_IRQ_TRIGGER_LEVEL 0xc +#define MP_IRQ_TRIGGER_MASK 0xc + +/* + * Interrupt assigned for SCI in order to + * create the ACPI MADT IRQ override entry + */ +#define ACTL 0x00 +#define SCIS_MASK 0x07 +#define SCIS_IRQ9 0x00 +#define SCIS_IRQ10 0x01 +#define SCIS_IRQ11 0x02 +#define SCIS_IRQ20 0x04 +#define SCIS_IRQ21 0x05 +#define SCIS_IRQ22 0x06 +#define SCIS_IRQ23 0x07 + +#define ACPI_REV_ACPI_1_0 1 +#define ACPI_REV_ACPI_2_0 1 +#define ACPI_REV_ACPI_3_0 2 +#define ACPI_REV_ACPI_4_0 3 +#define ACPI_REV_ACPI_5_0 5 + +#define ACPI_RSDP_REV_ACPI_1_0 0 +#define ACPI_RSDP_REV_ACPI_2_0 2 + +typedef struct acpi_gen_regaddr { + u8 space_id; /* Address space ID */ + u8 bit_width; /* Register size in bits */ + u8 bit_offset; /* Register bit offset */ + union { + /* Reserved in ACPI 2.0 - 2.0b */ + u8 resv; + /* Access size in ACPI 2.0c/3.0/4.0/5.0 */ + u8 access_size; + }; + u32 addrl; /* Register address, low 32 bits */ + u32 addrh; /* Register address, high 32 bits */ +} acpi_addr_t; + + +/* + * RSDP (Root System Description Pointer) + * Note: ACPI 1.0 didn't have length, xsdt_address, and ext_checksum + */ +struct acpi_rsdp { + char signature[8]; /* RSDP signature */ + u8 checksum; /* Checksum of the first 20 bytes */ + char oem_id[6]; /* OEM ID */ + u8 revision; /* 0 for ACPI 1.0, 2 for ACPI 2.0/3.0/4.0 */ + u32 rsdt_address; /* Physical address of RSDT (32 bits) */ + u32 length; /* Total RSDP length (incl. extended part) */ + u64 xsdt_address; /* Physical address of XSDT (64 bits) */ + u8 ext_checksum; /* Checksum of the whole table */ + u8 reserved[3]; +}; + +enum acpi_address_space_type { + ACPI_ADDRESS_SPACE_MEMORY = 0, /* System memory */ + ACPI_ADDRESS_SPACE_IO, /* System I/O */ + ACPI_ADDRESS_SPACE_PCI, /* PCI config space */ + ACPI_ADDRESS_SPACE_EC, /* Embedded controller */ + ACPI_ADDRESS_SPACE_SMBUS, /* SMBus */ + ACPI_ADDRESS_SPACE_PCC = 0x0a, /* Platform Comm. Channel */ + ACPI_ADDRESS_SPACE_FIXED = 0x7f /* Functional fixed hardware */ +}; + +/* functional fixed hardware */ +#define ACPI_FFIXEDHW_VENDOR_INTEL 1 /* Intel */ +#define ACPI_FFIXEDHW_CLASS_HLT 0 /* C1 Halt */ +#define ACPI_FFIXEDHW_CLASS_IO_HLT 1 /* C1 I/O then Halt */ +#define ACPI_FFIXEDHW_CLASS_MWAIT 2 /* MWAIT Native C-state */ +#define ACPI_FFIXEDHW_FLAG_HW_COORD 1 /* Hardware Coordination bit */ +#define ACPI_FFIXEDHW_FLAG_BM_STS 2 /* BM_STS avoidance bit */ + +/* Access size definitions for Generic address structure */ +enum acpi_address_space_size { + ACPI_ACCESS_SIZE_UNDEFINED = 0, /* Undefined (legacy reasons) */ + ACPI_ACCESS_SIZE_BYTE_ACCESS = 1, + ACPI_ACCESS_SIZE_WORD_ACCESS = 2, + ACPI_ACCESS_SIZE_DWORD_ACCESS = 3, + ACPI_ACCESS_SIZE_QWORD_ACCESS = 4 +}; + +/* Generic ACPI header, provided by (almost) all tables */ +typedef struct acpi_table_header { + char signature[4]; /* ACPI signature (4 ASCII characters) */ + u32 length; /* Table length in bytes (incl. header) */ + u8 revision; /* Table version (not ACPI version!) */ + volatile u8 checksum; /* To make sum of entire table == 0 */ + char oem_id[6]; /* OEM identification */ + char oem_table_id[8]; /* OEM table identification */ + u32 oem_revision; /* OEM revision number */ + char asl_compiler_id[4]; /* ASL compiler vendor ID */ + u32 asl_compiler_revision; /* ASL compiler revision number */ +} acpi_header_t; + +/* A maximum number of 32 ACPI tables ought to be enough for now */ +#define MAX_ACPI_TABLES 32 + +/* RSDT (Root System Description Table) */ +struct acpi_rsdt { + struct acpi_table_header header; + u32 entry[MAX_ACPI_TABLES]; +}; + +/* XSDT (Extended System Description Table) */ +struct acpi_xsdt { + struct acpi_table_header header; + u64 entry[MAX_ACPI_TABLES]; +}; + +/* MCFG (PCI Express MMIO config space BAR description table) */ +struct acpi_mcfg { + struct acpi_table_header header; + u8 reserved[8]; +}; + +struct acpi_mcfg_mmconfig { + u32 base_address; + u32 base_reserved; + u16 pci_segment_group_number; + u8 start_bus_number; + u8 end_bus_number; + u8 reserved[4]; +}; + +/* MADT (Multiple APIC Description Table) */ +struct acpi_madt { + struct acpi_table_header header; + u32 lapic_addr; /* Local APIC address */ + u32 flags; /* Multiple APIC flags */ +} acpi_madt_t; + +enum dev_scope_type { + SCOPE_PCI_ENDPOINT = 1, + SCOPE_PCI_SUB = 2, + SCOPE_IOAPIC = 3, + SCOPE_MSI_HPET = 4 +}; + +typedef struct dev_scope { + u8 type; + u8 length; + u8 reserved[2]; + u8 enumeration; + u8 start_bus; + struct { + u8 dev; + u8 fn; + } path[0]; +} __packed dev_scope_t; + +/* MADT: APIC Structure Type*/ +enum acpi_apic_types { + LOCALAPIC = 0, /* Processor local APIC */ + IOAPIC, /* I/O APIC */ + IRQSOURCEOVERRIDE, /* Interrupt source override */ + NMITYPE, /* NMI source */ + LOCALNMITYPE, /* Local APIC NMI */ + LAPICADDRESSOVERRIDE, /* Local APIC address override */ + IOSAPIC, /* I/O SAPIC */ + LOCALSAPIC, /* Local SAPIC */ + PLATFORMIRQSOURCES, /* Platform interrupt sources */ + LOCALX2SAPIC, /* Processor local x2APIC */ + LOCALX2APICNMI, /* Local x2APIC NMI */ +}; + +/* MADT: Processor Local APIC Structure */ +struct acpi_madt_lapic { + u8 type; /* Type (0) */ + u8 length; /* Length in bytes (8) */ + u8 processor_id; /* ACPI processor ID */ + u8 apic_id; /* Local APIC ID */ + u32 flags; /* Local APIC flags */ +}; + +#define LOCAL_APIC_FLAG_ENABLED (1 << 0) +/* bits 1-31: reserved */ +#define PCAT_COMPAT (1 << 0) +/* bits 1-31: reserved */ + +/* MADT: Local APIC NMI Structure */ +struct acpi_madt_lapic_nmi { + u8 type; /* Type (4) */ + u8 length; /* Length in bytes (6) */ + u8 processor_id; /* ACPI processor ID */ + u16 flags; /* MPS INTI flags */ + u8 lint; /* Local APIC LINT# */ +}; + +/* MADT: I/O APIC Structure */ +struct acpi_madt_ioapic { + u8 type; /* Type (1) */ + u8 length; /* Length in bytes (12) */ + u8 ioapic_id; /* I/O APIC ID */ + u8 reserved; + u32 ioapic_addr; /* I/O APIC address */ + u32 gsi_base; /* Global system interrupt base */ +}; + +/* MADT: Interrupt Source Override Structure */ +struct acpi_madt_irqoverride { + u8 type; /* Type (2) */ + u8 length; /* Length in bytes (10) */ + u8 bus; /* ISA (0) */ + u8 source; /* Bus-relative int. source (IRQ) */ + u32 gsirq; /* Global system interrupt */ + u16 flags; /* MPS INTI flags */ +}; + +/* FADT (Fixed ACPI Description Table) */ +struct __packed acpi_fadt { + struct acpi_table_header header; + u32 firmware_ctrl; + u32 dsdt; + u8 model; + u8 preferred_pm_profile; + u16 sci_int; + u32 smi_cmd; + u8 acpi_enable; + u8 acpi_disable; + u8 s4bios_req; + u8 pstate_cnt; + u32 pm1a_evt_blk; + u32 pm1b_evt_blk; + u32 pm1a_cnt_blk; + u32 pm1b_cnt_blk; + u32 pm2_cnt_blk; + u32 pm_tmr_blk; + u32 gpe0_blk; + u32 gpe1_blk; + u8 pm1_evt_len; + u8 pm1_cnt_len; + u8 pm2_cnt_len; + u8 pm_tmr_len; + u8 gpe0_blk_len; + u8 gpe1_blk_len; + u8 gpe1_base; + u8 cst_cnt; + u16 p_lvl2_lat; + u16 p_lvl3_lat; + u16 flush_size; + u16 flush_stride; + u8 duty_offset; + u8 duty_width; + u8 day_alrm; + u8 mon_alrm; + u8 century; + u16 iapc_boot_arch; + u8 res2; + u32 flags; + struct acpi_gen_regaddr reset_reg; + u8 reset_value; + u8 res3; + u8 res4; + u8 res5; + u32 x_firmware_ctl_l; + u32 x_firmware_ctl_h; + u32 x_dsdt_l; + u32 x_dsdt_h; + struct acpi_gen_regaddr x_pm1a_evt_blk; + struct acpi_gen_regaddr x_pm1b_evt_blk; + struct acpi_gen_regaddr x_pm1a_cnt_blk; + struct acpi_gen_regaddr x_pm1b_cnt_blk; + struct acpi_gen_regaddr x_pm2_cnt_blk; + struct acpi_gen_regaddr x_pm_tmr_blk; + struct acpi_gen_regaddr x_gpe0_blk; + struct acpi_gen_regaddr x_gpe1_blk; +}; + +/* Flags for p_lvl2_lat and p_lvl3_lat */ +#define ACPI_FADT_C2_NOT_SUPPORTED 101 +#define ACPI_FADT_C3_NOT_SUPPORTED 1001 + +/* FADT Feature Flags */ +#define ACPI_FADT_WBINVD (1 << 0) +#define ACPI_FADT_WBINVD_FLUSH (1 << 1) +#define ACPI_FADT_C1_SUPPORTED (1 << 2) +#define ACPI_FADT_C2_MP_SUPPORTED (1 << 3) +#define ACPI_FADT_POWER_BUTTON (1 << 4) +#define ACPI_FADT_SLEEP_BUTTON (1 << 5) +#define ACPI_FADT_FIXED_RTC (1 << 6) +#define ACPI_FADT_S4_RTC_WAKE (1 << 7) +#define ACPI_FADT_32BIT_TIMER (1 << 8) +#define ACPI_FADT_DOCKING_SUPPORTED (1 << 9) +#define ACPI_FADT_RESET_REGISTER (1 << 10) +#define ACPI_FADT_SEALED_CASE (1 << 11) +#define ACPI_FADT_HEADLESS (1 << 12) +#define ACPI_FADT_SLEEP_TYPE (1 << 13) +#define ACPI_FADT_PCI_EXPRESS_WAKE (1 << 14) +#define ACPI_FADT_PLATFORM_CLOCK (1 << 15) +#define ACPI_FADT_S4_RTC_VALID (1 << 16) +#define ACPI_FADT_REMOTE_POWER_ON (1 << 17) +#define ACPI_FADT_APIC_CLUSTER (1 << 18) +#define ACPI_FADT_APIC_PHYSICAL (1 << 19) +/* Bits 20-31: reserved ACPI 3.0 & 4.0 */ +#define ACPI_FADT_HW_REDUCED_ACPI (1 << 20) +#define ACPI_FADT_LOW_PWR_IDLE_S0 (1 << 21) +/* bits 22-31: reserved ACPI 5.0 */ + +/* FADT Boot Architecture Flags */ +#define ACPI_FADT_LEGACY_DEVICES (1 << 0) +#define ACPI_FADT_8042 (1 << 1) +#define ACPI_FADT_VGA_NOT_PRESENT (1 << 2) +#define ACPI_FADT_MSI_NOT_SUPPORTED (1 << 3) +#define ACPI_FADT_NO_PCIE_ASPM_CONTROL (1 << 4) +/* No legacy devices (including 8042) */ +#define ACPI_FADT_LEGACY_FREE 0x00 + +/* FADT Preferred Power Management Profile */ +#define PM_UNSPECIFIED 0 +#define PM_DESKTOP 1 +#define PM_MOBILE 2 +#define PM_WORKSTATION 3 +#define PM_ENTERPRISE_SERVER 4 +#define PM_SOHO_SERVER 5 +#define PM_APPLIANCE_PC 6 +#define PM_PERFORMANCE_SERVER 7 +#define PM_TABLET 8 /* ACPI 5.0 */ + +/* FACS (Firmware ACPI Control Structure) */ +struct acpi_facs { + char signature[4]; /* "FACS" */ + u32 length; /* Length in bytes (>= 64) */ + u32 hardware_signature; /* Hardware signature */ + u32 firmware_waking_vector; /* Firmware waking vector */ + u32 global_lock; /* Global lock */ + u32 flags; /* FACS flags */ + u32 x_firmware_waking_vector_l; /* X FW waking vector, low */ + u32 x_firmware_waking_vector_h; /* X FW waking vector, high */ + u8 version; /* ACPI 4.0: 2 */ + u8 resv[31]; /* FIXME: 4.0: ospm_flags */ +}; + +/* FACS flags */ +#define ACPI_FACS_S4BIOS_F (1 << 0) +#define ACPI_FACS_64BIT_WAKE_F (1 << 1) +/* Bits 31..2: reserved */ + +/* These can be used by the target port */ + +unsigned long acpi_create_madt_lapics(unsigned long current); +int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id, u32 addr, + u32 gsi_base); +int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride, + u8 bus, u8 source, u32 gsirq, u16 flags); +unsigned long acpi_fill_madt(unsigned long current); +void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, + void *dsdt); +int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, u8 cpu, + u16 flags, u8 lint); +unsigned long write_acpi_tables(unsigned long start); diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index dcfe9ee85c5..6ecd6dbd9d2 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -30,6 +30,7 @@ obj-y += physmem.o obj-$(CONFIG_X86_RAMTEST) += ramtest.o obj-y += sfi.o obj-y += string.o +obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o obj-y += tables.o obj-$(CONFIG_SYS_X86_TSC_TIMER) += tsc_timer.o obj-$(CONFIG_CMD_ZBOOT) += zimage.o diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c new file mode 100644 index 00000000000..ab1db7ec26e --- /dev/null +++ b/arch/x86/lib/acpi_table.c @@ -0,0 +1,436 @@ +/* + * Based on acpi.c from coreboot + * + * Copyright (C) 2015, Saket Sinha <saket.sinha89@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <dm/uclass-internal.h> +#include <dm/lists.h> +#include <asm/acpi_table.h> +#include <asm/cpu.h> +#include <asm/ioapic.h> +#include <asm/lapic.h> +#include <asm/tables.h> +#include <asm/pci.h> + +/* + * IASL compiles the dsdt entries and + * writes the hex values to AmlCode array. + * CamelCase cannot be handled here. + */ +extern const unsigned char AmlCode[]; + +/** +* Add an ACPI table to the RSDT (and XSDT) structure, recalculate length +* and checksum. +*/ +static void acpi_add_table(struct acpi_rsdp *rsdp, void *table) +{ + int i, entries_num; + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt = NULL; + + /* The RSDT is mandatory while the XSDT is not */ + rsdt = (struct acpi_rsdt *)rsdp->rsdt_address; + + if (rsdp->xsdt_address) + xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_address); + + /* This should always be MAX_ACPI_TABLES */ + entries_num = ARRAY_SIZE(rsdt->entry); + + for (i = 0; i < entries_num; i++) { + if (rsdt->entry[i] == 0) + break; + } + + if (i >= entries_num) { + debug("ACPI: Error: too many tables.\n"); + return; + } + + /* Add table to the RSDT */ + rsdt->entry[i] = (u32)table; + + /* Fix RSDT length or the kernel will assume invalid entries */ + rsdt->header.length = sizeof(acpi_header_t) + (sizeof(u32) * (i + 1)); + + /* Re-calculate checksum */ + rsdt->header.checksum = 0; + rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, + rsdt->header.length); + + /* + * And now the same thing for the XSDT. We use the same index as for + * now we want the XSDT and RSDT to always be in sync in U-Boot + */ + if (xsdt) { + /* Add table to the XSDT */ + xsdt->entry[i] = (u64)(u32)table; + + /* Fix XSDT length */ + xsdt->header.length = sizeof(acpi_header_t) + + (sizeof(u64) * (i + 1)); + + /* Re-calculate checksum */ + xsdt->header.checksum = 0; + xsdt->header.checksum = table_compute_checksum((u8 *)xsdt, + xsdt->header.length); + } +} + +static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic, + u8 cpu, u8 apic) +{ + lapic->type = LOCALAPIC; /* Local APIC structure */ + lapic->length = sizeof(struct acpi_madt_lapic); + lapic->flags = LOCAL_APIC_FLAG_ENABLED; /* Processor/LAPIC enabled */ + lapic->processor_id = cpu; + lapic->apic_id = apic; + + return lapic->length; +} + +unsigned long acpi_create_madt_lapics(unsigned long current) +{ + struct udevice *dev; + + for (uclass_find_first_device(UCLASS_CPU, &dev); + dev; + uclass_find_next_device(&dev)) { + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + current += acpi_create_madt_lapic( + (struct acpi_madt_lapic *)current, + plat->cpu_id, plat->cpu_id); + } + return current; +} + +int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id, u32 addr, + u32 gsi_base) +{ + ioapic->type = IOAPIC; + ioapic->length = sizeof(struct acpi_madt_ioapic); + ioapic->reserved = 0x00; + ioapic->gsi_base = gsi_base; + ioapic->ioapic_id = id; + ioapic->ioapic_addr = addr; + + return ioapic->length; +} + +int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride, + u8 bus, u8 source, u32 gsirq, u16 flags) +{ + irqoverride->type = IRQSOURCEOVERRIDE; + irqoverride->length = sizeof(struct acpi_madt_irqoverride); + irqoverride->bus = bus; + irqoverride->source = source; + irqoverride->gsirq = gsirq; + irqoverride->flags = flags; + + return irqoverride->length; +} + +int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, + u8 cpu, u16 flags, u8 lint) +{ + lapic_nmi->type = LOCALNMITYPE; + lapic_nmi->length = sizeof(struct acpi_madt_lapic_nmi); + lapic_nmi->flags = flags; + lapic_nmi->processor_id = cpu; + lapic_nmi->lint = lint; + + return lapic_nmi->length; +} + +static void fill_header(acpi_header_t *header, char *signature, int length) +{ + memcpy(header->signature, signature, length); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); +} + +static void acpi_create_madt(struct acpi_madt *madt) +{ + acpi_header_t *header = &(madt->header); + unsigned long current = (unsigned long)madt + sizeof(struct acpi_madt); + + memset((void *)madt, 0, sizeof(struct acpi_madt)); + + /* Fill out header fields */ + fill_header(header, "APIC", 4); + header->length = sizeof(struct acpi_madt); + + /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + header->revision = ACPI_REV_ACPI_2_0; + + madt->lapic_addr = LAPIC_DEFAULT_BASE; + madt->flags = PCAT_COMPAT; + + current = acpi_fill_madt(current); + + /* (Re)calculate length and checksum */ + header->length = current - (unsigned long)madt; + + header->checksum = table_compute_checksum((void *)madt, header->length); +} + +static int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, + u32 base, u16 seg_nr, u8 start, u8 end) +{ + memset(mmconfig, 0, sizeof(*mmconfig)); + mmconfig->base_address = base; + mmconfig->base_reserved = 0; + mmconfig->pci_segment_group_number = seg_nr; + mmconfig->start_bus_number = start; + mmconfig->end_bus_number = end; + + return sizeof(struct acpi_mcfg_mmconfig); +} + +static unsigned long acpi_fill_mcfg(unsigned long current) +{ + current += acpi_create_mcfg_mmconfig + ((struct acpi_mcfg_mmconfig *)current, + CONFIG_PCIE_ECAM_BASE, 0x0, 0x0, 255); + + return current; +} + +/* MCFG is defined in the PCI Firmware Specification 3.0 */ +static void acpi_create_mcfg(struct acpi_mcfg *mcfg) +{ + acpi_header_t *header = &(mcfg->header); + unsigned long current = (unsigned long)mcfg + sizeof(struct acpi_mcfg); + + memset((void *)mcfg, 0, sizeof(struct acpi_mcfg)); + + /* Fill out header fields */ + fill_header(header, "MCFG", 4); + header->length = sizeof(struct acpi_mcfg); + + /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + header->revision = ACPI_REV_ACPI_2_0; + + current = acpi_fill_mcfg(current); + + /* (Re)calculate length and checksum */ + header->length = current - (unsigned long)mcfg; + header->checksum = table_compute_checksum((void *)mcfg, header->length); +} + +static void acpi_create_facs(struct acpi_facs *facs) +{ + memset((void *)facs, 0, sizeof(struct acpi_facs)); + + memcpy(facs->signature, "FACS", 4); + facs->length = sizeof(struct acpi_facs); + facs->hardware_signature = 0; + facs->firmware_waking_vector = 0; + facs->global_lock = 0; + facs->flags = 0; + facs->x_firmware_waking_vector_l = 0; + facs->x_firmware_waking_vector_h = 0; + facs->version = 1; /* ACPI 1.0: 0, ACPI 2.0/3.0: 1, ACPI 4.0: 2 */ +} + +static void acpi_write_rsdt(struct acpi_rsdt *rsdt) +{ + acpi_header_t *header = &(rsdt->header); + + /* Fill out header fields */ + fill_header(header, "RSDT", 4); + header->length = sizeof(struct acpi_rsdt); + + /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + header->revision = ACPI_REV_ACPI_2_0; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum((void *)rsdt, + sizeof(struct acpi_rsdt)); +} + +static void acpi_write_xsdt(struct acpi_xsdt *xsdt) +{ + acpi_header_t *header = &(xsdt->header); + + /* Fill out header fields */ + fill_header(header, "XSDT", 4); + header->length = sizeof(struct acpi_xsdt); + + /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + header->revision = ACPI_REV_ACPI_2_0; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum((void *)xsdt, + sizeof(struct acpi_xsdt)); +} + +static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, + struct acpi_xsdt *xsdt) +{ + memset(rsdp, 0, sizeof(struct acpi_rsdp)); + + memcpy(rsdp->signature, RSDP_SIG, 8); + memcpy(rsdp->oem_id, OEM_ID, 6); + + rsdp->length = sizeof(struct acpi_rsdp); + rsdp->rsdt_address = (u32)rsdt; + + /* + * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2 + * + * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2. + * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR + * revision 0) + */ + if (xsdt == NULL) { + rsdp->revision = ACPI_RSDP_REV_ACPI_1_0; + } else { + rsdp->xsdt_address = (u64)(u32)xsdt; + rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; + } + + /* Calculate checksums */ + rsdp->checksum = table_compute_checksum((void *)rsdp, 20); + rsdp->ext_checksum = table_compute_checksum((void *)rsdp, + sizeof(struct acpi_rsdp)); +} + +static void acpi_create_ssdt_generator(acpi_header_t *ssdt, + const char *oem_table_id) +{ + unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t); + + memset((void *)ssdt, 0, sizeof(acpi_header_t)); + + memcpy(&ssdt->signature, "SSDT", 4); + /* Access size in ACPI 2.0c/3.0/4.0/5.0 */ + ssdt->revision = ACPI_REV_ACPI_3_0; + memcpy(&ssdt->oem_id, OEM_ID, 6); + memcpy(&ssdt->oem_table_id, oem_table_id, 8); + ssdt->oem_revision = OEM_REVISION; + memcpy(&ssdt->asl_compiler_id, ASLC, 4); + ssdt->asl_compiler_revision = ASL_COMPILER_REVISION; + ssdt->length = sizeof(acpi_header_t); + + /* (Re)calculate length and checksum */ + ssdt->length = current - (unsigned long)ssdt; + ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); +} + +unsigned long write_acpi_tables(unsigned long start) +{ + unsigned long current; + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; + struct acpi_facs *facs; + acpi_header_t *dsdt; + struct acpi_fadt *fadt; + struct acpi_mcfg *mcfg; + struct acpi_madt *madt; + acpi_header_t *ssdt; + + current = start; + + /* Align ACPI tables to 16byte */ + current = ALIGN(current, 16); + + debug("ACPI: Writing ACPI tables at %lx.\n", start); + + /* We need at least an RSDP and an RSDT Table */ + rsdp = (struct acpi_rsdp *)current; + current += sizeof(struct acpi_rsdp); + current = ALIGN(current, 16); + rsdt = (struct acpi_rsdt *)current; + current += sizeof(struct acpi_rsdt); + current = ALIGN(current, 16); + xsdt = (struct acpi_xsdt *)current; + current += sizeof(struct acpi_xsdt); + current = ALIGN(current, 16); + + /* clear all table memory */ + memset((void *)start, 0, current - start); + + acpi_write_rsdp(rsdp, rsdt, xsdt); + acpi_write_rsdt(rsdt); + acpi_write_xsdt(xsdt); + + debug("ACPI: * FACS\n"); + facs = (struct acpi_facs *)current; + current += sizeof(struct acpi_facs); + current = ALIGN(current, 16); + + acpi_create_facs(facs); + + debug("ACPI: * DSDT\n"); + dsdt = (acpi_header_t *)current; + memcpy(dsdt, &AmlCode, sizeof(acpi_header_t)); + if (dsdt->length >= sizeof(acpi_header_t)) { + current += sizeof(acpi_header_t); + memcpy((char *)current, + (char *)&AmlCode + sizeof(acpi_header_t), + dsdt->length - sizeof(acpi_header_t)); + current += dsdt->length - sizeof(acpi_header_t); + + /* (Re)calculate length and checksum */ + dsdt->length = current - (unsigned long)dsdt; + dsdt->checksum = 0; + dsdt->checksum = table_compute_checksum((void *)dsdt, + dsdt->length); + } + current = ALIGN(current, 16); + + debug("ACPI: * FADT\n"); + fadt = (struct acpi_fadt *)current; + current += sizeof(struct acpi_fadt); + current = ALIGN(current, 16); + acpi_create_fadt(fadt, facs, dsdt); + acpi_add_table(rsdp, fadt); + + debug("ACPI: * MCFG\n"); + mcfg = (struct acpi_mcfg *)current; + acpi_create_mcfg(mcfg); + if (mcfg->header.length > sizeof(struct acpi_mcfg)) { + current += mcfg->header.length; + current = ALIGN(current, 16); + acpi_add_table(rsdp, mcfg); + } + + debug("ACPI: * MADT\n"); + madt = (struct acpi_madt *)current; + acpi_create_madt(madt); + if (madt->header.length > sizeof(struct acpi_madt)) { + current += madt->header.length; + acpi_add_table(rsdp, madt); + } + current = ALIGN(current, 16); + + debug("ACPI: * SSDT\n"); + ssdt = (acpi_header_t *)current; + acpi_create_ssdt_generator(ssdt, ACPI_TABLE_CREATOR); + if (ssdt->length > sizeof(acpi_header_t)) { + current += ssdt->length; + acpi_add_table(rsdp, ssdt); + current = ALIGN(current, 16); + } + + debug("current = %lx\n", current); + + debug("ACPI: done.\n"); + + return current; +} diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c index 75ffbc1b220..f15b2e2855a 100644 --- a/arch/x86/lib/tables.c +++ b/arch/x86/lib/tables.c @@ -8,6 +8,7 @@ #include <asm/sfi.h> #include <asm/mpspec.h> #include <asm/tables.h> +#include <asm/acpi_table.h> u8 table_compute_checksum(void *v, int len) { @@ -51,4 +52,8 @@ void write_tables(void) rom_table_end = write_mp_table(rom_table_end); rom_table_end = ALIGN(rom_table_end, 1024); #endif +#ifdef CONFIG_GENERATE_ACPI_TABLE + rom_table_end = write_acpi_tables(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); +#endif } diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 1c949fc0eb9..ed30bf5bb96 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -297,6 +297,17 @@ $(obj)/%.dtb: $(src)/%.dts FORCE dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) +# ACPI +# --------------------------------------------------------------------------- +quiet_cmd_acpi_c_asl= ASL $@ +cmd_acpi_c_asl= \ + $(CPP) -x assembler-with-cpp -P -o $<.tmp $<; \ + iasl -p $< -tc -va $<.tmp; \ + mv $(patsubst %.asl,%.hex,$<) $@ + +$(obj)/%.c: $(src)/%.asl + $(call cmd,acpi_c_asl) + # Bzip2 # --------------------------------------------------------------------------- |