diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-18 15:51:48 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-18 15:51:48 -0800 |
commit | 507a03c1cba0e32309223d23d19a1bfc0916c140 (patch) | |
tree | 8da15f9c635733948a73bfe35cb50e1195702952 /drivers/acpi/apei/apei-base.c | |
parent | be405411f712489f2f780ab085e1069e8fb85f19 (diff) | |
parent | 79ba0db69c5887f1ad4ed51d58894e7e889084b0 (diff) |
Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
This includes initial support for the recently published ACPI 5.0 spec.
In particular, support for the "hardware-reduced" bit that eliminates
the dependency on legacy hardware.
APEI has patches resulting from testing on real hardware.
Plus other random fixes.
* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux: (52 commits)
acpi/apei/einj: Add extensions to EINJ from rev 5.0 of acpi spec
intel_idle: Split up and provide per CPU initialization func
ACPI processor: Remove unneeded variable passed by acpi_processor_hotadd_init V2
ACPI processor: Remove unneeded cpuidle_unregister_driver call
intel idle: Make idle driver more robust
intel_idle: Fix a cast to pointer from integer of different size warning in intel_idle
ACPI: kernel-parameters.txt : Add intel_idle.max_cstate
intel_idle: remove redundant local_irq_disable() call
ACPI processor: Fix error path, also remove sysdev link
ACPI: processor: fix acpi_get_cpuid for UP processor
intel_idle: fix API misuse
ACPI APEI: Convert atomicio routines
ACPI: Export interfaces for ioremapping/iounmapping ACPI registers
ACPI: Fix possible alignment issues with GAS 'address' references
ACPI, ia64: Use SRAT table rev to use 8bit or 16/32bit PXM fields (ia64)
ACPI, x86: Use SRAT table rev to use 8bit or 32bit PXM fields (x86/x86-64)
ACPI: Store SRAT table revision
ACPI, APEI, Resolve false conflict between ACPI NVS and APEI
ACPI, Record ACPI NVS regions
ACPI, APEI, EINJ, Refine the fix of resource conflict
...
Diffstat (limited to 'drivers/acpi/apei/apei-base.c')
-rw-r--r-- | drivers/acpi/apei/apei-base.c | 150 |
1 files changed, 139 insertions, 11 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 61540360d5c..e45350cb6ac 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -34,13 +34,13 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/acpi.h> +#include <linux/acpi_io.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/kref.h> #include <linux/rculist.h> #include <linux/interrupt.h> #include <linux/debugfs.h> -#include <acpi/atomicio.h> #include "apei-internal.h" @@ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) { int rc; - rc = acpi_atomic_read(val, &entry->register_region); + rc = apei_read(val, &entry->register_region); if (rc) return rc; *val >>= entry->register_region.bit_offset; @@ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) val <<= entry->register_region.bit_offset; if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { u64 valr = 0; - rc = acpi_atomic_read(&valr, &entry->register_region); + rc = apei_read(&valr, &entry->register_region); if (rc) return rc; valr &= ~(entry->mask << entry->register_region.bit_offset); val |= valr; } - rc = acpi_atomic_write(val, &entry->register_region); + rc = apei_write(val, &entry->register_region); return rc; } @@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - return acpi_pre_map_gar(&entry->register_region); + return acpi_os_map_generic_address(&entry->register_region); return 0; } @@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - acpi_post_unmap_gar(&entry->register_region); + acpi_os_unmap_generic_address(&entry->register_region); return 0; } @@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1, return 0; } +int apei_resources_add(struct apei_resources *resources, + unsigned long start, unsigned long size, + bool iomem) +{ + if (iomem) + return apei_res_add(&resources->iomem, start, size); + else + return apei_res_add(&resources->ioport, start, size); +} +EXPORT_SYMBOL_GPL(apei_resources_add); + /* * EINJ has two groups of GARs (EINJ table entry and trigger table * entry), so common resources are subtracted from the trigger table @@ -438,8 +449,19 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); +static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) +{ + struct apei_resources *resources = data; + return apei_res_add(&resources->iomem, start, size); +} + +static int apei_get_nvs_resources(struct apei_resources *resources) +{ + return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); +} + /* - * IO memory/port rersource management mechanism is used to check + * IO memory/port resource management mechanism is used to check * whether memory/port area used by GARs conflicts with normal memory * or IO memory/port of devices. */ @@ -448,21 +470,35 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; + struct apei_resources nvs_resources; int rc; rc = apei_resources_sub(resources, &apei_resources_all); if (rc) return rc; + /* + * Some firmware uses ACPI NVS region, that has been marked as + * busy, so exclude it from APEI resources to avoid false + * conflict. + */ + apei_resources_init(&nvs_resources); + rc = apei_get_nvs_resources(&nvs_resources); + if (rc) + goto res_fini; + rc = apei_resources_sub(resources, &nvs_resources); + if (rc) + goto res_fini; + rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { r = request_mem_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX - "Can not request iomem region <%016llx-%016llx> for GARs.\n", + "Can not request [mem %#010llx-%#010llx] for %s registers\n", (unsigned long long)res->start, - (unsigned long long)res->end); + (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_iomem; } @@ -472,9 +508,9 @@ int apei_resources_request(struct apei_resources *resources, r = request_region(res->start, res->end - res->start, desc); if (!r) { pr_err(APEI_PFX - "Can not request ioport region <%016llx-%016llx> for GARs.\n", + "Can not request [io %#06llx-%#06llx] for %s registers\n", (unsigned long long)res->start, - (unsigned long long)res->end); + (unsigned long long)res->end - 1, desc); res_bak = res; goto err_unmap_ioport; } @@ -500,6 +536,8 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } +res_fini: + apei_resources_fini(&nvs_resources); return rc; } EXPORT_SYMBOL_GPL(apei_resources_request); @@ -553,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) return 0; } +/* read GAR in interrupt (including NMI) or process context */ +int apei_read(u64 *val, struct acpi_generic_address *reg) +{ + int rc; + u64 address; + u32 tmp, width = reg->bit_width; + acpi_status status; + + rc = apei_check_gar(reg, &address); + if (rc) + return rc; + + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + + *val = 0; + switch(reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_read_memory((acpi_physical_address) + address, &tmp, width); + if (ACPI_FAILURE(status)) + return -EIO; + *val = tmp; + + if (reg->bit_width == 64) { + /* Read the top 32 bits */ + status = acpi_os_read_memory((acpi_physical_address) + (address + 4), &tmp, 32); + if (ACPI_FAILURE(status)) + return -EIO; + *val |= ((u64)tmp << 32); + } + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_read); + +/* write GAR in interrupt (including NMI) or process context */ +int apei_write(u64 val, struct acpi_generic_address *reg) +{ + int rc; + u64 address; + u32 width = reg->bit_width; + acpi_status status; + + rc = apei_check_gar(reg, &address); + if (rc) + return rc; + + if (width == 64) + width = 32; /* Break into two 32-bit transfers */ + + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + status = acpi_os_write_memory((acpi_physical_address) + address, ACPI_LODWORD(val), + width); + if (ACPI_FAILURE(status)) + return -EIO; + + if (reg->bit_width == 64) { + status = acpi_os_write_memory((acpi_physical_address) + (address + 4), + ACPI_HIDWORD(val), 32); + if (ACPI_FAILURE(status)) + return -EIO; + } + break; + case ACPI_ADR_SPACE_SYSTEM_IO: + status = acpi_os_write_port(address, val, reg->bit_width); + if (ACPI_FAILURE(status)) + return -EIO; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(apei_write); + static int collect_res_callback(struct apei_exec_context *ctx, struct acpi_whea_header *entry, void *data) |