From ac906a6d56b742e13f9e600c7c7879b323cfbf15 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:31 -0700 Subject: ACPI / NUMA: Use pr_fmt() instead of printk Just do some cleanups to replace printk with pr_fmt(). Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index d176e0e..bdb7622 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -18,6 +18,9 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ + +#define pr_fmt(fmt) "ACPI: " fmt + #include #include #include @@ -28,8 +31,6 @@ #include #include -#define PREFIX "ACPI: " - #define ACPI_NUMA 0x80000000 #define _COMPONENT ACPI_NUMA ACPI_MODULE_NAME("numa"); @@ -187,9 +188,8 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header) #endif /* ACPI_DEBUG_OUTPUT */ break; default: - printk(KERN_WARNING PREFIX - "Found unsupported SRAT entry (type = 0x%x)\n", - header->type); + pr_warn("Found unsupported SRAT entry (type = 0x%x)\n", + header->type); break; } } @@ -222,7 +222,7 @@ static int __init acpi_parse_slit(struct acpi_table_header *table) struct acpi_table_slit *slit = (struct acpi_table_slit *)table; if (!slit_valid(slit)) { - printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n"); + pr_info("SLIT table looks invalid. Not used.\n"); return -EINVAL; } acpi_numa_slit_init(slit); @@ -233,12 +233,9 @@ static int __init acpi_parse_slit(struct acpi_table_header *table) void __init __weak acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) { - printk(KERN_WARNING PREFIX - "Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id); - return; + pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id); } - static int __init acpi_parse_x2apic_affinity(struct acpi_subtable_header *header, const unsigned long end) -- cgit v0.10.2 From 3dda448189af1c55637628423487f607244fb669 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:32 -0700 Subject: ACPI / NUMA: Replace ACPI_DEBUG_PRINT() with pr_debug() ACPI_DEBUG_PRINT is a bit fragile in acpi/numa.c, the first thing is that component ACPI_NUMA(0x80000000) is not described in the Documentation/acpi/debug.txt, and even not defined in the struct acpi_dlayer acpi_debug_layers which we can not dynamically enable/disable it with /sys/modules/acpi/parameters/debug_layer. another thing is that ACPI_DEBUG_OUTPUT is controlled by ACPICA which not coordinate well with ACPI drivers. Replace ACPI_DEBUG_PRINT() with pr_debug() in this patch as pr_debug will do the same thing for debug purpose and it can make the code much cleaner, also remove the related code which not needed anymore if ACPI_DEBUG_PRINT() is gone. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index bdb7622..a089c39 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -31,10 +31,6 @@ #include #include -#define ACPI_NUMA 0x80000000 -#define _COMPONENT ACPI_NUMA -ACPI_MODULE_NAME("numa"); - static nodemask_t nodes_found_map = NODE_MASK_NONE; /* maps to convert between proximity domain and logical node ID */ @@ -129,64 +125,51 @@ EXPORT_SYMBOL(acpi_map_pxm_to_online_node); static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) { - - ACPI_FUNCTION_NAME("acpi_table_print_srat_entry"); - if (!header) return; switch (header->type) { - case ACPI_SRAT_TYPE_CPU_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_cpu_affinity *p = (struct acpi_srat_cpu_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", - p->apic_id, p->local_sapic_eid, - p->proximity_domain_lo, - (p->flags & ACPI_SRAT_CPU_ENABLED)? - "enabled" : "disabled")); + pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", + p->apic_id, p->local_sapic_eid, + p->proximity_domain_lo, + (p->flags & ACPI_SRAT_CPU_ENABLED) ? + "enabled" : "disabled"); } -#endif /* ACPI_DEBUG_OUTPUT */ break; case ACPI_SRAT_TYPE_MEMORY_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_mem_affinity *p = (struct acpi_srat_mem_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n", - (unsigned long)p->base_address, - (unsigned long)p->length, - p->proximity_domain, - (p->flags & ACPI_SRAT_MEM_ENABLED)? - "enabled" : "disabled", - (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)? - " hot-pluggable" : "", - (p->flags & ACPI_SRAT_MEM_NON_VOLATILE)? - " non-volatile" : "")); + pr_debug("SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n", + (unsigned long)p->base_address, + (unsigned long)p->length, + p->proximity_domain, + (p->flags & ACPI_SRAT_MEM_ENABLED) ? + "enabled" : "disabled", + (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ? + " hot-pluggable" : "", + (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ? + " non-volatile" : ""); } -#endif /* ACPI_DEBUG_OUTPUT */ break; case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY: -#ifdef ACPI_DEBUG_OUTPUT { struct acpi_srat_x2apic_cpu_affinity *p = (struct acpi_srat_x2apic_cpu_affinity *)header; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "SRAT Processor (x2apicid[0x%08x]) in" - " proximity domain %d %s\n", - p->apic_id, - p->proximity_domain, - (p->flags & ACPI_SRAT_CPU_ENABLED) ? - "enabled" : "disabled")); + pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n", + p->apic_id, + p->proximity_domain, + (p->flags & ACPI_SRAT_CPU_ENABLED) ? + "enabled" : "disabled"); } -#endif /* ACPI_DEBUG_OUTPUT */ break; + default: pr_warn("Found unsupported SRAT entry (type = 0x%x)\n", header->type); -- cgit v0.10.2 From 258cb74ba5d16265b0e96225c06bc41cd53b9e61 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:33 -0700 Subject: ACPI / NUMA: remove duplicate NULL check The argument "header" for acpi_table_print_srat_entry() is always checked before the function is called, it's duplicate to check it again, remove it. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index a089c39..6f1cad8 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -125,9 +125,6 @@ EXPORT_SYMBOL(acpi_map_pxm_to_online_node); static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) { - if (!header) - return; - switch (header->type) { case ACPI_SRAT_TYPE_CPU_AFFINITY: { -- cgit v0.10.2 From 312521d0543513f37f0badb4efc7bfed2d99672e Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 24 May 2016 15:35:34 -0700 Subject: ACPI / NUMA: Move acpi_numa_arch_fixup() to ia64 only Since acpi_numa_arch_fixup() is only used in arch ia64, move it there to make a generic interface easier. This avoids empty function stubs or some complex kconfig options for x86 and arm64. Signed-off-by: Robert Richter Reviewed-by: Hanjun Guo Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/ia64/include/asm/acpi.h b/arch/ia64/include/asm/acpi.h index aa0fdf1..a3d0211 100644 --- a/arch/ia64/include/asm/acpi.h +++ b/arch/ia64/include/asm/acpi.h @@ -140,6 +140,9 @@ static inline void per_cpu_scan_finalize(int min_cpus, int reserve_cpus) } } } + +extern void acpi_numa_fixup(void); + #endif /* CONFIG_ACPI_NUMA */ #endif /*__KERNEL__*/ diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index b1698bc..92b7bc9 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -524,7 +524,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) return 0; } -void __init acpi_numa_arch_fixup(void) +void __init acpi_numa_fixup(void) { int i, j, node_from, node_to; diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 2029a38..afddb3e 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -552,6 +552,7 @@ setup_arch (char **cmdline_p) early_acpi_boot_init(); # ifdef CONFIG_ACPI_NUMA acpi_numa_init(); + acpi_numa_fixup(); # ifdef CONFIG_ACPI_HOTPLUG_CPU prefill_possible_map(); # endif diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index b5f8218..90b6ed9 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -212,8 +212,6 @@ out_err: return -1; } -void __init acpi_numa_arch_fixup(void) {} - int __init x86_acpi_numa_init(void) { int ret; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 6f1cad8..fb2c0d6 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -324,8 +324,6 @@ int __init acpi_numa_init(void) /* SLIT: System Locality Information Table */ acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); - acpi_numa_arch_fixup(); - if (cnt < 0) return cnt; else if (!parsed_numa_memblks) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 288fac5..8010b26 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -237,7 +237,6 @@ void acpi_numa_slit_init (struct acpi_table_slit *slit); void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); -void acpi_numa_arch_fixup(void); #ifndef PHYS_CPUID_INVALID typedef u32 phys_cpuid_t; -- cgit v0.10.2 From 6525afdf53b39968f1a109b1ce1607ca6c98d300 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:35 -0700 Subject: ACPI / NUMA: move acpi_numa_slit_init() to drivers/acpi/numa.c Identical implementations of acpi_numa_slit_init() are used by both x86 and follow-on arm64 support. Move it to drivers/acpi/numa.c, and guard with CONFIG_X86 || CONFIG_ARM64 because ia64 has its own architecture specific implementation. No code change. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 90b6ed9..f242a11 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -42,33 +42,6 @@ static __init inline int srat_disabled(void) return acpi_numa < 0; } -/* - * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for - * I/O localities since SRAT does not list them. I/O localities are - * not supported at this point. - */ -void __init acpi_numa_slit_init(struct acpi_table_slit *slit) -{ - int i, j; - - for (i = 0; i < slit->locality_count; i++) { - const int from_node = pxm_to_node(i); - - if (from_node == NUMA_NO_NODE) - continue; - - for (j = 0; j < slit->locality_count; j++) { - const int to_node = pxm_to_node(j); - - if (to_node == NUMA_NO_NODE) - continue; - - numa_set_distance(from_node, to_node, - slit->entry[slit->locality_count * i + j]); - } - } -} - /* Callback for Proximity Domain -> x2APIC mapping */ void __init acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index fb2c0d6..7e0649d 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -197,6 +197,35 @@ static int __init slit_valid(struct acpi_table_slit *slit) return 1; } +#if defined(CONFIG_X86) || defined(CONFIG_ARM64) +/* + * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for + * I/O localities since SRAT does not list them. I/O localities are + * not supported at this point. + */ +void __init acpi_numa_slit_init(struct acpi_table_slit *slit) +{ + int i, j; + + for (i = 0; i < slit->locality_count; i++) { + const int from_node = pxm_to_node(i); + + if (from_node == NUMA_NO_NODE) + continue; + + for (j = 0; j < slit->locality_count; j++) { + const int to_node = pxm_to_node(j); + + if (to_node == NUMA_NO_NODE) + continue; + + numa_set_distance(from_node, to_node, + slit->entry[slit->locality_count * i + j]); + } + } +} +#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */ + static int __init acpi_parse_slit(struct acpi_table_header *table) { struct acpi_table_slit *slit = (struct acpi_table_slit *)table; -- cgit v0.10.2 From 8ccbbdaa2bc06f7b11fec7f44bf2501786a189ba Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:36 -0700 Subject: arm64, NUMA: rework numa_add_memblk() Rework numa_add_memblk() to update the parameter "u64 size" to "u64 end", this will make it consistent with x86 and simplifies the arm64 ACPI NUMA code to be added later. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Acked-by: Catalin Marinas Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c index 98dc104..6cb03f9 100644 --- a/arch/arm64/mm/numa.c +++ b/arch/arm64/mm/numa.c @@ -131,25 +131,25 @@ void __init early_map_cpu_to_node(unsigned int cpu, int nid) * numa_add_memblk - Set node id to memblk * @nid: NUMA node ID of the new memblk * @start: Start address of the new memblk - * @size: Size of the new memblk + * @end: End address of the new memblk * * RETURNS: * 0 on success, -errno on failure. */ -int __init numa_add_memblk(int nid, u64 start, u64 size) +int __init numa_add_memblk(int nid, u64 start, u64 end) { int ret; - ret = memblock_set_node(start, size, &memblock.memory, nid); + ret = memblock_set_node(start, (end - start), &memblock.memory, nid); if (ret < 0) { pr_err("NUMA: memblock [0x%llx - 0x%llx] failed to add on node %d\n", - start, (start + size - 1), nid); + start, (end - 1), nid); return ret; } node_set(nid, numa_nodes_parsed); pr_info("NUMA: Adding memblock [0x%llx - 0x%llx] on node %d\n", - start, (start + size - 1), nid); + start, (end - 1), nid); return ret; } @@ -367,7 +367,7 @@ static int __init dummy_numa_init(void) 0LLU, PFN_PHYS(max_pfn) - 1); for_each_memblock(memory, mblk) { - ret = numa_add_memblk(0, mblk->base, mblk->size); + ret = numa_add_memblk(0, mblk->base, mblk->base + mblk->size); if (!ret) continue; diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c index 0f2784b..ed5a097 100644 --- a/drivers/of/of_numa.c +++ b/drivers/of/of_numa.c @@ -91,8 +91,8 @@ static int __init of_numa_parse_memory_nodes(void) pr_debug("NUMA: base = %llx len = %llx, node = %u\n", rsrc.start, rsrc.end - rsrc.start + 1, nid); - r = numa_add_memblk(nid, rsrc.start, - rsrc.end - rsrc.start + 1); + + r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1); if (r) break; } -- cgit v0.10.2 From 34c333705238dcc82aa758ef1fbb002dbcd76414 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 24 May 2016 15:35:37 -0700 Subject: arm64, NUMA: Cleanup NUMA disabled messages As noted by Dennis Chen, we don't want to print "No NUMA configuration found" if NUMA was forced off from the command line. Change the type of numa_off to bool, and clean up printing code. Print "NUMA disabled" if forced off on command line and "No NUMA configuration found" if there was no firmware NUMA information. Signed-off-by: David Daney Acked-by: Catalin Marinas Reviewed-by: Dennis Chen Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c index 6cb03f9..1def1de 100644 --- a/arch/arm64/mm/numa.c +++ b/arch/arm64/mm/numa.c @@ -29,7 +29,7 @@ static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE }; static int numa_distance_cnt; static u8 *numa_distance; -static int numa_off; +static bool numa_off; static __init int numa_parse_early_param(char *opt) { @@ -37,7 +37,7 @@ static __init int numa_parse_early_param(char *opt) return -EINVAL; if (!strncmp(opt, "off", 3)) { pr_info("%s\n", "NUMA turned off"); - numa_off = 1; + numa_off = true; } return 0; } @@ -362,7 +362,10 @@ static int __init dummy_numa_init(void) int ret; struct memblock_region *mblk; - pr_info("%s\n", "No NUMA configuration found"); + if (numa_off) + pr_info("NUMA disabled\n"); /* Forced off on command line. */ + else + pr_info("No NUMA configuration found\n"); pr_info("NUMA: Faking a node at [mem %#018Lx-%#018Lx]\n", 0LLU, PFN_PHYS(max_pfn) - 1); @@ -375,7 +378,7 @@ static int __init dummy_numa_init(void) return ret; } - numa_off = 1; + numa_off = true; return 0; } -- cgit v0.10.2 From 2faeff1d507c21d262e8afca862243909970d567 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:38 -0700 Subject: x86 / ACPI / NUMA: cleanup acpi_numa_processor_affinity_init() Cleanup acpi_numa_processor_affinity_init() in preparation for its move to drivers/acpi/numa.c. It will be reused by arm64, this has no functional change. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index f242a11..9e2a833 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -26,11 +26,6 @@ int acpi_numa __initdata; -static __init int setup_node(int pxm) -{ - return acpi_map_pxm_to_node(pxm); -} - static __init void bad_srat(void) { printk(KERN_ERR "SRAT: SRAT not used.\n"); @@ -64,7 +59,7 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) pxm, apic_id); return; } - node = setup_node(pxm); + node = acpi_map_pxm_to_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); bad_srat(); @@ -100,7 +95,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) pxm = pa->proximity_domain_lo; if (acpi_srat_revision >= 2) pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8; - node = setup_node(pxm); + node = acpi_map_pxm_to_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm); bad_srat(); @@ -124,12 +119,6 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) pxm, apic_id, node); } -#ifdef CONFIG_MEMORY_HOTPLUG -static inline int save_add_info(void) {return 1;} -#else -static inline int save_add_info(void) {return 0;} -#endif - /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) @@ -145,7 +134,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) goto out_err; hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; - if (hotpluggable && !save_add_info()) + if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) goto out_err; start = ma->base_address; @@ -154,7 +143,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) if (acpi_srat_revision <= 1) pxm &= 0xff; - node = setup_node(pxm); + node = acpi_map_pxm_to_node(pxm); if (node < 0) { printk(KERN_ERR "SRAT: Too many proximity domains.\n"); goto out_err_bad_srat; @@ -182,7 +171,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) out_err_bad_srat: bad_srat(); out_err: - return -1; + return -EINVAL; } int __init x86_acpi_numa_init(void) -- cgit v0.10.2 From e84025e274e66986277e11f0dda03373e246ffad Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 24 May 2016 15:35:39 -0700 Subject: ACPI / NUMA: move bad_srat() and srat_disabled() to drivers/acpi/numa.c bad_srat() and srat_disabled() are shared by x86 and follow-on arm64 patches. Move them to drivers/acpi/numa.c in preparation for arm64 support. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter [david.daney@cavium.com moved definitions to drivers/acpi/numa.c] Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 94c18eb..65f1e95 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -145,7 +145,6 @@ static inline void disable_acpi(void) { } #define ARCH_HAS_POWER_INIT 1 #ifdef CONFIG_ACPI_NUMA -extern int acpi_numa; extern int x86_acpi_numa_init(void); #endif /* CONFIG_ACPI_NUMA */ diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index 9c086c5..968ac02 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -1,4 +1,5 @@ /* Common code for 32 and 64-bit NUMA */ +#include #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include #include "numa_internal.h" diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 9e2a833..4217071f 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -24,19 +24,6 @@ #include #include -int acpi_numa __initdata; - -static __init void bad_srat(void) -{ - printk(KERN_ERR "SRAT: SRAT not used.\n"); - acpi_numa = -1; -} - -static __init inline int srat_disabled(void) -{ - return acpi_numa < 0; -} - /* Callback for Proximity Domain -> x2APIC mapping */ void __init acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 7e0649d..af0c8d5 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -40,6 +40,7 @@ static int node_to_pxm_map[MAX_NUMNODES] = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; unsigned char acpi_srat_revision __initdata; +int acpi_numa __initdata; int pxm_to_node(int pxm) { @@ -197,6 +198,17 @@ static int __init slit_valid(struct acpi_table_slit *slit) return 1; } +void __init bad_srat(void) +{ + pr_err("SRAT: SRAT not used.\n"); + acpi_numa = -1; +} + +int __init srat_disabled(void) +{ + return acpi_numa < 0; +} + #if defined(CONFIG_X86) || defined(CONFIG_ARM64) /* * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for diff --git a/include/acpi/acpi_numa.h b/include/acpi/acpi_numa.h index 94a37cd..d4b7294 100644 --- a/include/acpi/acpi_numa.h +++ b/include/acpi/acpi_numa.h @@ -15,6 +15,10 @@ extern int pxm_to_node(int); extern int node_to_pxm(int); extern int acpi_map_pxm_to_node(int); extern unsigned char acpi_srat_revision; +extern int acpi_numa __initdata; + +extern void bad_srat(void); +extern int srat_disabled(void); #endif /* CONFIG_ACPI_NUMA */ #endif /* __ACP_NUMA_H */ -- cgit v0.10.2 From fb1f4181cef6ca15840164a5bd41f3dc4cb4f88d Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:40 -0700 Subject: ACPI / NUMA: remove unneeded acpi_numa=1 acpi_numa is default to 0, it's set to -1 when disable acpi numa or when a bad SRAT is parsed, and it's only consumed in srat_disabled() (compare it with 0) to continue parse the SRAT or not, so we don't need to set acpi_numa to 1 when we get a valid SRAT entry. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 4217071f..30460f5 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -59,7 +59,6 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) } set_apicid_to_node(apic_id, node); node_set(node, numa_nodes_parsed); - acpi_numa = 1; printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n", pxm, apic_id, node); } @@ -101,7 +100,6 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) set_apicid_to_node(apic_id, node); node_set(node, numa_nodes_parsed); - acpi_numa = 1; printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n", pxm, apic_id, node); } -- cgit v0.10.2 From 3770442e79380cf105c8e272622c565cf63524d6 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:41 -0700 Subject: ACPI / NUMA: Move acpi_numa_memory_affinity_init() to drivers/acpi/numa.c acpi_numa_memory_affinity_init() will be reused by arm64. Move it to drivers/acpi/numa.c to facilitate reuse. No code change. Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c index 30460f5..b1ecff4 100644 --- a/arch/x86/mm/srat.c +++ b/arch/x86/mm/srat.c @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include #include #include @@ -104,61 +102,6 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) pxm, apic_id, node); } -/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ -int __init -acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) -{ - u64 start, end; - u32 hotpluggable; - int node, pxm; - - if (srat_disabled()) - goto out_err; - if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) - goto out_err_bad_srat; - if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) - goto out_err; - hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; - if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) - goto out_err; - - start = ma->base_address; - end = start + ma->length; - pxm = ma->proximity_domain; - if (acpi_srat_revision <= 1) - pxm &= 0xff; - - node = acpi_map_pxm_to_node(pxm); - if (node < 0) { - printk(KERN_ERR "SRAT: Too many proximity domains.\n"); - goto out_err_bad_srat; - } - - if (numa_add_memblk(node, start, end) < 0) - goto out_err_bad_srat; - - node_set(node, numa_nodes_parsed); - - pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n", - node, pxm, - (unsigned long long) start, (unsigned long long) end - 1, - hotpluggable ? " hotplug" : "", - ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : ""); - - /* Mark hotplug range in memblock. */ - if (hotpluggable && memblock_mark_hotplug(start, ma->length)) - pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n", - (unsigned long long)start, (unsigned long long)end - 1); - - max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1)); - - return 0; -out_err_bad_srat: - bad_srat(); -out_err: - return -EINVAL; -} - int __init x86_acpi_numa_init(void) { int ret; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index af0c8d5..811f90a 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -236,6 +238,64 @@ void __init acpi_numa_slit_init(struct acpi_table_slit *slit) } } } + +/* + * Default callback for parsing of the Proximity Domain <-> Memory + * Area mappings + */ +int __init +acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) +{ + u64 start, end; + u32 hotpluggable; + int node, pxm; + + if (srat_disabled()) + goto out_err; + if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) + goto out_err_bad_srat; + if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) + goto out_err; + hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; + if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG)) + goto out_err; + + start = ma->base_address; + end = start + ma->length; + pxm = ma->proximity_domain; + if (acpi_srat_revision <= 1) + pxm &= 0xff; + + node = acpi_map_pxm_to_node(pxm); + if (node < 0) { + printk(KERN_ERR "SRAT: Too many proximity domains.\n"); + goto out_err_bad_srat; + } + + if (numa_add_memblk(node, start, end) < 0) + goto out_err_bad_srat; + + node_set(node, numa_nodes_parsed); + + pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n", + node, pxm, + (unsigned long long) start, (unsigned long long) end - 1, + hotpluggable ? " hotplug" : "", + ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : ""); + + /* Mark hotplug range in memblock. */ + if (hotpluggable && memblock_mark_hotplug(start, ma->length)) + pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n", + (unsigned long long)start, (unsigned long long)end - 1); + + max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1)); + + return 0; +out_err_bad_srat: + bad_srat(); +out_err: + return -EINVAL; +} #endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */ static int __init acpi_parse_slit(struct acpi_table_header *table) -- cgit v0.10.2 From e0af261a437db4f4b21478ac677ccca5fc0ec07e Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 24 May 2016 15:35:42 -0700 Subject: ACPI / NUMA: Improve SRAT error detection and add messages Loosely based on code from Robert Richter and Hanjun Guo. Improve out of range node detection as well as allow for Larger SRAT entities. Add printing of nice messages. Signed-off-by: David Daney Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 811f90a..fad6d28 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -252,8 +252,11 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) if (srat_disabled()) goto out_err; - if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) + if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) { + pr_err("SRAT: Unexpected header length: %d\n", + ma->header.length); goto out_err_bad_srat; + } if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0) goto out_err; hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE; @@ -267,13 +270,17 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma) pxm &= 0xff; node = acpi_map_pxm_to_node(pxm); - if (node < 0) { - printk(KERN_ERR "SRAT: Too many proximity domains.\n"); + if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { + pr_err("SRAT: Too many proximity domains.\n"); goto out_err_bad_srat; } - if (numa_add_memblk(node, start, end) < 0) + if (numa_add_memblk(node, start, end) < 0) { + pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n", + node, (unsigned long long) start, + (unsigned long long) end - 1); goto out_err_bad_srat; + } node_set(node, numa_nodes_parsed); -- cgit v0.10.2 From fb7c2bae8a479e388a1f664e32d6ddca55c7a249 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 24 May 2016 15:35:43 -0700 Subject: ACPI / processor: Add acpi_map_madt_entry() Follow-on arm64 ACPI/NUMA patches need to map MADT entries very early (before kmalloc is usable). Add acpi_map_madt_entry() which, indirectly, uses early_memremap()/early_memunmap() to access the table and parse out the mpidr. The existing implementation of map_madt_entry() is modified to take a pointer to the MADT as a parameter and the callers adjusted. Signed-off-by: David Daney Acked-by: Catalin Marinas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 33a38d6..9125d7d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -108,13 +108,12 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry, return -EINVAL; } -static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) +static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt, + int type, u32 acpi_id) { unsigned long madt_end, entry; phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */ - struct acpi_table_madt *madt; - madt = get_madt_table(); if (!madt) return phys_id; @@ -145,6 +144,25 @@ static phys_cpuid_t map_madt_entry(int type, u32 acpi_id) return phys_id; } +phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id) +{ + struct acpi_table_madt *madt = NULL; + acpi_size tbl_size; + phys_cpuid_t rv; + + acpi_get_table_with_size(ACPI_SIG_MADT, 0, + (struct acpi_table_header **)&madt, + &tbl_size); + if (!madt) + return PHYS_CPUID_INVALID; + + rv = map_madt_entry(madt, 1, acpi_id); + + early_acpi_os_unmap_memory(madt, tbl_size); + + return rv; +} + static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -185,7 +203,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) phys_id = map_mat_entry(handle, type, acpi_id); if (invalid_phys_cpuid(phys_id)) - phys_id = map_madt_entry(type, acpi_id); + phys_id = map_madt_entry(get_madt_table(), type, acpi_id); return phys_id; } diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 6f1805d..f473e66 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -309,6 +309,7 @@ static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) /* in processor_core.c */ phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); +phys_cpuid_t acpi_map_madt_entry(u32 acpi_id); int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id); int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id); -- cgit v0.10.2 From d8b47fca8c233642d1a20fa4025579ebc8be6f1e Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Tue, 24 May 2016 15:35:44 -0700 Subject: arm64, ACPI, NUMA: NUMA support based on SRAT and SLIT Introduce a new file to hold ACPI based NUMA information parsing from SRAT and SLIT. SRAT includes the CPU ACPI ID to Proximity Domain mappings and memory ranges to Proximity Domain mapping. SLIT has the information of inter node distances(relative number for access latency). Signed-off-by: Hanjun Guo Signed-off-by: Ganapatrao Kulkarni [rrichter@cavium.com Reworked for numa v10 series ] Signed-off-by: Robert Richter [david.daney@cavium.com reorderd and combinded with other patches in Hanjun Guo's original set, removed get_mpidr_in_madt() and use acpi_map_madt_entry() instead.] Signed-off-by: David Daney Reviewed-by: Catalin Marinas Tested-by: Dennis Chen Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index aee323b..4b13ecd 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -113,4 +113,12 @@ static inline const char *acpi_get_enable_method(int cpu) pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); #endif +#ifdef CONFIG_ACPI_NUMA +int arm64_acpi_numa_init(void); +int acpi_numa_get_nid(unsigned int cpu, u64 hwid); +#else +static inline int arm64_acpi_numa_init(void) { return -ENOSYS; } +static inline int acpi_numa_get_nid(unsigned int cpu, u64 hwid) { return NUMA_NO_NODE; } +#endif /* CONFIG_ACPI_NUMA */ + #endif /*_ASM_ACPI_H*/ diff --git a/arch/arm64/include/asm/numa.h b/arch/arm64/include/asm/numa.h index e9b4f29..600887e 100644 --- a/arch/arm64/include/asm/numa.h +++ b/arch/arm64/include/asm/numa.h @@ -5,6 +5,8 @@ #ifdef CONFIG_NUMA +#define NR_NODE_MEMBLKS (MAX_NUMNODES * 2) + /* currently, arm64 implements flat NUMA topology */ #define parent_node(node) (node) diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2173149..a5125c6 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -42,6 +42,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ACPI) += acpi.o +arm64-obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o diff --git a/arch/arm64/kernel/acpi_numa.c b/arch/arm64/kernel/acpi_numa.c new file mode 100644 index 0000000..f85149c --- /dev/null +++ b/arch/arm64/kernel/acpi_numa.c @@ -0,0 +1,112 @@ +/* + * ACPI 5.1 based NUMA setup for ARM64 + * Lots of code was borrowed from arch/x86/mm/srat.c + * + * Copyright 2004 Andi Kleen, SuSE Labs. + * Copyright (C) 2013-2016, Linaro Ltd. + * Author: Hanjun Guo + * + * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs. + * + * Called from acpi_numa_init while reading the SRAT and SLIT tables. + * Assumes all memory regions belonging to a single proximity domain + * are in one chunk. Holes between them will be included in the node. + */ + +#define pr_fmt(fmt) "ACPI: NUMA: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int cpus_in_srat; + +struct __node_cpu_hwid { + u32 node_id; /* logical node containing this CPU */ + u64 cpu_hwid; /* MPIDR for this CPU */ +}; + +static struct __node_cpu_hwid early_node_cpu_hwid[NR_CPUS] = { +[0 ... NR_CPUS - 1] = {NUMA_NO_NODE, PHYS_CPUID_INVALID} }; + +int acpi_numa_get_nid(unsigned int cpu, u64 hwid) +{ + int i; + + for (i = 0; i < cpus_in_srat; i++) { + if (hwid == early_node_cpu_hwid[i].cpu_hwid) + return early_node_cpu_hwid[i].node_id; + } + + return NUMA_NO_NODE; +} + +/* Callback for Proximity Domain -> ACPI processor UID mapping */ +void __init acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa) +{ + int pxm, node; + phys_cpuid_t mpidr; + + if (srat_disabled()) + return; + + if (pa->header.length < sizeof(struct acpi_srat_gicc_affinity)) { + pr_err("SRAT: Invalid SRAT header length: %d\n", + pa->header.length); + bad_srat(); + return; + } + + if (!(pa->flags & ACPI_SRAT_GICC_ENABLED)) + return; + + if (cpus_in_srat >= NR_CPUS) { + pr_warn_once("SRAT: cpu_to_node_map[%d] is too small, may not be able to use all cpus\n", + NR_CPUS); + return; + } + + pxm = pa->proximity_domain; + node = acpi_map_pxm_to_node(pxm); + + if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { + pr_err("SRAT: Too many proximity domains %d\n", pxm); + bad_srat(); + return; + } + + mpidr = acpi_map_madt_entry(pa->acpi_processor_uid); + if (mpidr == PHYS_CPUID_INVALID) { + pr_err("SRAT: PXM %d with ACPI ID %d has no valid MPIDR in MADT\n", + pxm, pa->acpi_processor_uid); + bad_srat(); + return; + } + + early_node_cpu_hwid[cpus_in_srat].node_id = node; + early_node_cpu_hwid[cpus_in_srat].cpu_hwid = mpidr; + node_set(node, numa_nodes_parsed); + cpus_in_srat++; + pr_info("SRAT: PXM %d -> MPIDR 0x%Lx -> Node %d\n", + pxm, mpidr, node); +} + +int __init arm64_acpi_numa_init(void) +{ + int ret; + + ret = acpi_numa_init(); + if (ret) + return ret; + + return srat_disabled() ? -EINVAL : 0; +} diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 678e084..d099306 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -560,6 +560,8 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) */ acpi_set_mailbox_entry(cpu_count, processor); + early_map_cpu_to_node(cpu_count, acpi_numa_get_nid(cpu_count, hwid)); + cpu_count++; } diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c index 1def1de..c7fe3ec 100644 --- a/arch/arm64/mm/numa.c +++ b/arch/arm64/mm/numa.c @@ -17,6 +17,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -391,7 +392,9 @@ static int __init dummy_numa_init(void) void __init arm64_numa_init(void) { if (!numa_off) { - if (!numa_init(of_numa_init)) + if (!acpi_disabled && !numa_init(arm64_acpi_numa_init)) + return; + if (acpi_disabled && !numa_init(of_numa_init)) return; } -- cgit v0.10.2 From ce0c1fcc7357ef0ec93c5a1cd1a759729d27599a Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Mon, 20 Jun 2016 13:56:09 +0300 Subject: ACPI / tables: table upgrade: use cacheable map for tables The new memory allocated in acpi_table_initrd_init() is used to copy the upgraded tables to it. So it should be mapped with early_memunmap() instead of early_ioremap(). This is critical for ARM. Signed-off-by: Aleksey Makarov Acked-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index a372f9e..f829e6a 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -578,10 +578,10 @@ static void __init acpi_table_initrd_init(void *data, size_t size) clen = size; if (clen > MAP_CHUNK_SIZE - slop) clen = MAP_CHUNK_SIZE - slop; - dest_p = early_ioremap(dest_addr & PAGE_MASK, - clen + slop); + dest_p = early_memremap(dest_addr & PAGE_MASK, + clen + slop); memcpy(dest_p + slop, src_p, clen); - early_iounmap(dest_p, clen + slop); + early_memunmap(dest_p, clen + slop); src_p += clen; dest_addr += clen; size -= clen; -- cgit v0.10.2 From da3d3f98d28bc071a2d566aefc8c461bd564be35 Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Mon, 20 Jun 2016 13:56:10 +0300 Subject: ACPI / tables: table upgrade: refactor function definitions Refer initrd_start, initrd_end directly from drivers/acpi/tables.c. This allows to use the table upgrade feature in architectures other than x86. Also this simplifies header files. The patch renames acpi_table_initrd_init() to acpi_table_upgrade() (what reflects the purpose of the function) and removes the unneeded wraps early_acpi_table_init() and early_initrd_acpi_init(). Signed-off-by: Aleksey Makarov Acked-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index c4e7b39..aac91be 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -399,10 +399,6 @@ static void __init reserve_initrd(void) memblock_free(ramdisk_image, ramdisk_end - ramdisk_image); } -static void __init early_initrd_acpi_init(void) -{ - early_acpi_table_init((void *)initrd_start, initrd_end - initrd_start); -} #else static void __init early_reserve_initrd(void) { @@ -410,9 +406,6 @@ static void __init early_reserve_initrd(void) static void __init reserve_initrd(void) { } -static void __init early_initrd_acpi_init(void) -{ -} #endif /* CONFIG_BLK_DEV_INITRD */ static void __init parse_setup_data(void) @@ -1146,7 +1139,7 @@ void __init setup_arch(char **cmdline_p) reserve_initrd(); - early_initrd_acpi_init(); + acpi_table_upgrade(); vsmp_init(); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index f829e6a..b05df13 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT @@ -481,8 +482,10 @@ static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES); #define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) -static void __init acpi_table_initrd_init(void *data, size_t size) +void __init acpi_table_upgrade(void) { + void *data = (void *)initrd_start; + size_t size = initrd_end - initrd_start; int sig, no, table_nr = 0, total_offset = 0; long offset = 0; struct acpi_table_header *table; @@ -696,10 +699,6 @@ next_table: } } #else -static void __init acpi_table_initrd_init(void *data, size_t size) -{ -} - static acpi_status acpi_table_initrd_override(struct acpi_table_header *existing_table, acpi_physical_address *address, @@ -742,11 +741,6 @@ acpi_os_table_override(struct acpi_table_header *existing_table, return AE_OK; } -void __init early_acpi_table_init(void *data, size_t size) -{ - acpi_table_initrd_init(data, size); -} - /* * acpi_table_init() * diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 288fac5..ef2ad26 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -208,7 +208,6 @@ void acpi_boot_table_init (void); int acpi_mps_check (void); int acpi_numa_init (void); -void early_acpi_table_init(void *data, size_t size); int acpi_table_init (void); int acpi_table_parse(char *id, acpi_tbl_table_handler handler); int __init acpi_parse_entries(char *id, unsigned long table_size, @@ -588,7 +587,6 @@ static inline const char *acpi_dev_name(struct acpi_device *adev) return NULL; } -static inline void early_acpi_table_init(void *data, size_t size) { } static inline void acpi_early_init(void) { } static inline void acpi_subsystem_init(void) { } @@ -997,4 +995,10 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, #define acpi_probe_device_table(t) ({ int __r = 0; __r;}) #endif +#ifdef CONFIG_ACPI_TABLE_UPGRADE +void acpi_table_upgrade(void); +#else +static inline void acpi_table_upgrade(void) { } +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v0.10.2 From 84b06ca319dae15f40fd7ff2bfff4769ab8cc58d Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Mon, 20 Jun 2016 13:56:11 +0300 Subject: ACPI / tables: move arch-specific symbol to asm/acpi.h The constant that defines max phys address where the new upgraded ACPI table should be allocated is arch-specific. Move it to Signed-off-by: Aleksey Makarov Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 94c18eb..c24c070 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -170,4 +170,6 @@ static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr) } #endif +#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn_mapped << PAGE_SHIFT) + #endif /* _ASM_X86_ACPI_H */ diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index b05df13..9f0ad6e 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "internal.h" #ifdef CONFIG_ACPI_CUSTOM_DSDT @@ -543,7 +544,7 @@ void __init acpi_table_upgrade(void) return; acpi_tables_addr = - memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT, + memblock_find_in_range(0, ACPI_TABLE_UPGRADE_MAX_PHYS, all_tables_size, PAGE_SIZE); if (!acpi_tables_addr) { WARN_ON(1); -- cgit v0.10.2 From 91dda51a115770fb9f24634fb09a502ad7bda5c6 Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Mon, 20 Jun 2016 13:56:12 +0300 Subject: ACPI / tables: introduce ARCH_HAS_ACPI_TABLE_UPGRADE We want to use the table upgrade feature in ARM64. Introduce a new configuration option that allows that. Signed-off-by: Aleksey Makarov Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d9a94da..f1a62447 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -22,6 +22,7 @@ config X86 select ANON_INODES select ARCH_CLOCKSOURCE_DATA select ARCH_DISCARD_MEMBLOCK + select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS select ARCH_HAS_DEVMEM_IS_ALLOWED diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..0a8e41a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -311,9 +311,12 @@ config ACPI_CUSTOM_DSDT bool default ACPI_CUSTOM_DSDT_FILE != "" +config ARCH_HAS_ACPI_TABLE_UPGRADE + def_bool n + config ACPI_TABLE_UPGRADE bool "Allow upgrading ACPI tables via initrd" - depends on BLK_DEV_INITRD && X86 + depends on BLK_DEV_INITRD && ARCH_HAS_ACPI_TABLE_UPGRADE default y help This option provides functionality to upgrade arbitrary ACPI tables -- cgit v0.10.2 From 38b04a74c58749231ee706752f81f7dc7c281a15 Mon Sep 17 00:00:00 2001 From: Jon Masters Date: Mon, 20 Jun 2016 13:56:13 +0300 Subject: ACPI: ARM64: support for ACPI_TABLE_UPGRADE This patch adds support for ACPI_TABLE_UPGRADE for ARM64 To access initrd image we need to move initialization of linear mapping a bit earlier. The implementation of the feature acpi_table_upgrade() (drivers/acpi/tables.c) works with initrd data represented as an array in virtual memory. It uses some library utility to find the redefined tables in that array and iterates over it to copy the data to new allocated memory. So to access the initrd data via fixmap we need to rewrite it considerably. In x86 arch, kernel memory is already mapped by the time when acpi_table_upgrade() and acpi_boot_table_init() are called so I think that we can just move this mapping one function earlier too. Signed-off-by: Jon Masters Signed-off-by: Aleksey Makarov Acked-by: Will Deacon Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5a0a691..20d5a60 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -4,6 +4,7 @@ config ARM64 select ACPI_GENERIC_GSI if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_HAS_DEVMEM_IS_ALLOWED + select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_GCOV_PROFILE_ALL diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index aee323b..c7aefc5 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -113,4 +113,6 @@ static inline const char *acpi_get_enable_method(int cpu) pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); #endif +#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE + #endif /*_ASM_ACPI_H*/ diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 3279def..92f0e1e 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -260,11 +260,13 @@ void __init setup_arch(char **cmdline_p) efi_init(); arm64_memblock_init(); + paging_init(); + + acpi_table_upgrade(); + /* Parse the ACPI tables for possible boot-time configuration */ acpi_boot_table_init(); - paging_init(); - if (acpi_disabled) unflatten_device_tree(); -- cgit v0.10.2 From 4bac6fa73db77d4ff49a965a7c6dc69d9e235e5f Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Fri, 17 Jun 2016 11:53:02 +0800 Subject: ACPI / NUMA: Enable ACPI based NUMA on ARM64 Add function needed for cpu to node mapping, and enable ACPI based NUMA for ARM64 in Kconfig Signed-off-by: Hanjun Guo Signed-off-by: Robert Richter [david.daney@cavium.com added ACPI_NUMA default to y for ARM64] Signed-off-by: David Daney Acked-by: Catalin Marinas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..dd76b36 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -291,8 +291,8 @@ config ACPI_THERMAL config ACPI_NUMA bool "NUMA support" depends on NUMA - depends on (X86 || IA64) - default y if IA64_GENERIC || IA64_SGI_SN2 + depends on (X86 || IA64 || ARM64) + default y if IA64_GENERIC || IA64_SGI_SN2 || ARM64 config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index fad6d28..ce3a7a1 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -170,6 +170,18 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header) } break; + case ACPI_SRAT_TYPE_GICC_AFFINITY: + { + struct acpi_srat_gicc_affinity *p = + (struct acpi_srat_gicc_affinity *)header; + pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n", + p->acpi_processor_uid, + p->proximity_domain, + (p->flags & ACPI_SRAT_GICC_ENABLED) ? + "enabled" : "disabled"); + } + break; + default: pr_warn("Found unsupported SRAT entry (type = 0x%x)\n", header->type); @@ -360,6 +372,24 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header, return 0; } +static int __init +acpi_parse_gicc_affinity(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_srat_gicc_affinity *processor_affinity; + + processor_affinity = (struct acpi_srat_gicc_affinity *)header; + if (!processor_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_gicc_affinity_init(processor_affinity); + + return 0; +} + static int __initdata parsed_numa_memblks; static int __init @@ -404,6 +434,9 @@ int __init acpi_numa_init(void) { int cnt = 0; + if (acpi_disabled) + return -EINVAL; + /* * Should not limit number with cpu num that is from NR_CPUS or nr_cpus= * SRAT cpu entries could have different order with that in MADT. @@ -412,13 +445,15 @@ int __init acpi_numa_init(void) /* SRAT: Static Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { - struct acpi_subtable_proc srat_proc[2]; + struct acpi_subtable_proc srat_proc[3]; memset(srat_proc, 0, sizeof(srat_proc)); srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY; srat_proc[0].handler = acpi_parse_processor_affinity; srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY; srat_proc[1].handler = acpi_parse_x2apic_affinity; + srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY; + srat_proc[2].handler = acpi_parse_gicc_affinity; acpi_table_parse_entries_array(ACPI_SIG_SRAT, sizeof(struct acpi_table_srat), diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8010b26..9515db6 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -232,10 +232,25 @@ int acpi_table_parse_madt(enum acpi_madt_type id, int acpi_parse_mcfg (struct acpi_table_header *header); void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); -/* the following four functions are architecture-dependent */ +/* the following numa functions are architecture-dependent */ void acpi_numa_slit_init (struct acpi_table_slit *slit); + +#if defined(CONFIG_X86) || defined(CONFIG_IA64) void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa); +#else +static inline void +acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) { } +#endif + void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); + +#ifdef CONFIG_ARM64 +void acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa); +#else +static inline void +acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa) { } +#endif + int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); #ifndef PHYS_CPUID_INVALID -- cgit v0.10.2 From 007d94ca8b7df4d3230bfb68b88a627da887a671 Mon Sep 17 00:00:00 2001 From: Ralf Gerbig Date: Wed, 22 Jun 2016 01:59:03 +0200 Subject: ACPI / video: Thinkpad X201 Tablet needs video_detect_force_video Add Thinkpad X201 Tablet to the video_detect_force_video blacklist. Signed-off-by: Ralf Gerbig [ rjw : Changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 3d13276..a6b36fc 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -167,6 +167,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), }, }, + { + .callback = video_detect_force_video, + .ident = "ThinkPad X201T", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"), + }, + }, /* The native backlight controls do not work on some older machines */ { -- cgit v0.10.2 From e34fbbac669de0b7fb7803929d0477f35f6e2833 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 27 May 2016 15:47:06 +0800 Subject: ACPI / video: skip evaluating _DOD when it does not exist Some system supports hybrid graphics and its discrete VGA does not have any connectors and therefore has no _DOD method. Signed-off-by: Alex Hung Reviewed-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c1d138e..c5557d0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1246,6 +1246,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) union acpi_object *dod = NULL; union acpi_object *obj; + if (!video->cap._DOD) + return AE_NOT_EXIST; + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); if (!ACPI_SUCCESS(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); -- cgit v0.10.2 From 2cfd93dd21b0127feae2579d46b7650fbacd2af1 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 27 May 2016 09:00:28 -0700 Subject: ACPI / PCI: pci_slot: Use more common logging style Use generic pr_ functions with pr_fmt for info and err. This also reduces object size a trivial bit: $ size drivers/acpi/pci_slot.o* text data bss dec hex filename 935 752 5 1692 69c drivers/acpi/pci_slot.o.new 1027 752 5 1784 6f8 drivers/acpi/pci_slot.o.old Miscellanea: o Remove unnecessary OOM message as k.alloc functions get a generic stack dump on OOM o Remove unnecessary embedded prefix from a dbg() message Signed-off-by: Joe Perches Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 7188e53..aa8f4c7 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -22,6 +22,8 @@ * General Public License for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -48,14 +50,11 @@ module_param(debug, bool, 0644); #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_slot"); -#define MY_NAME "pci_slot" -#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg) -#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg) -#define dbg(format, arg...) \ - do { \ - if (debug) \ - pr_debug("%s: " format, MY_NAME , ## arg); \ - } while (0) +#define dbg(fmt, ...) \ +do { \ + if (debug) \ + pr_debug(fmt, ##__VA_ARGS__); \ +} while (0) #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ @@ -132,15 +131,13 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } slot = kmalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) { - err("%s: cannot allocate memory\n", __func__); + if (!slot) return AE_OK; - } snprintf(name, sizeof(name), "%llu", sun); pci_slot = pci_create_slot(pci_bus, device, name, NULL); if (IS_ERR(pci_slot)) { - err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); + pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); kfree(slot); return AE_OK; } @@ -150,8 +147,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) get_device(&pci_bus->dev); - dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n", - pci_slot, pci_bus->number, device, name); + dbg("%p, pci_bus: %x, device: %d, name: %s\n", + pci_slot, pci_bus->number, device, name); return AE_OK; } @@ -186,7 +183,8 @@ void acpi_pci_slot_remove(struct pci_bus *bus) static int do_sta_before_sun(const struct dmi_system_id *d) { - info("%s detected: will evaluate _STA before calling _SUN\n", d->ident); + pr_info("%s detected: will evaluate _STA before calling _SUN\n", + d->ident); check_sta_before_sun = 1; return 0; } -- cgit v0.10.2 From 409ffed2b6ae1d5db3cefed2e0fb2ca7e3f352bd Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 27 May 2016 09:13:29 -0700 Subject: ACPI / PCI: pci_slot: Use generic pr_debug utility Remove the dbg macro and debug module parameter and use the generic kernel facility. Trivially reduces defconfig object size on x86-64 $ size drivers/acpi/pci_slot.o* text data bss dec hex filename 880 752 4 1636 664 drivers/acpi/pci_slot.o.new 935 752 5 1692 69c drivers/acpi/pci_slot.o.old Signed-off-by: Joe Perches Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index aa8f4c7..1236fb0 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -35,7 +35,6 @@ #include #include -static bool debug; static int check_sta_before_sun; #define DRIVER_VERSION "0.1" @@ -44,18 +43,10 @@ static int check_sta_before_sun; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -module_param(debug, bool, 0644); #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_slot"); -#define dbg(fmt, ...) \ -do { \ - if (debug) \ - pr_debug(fmt, ##__VA_ARGS__); \ -} while (0) - #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ struct acpi_pci_slot { @@ -75,7 +66,7 @@ check_slot(acpi_handle handle, unsigned long long *sun) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - dbg("Checking slot on path: %s\n", (char *)buffer.pointer); + pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer); if (check_sta_before_sun) { /* If SxFy doesn't have _STA, we just assume it's there */ @@ -86,14 +77,16 @@ check_slot(acpi_handle handle, unsigned long long *sun) status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); if (ACPI_FAILURE(status)) { - dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer); + pr_debug("_ADR returned %d on %s\n", + status, (char *)buffer.pointer); goto out; } /* No _SUN == not a slot == bail */ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); if (ACPI_FAILURE(status)) { - dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer); + pr_debug("_SUN returned %d on %s\n", + status, (char *)buffer.pointer); goto out; } @@ -147,8 +140,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) get_device(&pci_bus->dev); - dbg("%p, pci_bus: %x, device: %d, name: %s\n", - pci_slot, pci_bus->number, device, name); + pr_debug("%p, pci_bus: %x, device: %d, name: %s\n", + pci_slot, pci_bus->number, device, name); return AE_OK; } -- cgit v0.10.2 From c2dd420034f24f356b86f90222ef19148b82a5c1 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 1 Jun 2016 18:10:34 +0800 Subject: ACPI / button: Remove initial lid state notification The _LID control method's initial returning value is not reliable. The _LID control method is described to return the "current" lid state. However the word of "current" has ambiguity, many BIOSen return the lid state upon the last lid notification instead of returning the lid state upon the last _LID evaluation. There won't be difference when the _LID control method is evaluated during the runtime, the problem is its initial returning value. When the BIOSen implement this control method with cached value, the initial returning value is likely not reliable. There are simply so many examples retuning "close" as initial lid state (Link 1), sending this state to the userspace causes suspending right after booting/resuming. Since the lid state is implemented by the BIOSen, the kernel lid driver has no idea how it can be correct, this patch stops sending the initial lid state to the userspace to try to avoid sending the wrong lid state to the userspace to trigger such kind of wrong suspending. This actually reverts the following commit introduced for fixing a Novell bug: Commit: 23de5d9ef2a4bbc4f733f58311bcb7cf6239c813 Subject: ACPI: button: send initial lid state after add and resume Link: https://bugzilla.kernel.org/show_bug.cgi?id=89211 Link: https://bugzilla.kernel.org/show_bug.cgi?id=106151 Link: https://bugzilla.kernel.org/show_bug.cgi?id=106941 Link: https://bugzilla.novell.com/show_bug.cgi?id=326814 Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 5c3b091..9863278 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -334,8 +334,6 @@ static int acpi_button_resume(struct device *dev) struct acpi_button *button = acpi_driver_data(device); button->suspended = false; - if (button->type == ACPI_BUTTON_TYPE_LID) - return acpi_lid_send_state(device); return 0; } #endif @@ -416,7 +414,6 @@ static int acpi_button_add(struct acpi_device *device) if (error) goto err_remove_fs; if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); /* * This assumes there's only one lid device, or if there are * more we only care about the last one... -- cgit v0.10.2 From ee7e22653f5077169ec706b5a140a3be9db381e7 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 1 Jun 2016 18:10:42 +0800 Subject: ACPI / button: Refactor functions to eliminate redundant code (Correct a wrong macro usage.) This patch simplies the code by merging some redundant code. No functional changes. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 9863278..6e291c1 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -113,16 +113,52 @@ static struct acpi_device *lid_device; static struct proc_dir_entry *acpi_button_dir; static struct proc_dir_entry *acpi_lid_dir; +static int acpi_lid_evaluate_state(struct acpi_device *device) +{ + unsigned long long lid_state; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return lid_state ? 1 : 0; +} + +static int acpi_lid_notify_state(struct acpi_device *device, int state) +{ + struct acpi_button *button = acpi_driver_data(device); + int ret; + + /* input layer checks if event is redundant */ + input_report_switch(button->input, SW_LID, !state); + input_sync(button->input); + + if (state) + pm_wakeup_event(&device->dev, 0); + + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); + if (ret == NOTIFY_DONE) + ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, + device); + if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { + /* + * It is also regarded as success if the notifier_chain + * returns NOTIFY_OK or NOTIFY_DONE. + */ + ret = 0; + } + return ret; +} + static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) { struct acpi_device *device = seq->private; - acpi_status status; - unsigned long long state; + int state; - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); + state = acpi_lid_evaluate_state(device); seq_printf(seq, "state: %s\n", - ACPI_FAILURE(status) ? "unsupported" : - (state ? "open" : "closed")); + state < 0 ? "unsupported" : (state ? "open" : "closed")); return 0; } @@ -231,51 +267,22 @@ EXPORT_SYMBOL(acpi_lid_notifier_unregister); int acpi_lid_open(void) { - acpi_status status; - unsigned long long state; - if (!lid_device) return -ENODEV; - status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL, - &state); - if (ACPI_FAILURE(status)) - return -ENODEV; - - return !!state; + return acpi_lid_evaluate_state(lid_device); } EXPORT_SYMBOL(acpi_lid_open); -static int acpi_lid_send_state(struct acpi_device *device) +static int acpi_lid_update_state(struct acpi_device *device) { - struct acpi_button *button = acpi_driver_data(device); - unsigned long long state; - acpi_status status; - int ret; - - status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state); - if (ACPI_FAILURE(status)) - return -ENODEV; + int state; - /* input layer checks if event is redundant */ - input_report_switch(button->input, SW_LID, !state); - input_sync(button->input); + state = acpi_lid_evaluate_state(device); + if (state < 0) + return state; - if (state) - pm_wakeup_event(&device->dev, 0); - - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); - if (ret == NOTIFY_DONE) - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, - device); - if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { - /* - * It is also regarded as success if the notifier_chain - * returns NOTIFY_OK or NOTIFY_DONE. - */ - ret = 0; - } - return ret; + return acpi_lid_notify_state(device, state); } static void acpi_button_notify(struct acpi_device *device, u32 event) @@ -290,7 +297,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) case ACPI_BUTTON_NOTIFY_STATUS: input = button->input; if (button->type == ACPI_BUTTON_TYPE_LID) { - acpi_lid_send_state(device); + acpi_lid_update_state(device); } else { int keycode; -- cgit v0.10.2 From 3540c32a9ae4cb23ab70f7798f45affc02762fa7 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 1 Jun 2016 18:10:48 +0800 Subject: ACPI / button: Add quirks for initial lid state notification Linux userspace (systemd-logind) keeps on rechecking lid state when the lid state is closed. If it failed to update the lid state to open after boot/resume, the system suspending right after the boot/resume could be resulted. Graphics drivers also use the lid notifications to implment MODESET_ON_LID_OPEN option. Before the situation is improved from the userspace and from the graphics driver, users can simply configure ACPI button driver to send initial "open" lid state using button.lid_init_state=open to avoid such kind of issues. And our ultimate target should be making button.lid_init_state=ignore the default behavior. This patch implements the 2 options and keep the old behavior (button.lid_init_state=method). Link: https://lkml.org/2016/3/7/460 Link: https://github.com/systemd/systemd/issues/2087 Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 6e291c1..148f4e5 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -53,6 +53,10 @@ #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" #define ACPI_BUTTON_TYPE_LID 0x05 +#define ACPI_BUTTON_LID_INIT_IGNORE 0x00 +#define ACPI_BUTTON_LID_INIT_OPEN 0x01 +#define ACPI_BUTTON_LID_INIT_METHOD 0x02 + #define _COMPONENT ACPI_BUTTON_COMPONENT ACPI_MODULE_NAME("button"); @@ -105,6 +109,7 @@ struct acpi_button { static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); static struct acpi_device *lid_device; +static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; /* -------------------------------------------------------------------------- FS Interface (/proc) @@ -285,6 +290,21 @@ static int acpi_lid_update_state(struct acpi_device *device) return acpi_lid_notify_state(device, state); } +static void acpi_lid_initialize_state(struct acpi_device *device) +{ + switch (lid_init_state) { + case ACPI_BUTTON_LID_INIT_OPEN: + (void)acpi_lid_notify_state(device, 1); + break; + case ACPI_BUTTON_LID_INIT_METHOD: + (void)acpi_lid_update_state(device); + break; + case ACPI_BUTTON_LID_INIT_IGNORE: + default: + break; + } +} + static void acpi_button_notify(struct acpi_device *device, u32 event) { struct acpi_button *button = acpi_driver_data(device); @@ -341,6 +361,8 @@ static int acpi_button_resume(struct device *dev) struct acpi_button *button = acpi_driver_data(device); button->suspended = false; + if (button->type == ACPI_BUTTON_TYPE_LID) + acpi_lid_initialize_state(device); return 0; } #endif @@ -421,6 +443,7 @@ static int acpi_button_add(struct acpi_device *device) if (error) goto err_remove_fs; if (button->type == ACPI_BUTTON_TYPE_LID) { + acpi_lid_initialize_state(device); /* * This assumes there's only one lid device, or if there are * more we only care about the last one... @@ -450,4 +473,42 @@ static int acpi_button_remove(struct acpi_device *device) return 0; } +static int param_set_lid_init_state(const char *val, struct kernel_param *kp) +{ + int result = 0; + + if (!strncmp(val, "open", sizeof("open") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_OPEN; + pr_info("Notify initial lid state as open\n"); + } else if (!strncmp(val, "method", sizeof("method") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; + pr_info("Notify initial lid state with _LID return value\n"); + } else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) { + lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE; + pr_info("Do not notify initial lid state\n"); + } else + result = -EINVAL; + return result; +} + +static int param_get_lid_init_state(char *buffer, struct kernel_param *kp) +{ + switch (lid_init_state) { + case ACPI_BUTTON_LID_INIT_OPEN: + return sprintf(buffer, "open"); + case ACPI_BUTTON_LID_INIT_METHOD: + return sprintf(buffer, "method"); + case ACPI_BUTTON_LID_INIT_IGNORE: + return sprintf(buffer, "ignore"); + default: + return sprintf(buffer, "invalid"); + } + return 0; +} + +module_param_call(lid_init_state, + param_set_lid_init_state, param_get_lid_init_state, + NULL, 0644); +MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state"); + module_acpi_driver(acpi_button_driver); -- cgit v0.10.2 From e053dc909352837a95676f7826aafa8701bf0dca Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Tue, 7 Jun 2016 08:45:40 +0530 Subject: ACPI / thermal: Remove create_workqueue() alloc_workqueue replaces deprecated create_workqueue(). A dedicated workqueue has been used since the workqueue acpi_thermal_pm_queue with workitem &tz->thermal_check_work (maps to acpi_thermal_check_fn), is involved in thermal zone polling. Wallclock time is actually important and getting delayed in handling critical temperature event can actually lead to unnecessary hardware damage. So while this is not used during memory reclaim, we still want forward progress guarantee and be generally snappy in servicing it. Hence, WQ_MEM_RECLAIM and WQ_HIGHPRI have been used here. Since there are only a fixed number of work items, explicit concurrency limit is unnecessary here. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 82707f9..f4ebe39 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1259,7 +1259,8 @@ static int __init acpi_thermal_init(void) return -ENODEV; } - acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm"); + acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm", + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); if (!acpi_thermal_pm_queue) return -ENODEV; -- cgit v0.10.2 From f26f5c8b8b182c38074d942e9124679238aa04ef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Jun 2016 17:23:09 +0200 Subject: ACPI / bus: Use acpi_handle_debug() in acpi_print_osc_error() acpi_print_osc_error() basically duplicates the functionalit of acpi_handle_debug(), so use that one in there. While at it, convert the explicit KERN_DEBUG prints to pr_debug() (and apply it to continuation messages too). Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 262ca31..a6bb8ae 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -174,22 +174,17 @@ void acpi_bus_detach_private_data(acpi_handle handle) EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); static void acpi_print_osc_error(acpi_handle handle, - struct acpi_osc_context *context, char *error) + struct acpi_osc_context *context, char *error) { - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; int i; - if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) - printk(KERN_DEBUG "%s: %s\n", context->uuid_str, error); - else { - printk(KERN_DEBUG "%s (%s): %s\n", - (char *)buffer.pointer, context->uuid_str, error); - kfree(buffer.pointer); - } - printk(KERN_DEBUG "_OSC request data:"); + acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error); + + pr_debug("_OSC request data:"); for (i = 0; i < context->cap.length; i += sizeof(u32)) - printk(" %x", *((u32 *)(context->cap.pointer + i))); - printk("\n"); + pr_debug(" %x", *((u32 *)(context->cap.pointer + i))); + + pr_debug("\n"); } acpi_status acpi_str_to_uuid(char *str, u8 *uuid) -- cgit v0.10.2 From a3bcf2d45d6a9c7950f58ec7dad20f22d422abd2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 Jun 2016 16:01:08 +0300 Subject: tools/acpi: use CROSS_COMPILE to define prefix CROSS_COMPILE can be considered as standard definition for toolchain prefix when cross-compiling. Use it here. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/acpi/Makefile.config b/tools/power/acpi/Makefile.config index 552af68..a538ff4 100644 --- a/tools/power/acpi/Makefile.config +++ b/tools/power/acpi/Makefile.config @@ -54,9 +54,10 @@ INSTALL_SCRIPT = ${INSTALL_PROGRAM} # to something more interesting, like "arm-linux-". If you want # to compile vs uClibc, that can be done here as well. CROSS = #/usr/i386-linux-uclibc/usr/bin/i386-uclibc- -CC = $(CROSS)gcc -LD = $(CROSS)gcc -STRIP = $(CROSS)strip +CROSS_COMPILE ?= $(CROSS) +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)gcc +STRIP = $(CROSS_COMPILE)strip HOSTCC = gcc # check if compiler option is supported -- cgit v0.10.2 From 8343c40d3de32ebfe8f48b043964e4ba0e7701f7 Mon Sep 17 00:00:00 2001 From: Hoan Tran Date: Fri, 17 Jun 2016 15:16:31 -0700 Subject: ACPI: CPPC: Return error if _CPC is invalid on a CPU Based on 8.4.7.1 section of ACPI 6.1 specification, if the platform supports CPPC, the _CPC object must exist under all processor objects. If cpc_desc_ptr pointer is invalid on any CPUs, acpi_get_psd_map() should return error and CPPC cpufreq driver can not be registered. Signed-off-by: Hoan Tran Reviewed-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 8adac69..f9be315 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -299,8 +299,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data) continue; cpc_ptr = per_cpu(cpc_desc_ptr, i); - if (!cpc_ptr) - continue; + if (!cpc_ptr) { + retval = -EFAULT; + goto err_ret; + } pdomain = &(cpc_ptr->domain_info); cpumask_set_cpu(i, pr->shared_cpu_map); @@ -322,8 +324,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data) continue; match_cpc_ptr = per_cpu(cpc_desc_ptr, j); - if (!match_cpc_ptr) - continue; + if (!match_cpc_ptr) { + retval = -EFAULT; + goto err_ret; + } match_pdomain = &(match_cpc_ptr->domain_info); if (match_pdomain->domain != pdomain->domain) @@ -353,8 +357,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data) continue; match_cpc_ptr = per_cpu(cpc_desc_ptr, j); - if (!match_cpc_ptr) - continue; + if (!match_cpc_ptr) { + retval = -EFAULT; + goto err_ret; + } match_pdomain = &(match_cpc_ptr->domain_info); if (match_pdomain->domain != pdomain->domain) -- cgit v0.10.2 From b2f740baa4215254cee94a1d326a29d984169e4e Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 23 May 2016 10:28:03 +0200 Subject: ACPI / einj: Convert EINJ_PFX to proper pr_fmt ... and remove it from the pr_* calls. Signed-off-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 559c117..4ba4185 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -33,7 +33,8 @@ #include "apei-internal.h" -#define EINJ_PFX "EINJ: " +#undef pr_fmt +#define pr_fmt(fmt) "EINJ: " fmt #define SPIN_UNIT 100 /* 100ns */ /* Firmware should respond within 1 milliseconds */ @@ -179,8 +180,7 @@ static int einj_get_available_error_type(u32 *type) static int einj_timedout(u64 *t) { if ((s64)*t < SPIN_UNIT) { - pr_warning(FW_WARN EINJ_PFX - "Firmware does not respond in time\n"); + pr_warning(FW_WARN "Firmware does not respond in time\n"); return 1; } *t -= SPIN_UNIT; @@ -307,8 +307,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { - pr_err(EINJ_PFX - "Can not request [mem %#010llx-%#010llx] for Trigger table\n", + pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr + sizeof(*trigger_tab) - 1); @@ -316,13 +315,12 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, } trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); if (!trigger_tab) { - pr_err(EINJ_PFX "Failed to map trigger table!\n"); + pr_err("Failed to map trigger table!\n"); goto out_rel_header; } rc = einj_check_trigger_header(trigger_tab); if (rc) { - pr_warning(FW_BUG EINJ_PFX - "The trigger error action table is invalid\n"); + pr_warning(FW_BUG "Invalid trigger error action table.\n"); goto out_rel_header; } @@ -336,8 +334,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, table_size - sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { - pr_err(EINJ_PFX -"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", + pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", (unsigned long long)trigger_paddr + sizeof(*trigger_tab), (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; @@ -345,7 +342,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, iounmap(trigger_tab); trigger_tab = ioremap_cache(trigger_paddr, table_size); if (!trigger_tab) { - pr_err(EINJ_PFX "Failed to map trigger table!\n"); + pr_err("Failed to map trigger table!\n"); goto out_rel_entry; } trigger_entry = (struct acpi_whea_header *) @@ -704,13 +701,13 @@ static int __init einj_init(void) return -ENODEV; else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); - pr_err(EINJ_PFX "Failed to get table, %s\n", msg); + pr_err("Failed to get table, %s\n", msg); return -EINVAL; } rc = einj_check_table(einj_tab); if (rc) { - pr_warning(FW_BUG EINJ_PFX "EINJ table is invalid\n"); + pr_warning(FW_BUG "EINJ table is invalid\n"); return -EINVAL; } @@ -787,7 +784,7 @@ static int __init einj_init(void) goto err_unmap; } - pr_info(EINJ_PFX "Error INJection is initialized.\n"); + pr_info("Error INJection is initialized.\n"); return 0; -- cgit v0.10.2 From dba648300e890fb6961d896ab3bd60704815adf3 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 23 May 2016 10:28:04 +0200 Subject: ACPI / einj: Make error paths more talkative It is absolutely unfriendly when one sees this: # modprobe einj modprobe: ERROR: could not insert 'einj': No such device without anything in dmesg to tell one why the load failed. Beef up the error handling of the init function to be more user-friendly when the load fails. Signed-off-by: Borislav Petkov Acked-by: Tony Luck Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 4ba4185..eebb7e3 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -692,34 +692,42 @@ static int __init einj_init(void) struct dentry *fentry; struct apei_exec_context ctx; - if (acpi_disabled) + if (acpi_disabled) { + pr_warn("ACPI disabled.\n"); return -ENODEV; + } status = acpi_get_table(ACPI_SIG_EINJ, 0, (struct acpi_table_header **)&einj_tab); - if (status == AE_NOT_FOUND) + if (status == AE_NOT_FOUND) { + pr_warn("EINJ table not found.\n"); return -ENODEV; + } else if (ACPI_FAILURE(status)) { - const char *msg = acpi_format_exception(status); - pr_err("Failed to get table, %s\n", msg); + pr_err("Failed to get EINJ table: %s\n", + acpi_format_exception(status)); return -EINVAL; } rc = einj_check_table(einj_tab); if (rc) { - pr_warning(FW_BUG "EINJ table is invalid\n"); + pr_warn(FW_BUG "Invalid EINJ table.n"); return -EINVAL; } rc = -ENOMEM; einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir()); - if (!einj_debug_dir) + if (!einj_debug_dir) { + pr_err("Error creating debugfs node.\n"); goto err_cleanup; + } + fentry = debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir, NULL, &available_error_type_fops); if (!fentry) goto err_cleanup; + fentry = debugfs_create_file("error_type", S_IRUSR | S_IWUSR, einj_debug_dir, NULL, &error_type_fops); if (!fentry) @@ -732,14 +740,22 @@ static int __init einj_init(void) apei_resources_init(&einj_resources); einj_exec_ctx_init(&ctx); rc = apei_exec_collect_resources(&ctx, &einj_resources); - if (rc) + if (rc) { + pr_err("Error collecting EINJ resources.\n"); goto err_fini; + } + rc = apei_resources_request(&einj_resources, "APEI EINJ"); - if (rc) + if (rc) { + pr_err("Error requesting memory/port resources.\n"); goto err_fini; + } + rc = apei_exec_pre_map_gars(&ctx); - if (rc) + if (rc) { + pr_err("Error pre-mapping GARs.\n"); goto err_release; + } rc = -ENOMEM; einj_param = einj_get_parameter_address(); @@ -795,6 +811,7 @@ err_unmap: sizeof(struct einj_parameter); acpi_os_unmap_iomem(einj_param, size); + pr_err("Error creating param extension debugfs nodes.\n"); } apei_exec_post_unmap_gars(&ctx); err_release: @@ -802,6 +819,7 @@ err_release: err_fini: apei_resources_fini(&einj_resources); err_cleanup: + pr_err("Error creating primary debugfs nodes.\n"); debugfs_remove_recursive(einj_debug_dir); return rc; -- cgit v0.10.2 From d8ba8191245e3484d8533d9bef1432d13829e347 Mon Sep 17 00:00:00 2001 From: Bin Gao Date: Thu, 23 Jun 2016 17:45:35 -0700 Subject: ACPI / PMIC: modify the pen function signature to take bit field Issue description: On some pmics, the policy enable for thermal alerts refers to different bit fields of the same registers, whereas on other pmics, the policy enable refers to the same bit field on different registers. Previous implementation did not provide the flexibility for supporting the first approach. Solution: Modified the policy enable function to take bit field as well. The use of bit field is left to the pmic specific opregion driver. Signed-off-by: Yegnesh Iyer Signed-off-by: Bin Gao Reviewed-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index bd772cd..410e96f 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -131,7 +131,7 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, } static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, - u32 function, u64 *value) + int bit, u32 function, u64 *value) { struct intel_pmic_opregion_data *d = opregion->data; struct regmap *regmap = opregion->regmap; @@ -140,12 +140,12 @@ static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, return -ENXIO; if (function == ACPI_READ) - return d->get_policy(regmap, reg, value); + return d->get_policy(regmap, reg, bit, value); if (*value != 0 && *value != 1) return -EINVAL; - return d->update_policy(regmap, reg, *value); + return d->update_policy(regmap, reg, bit, *value); } static bool pmic_thermal_is_temp(int address) @@ -170,13 +170,13 @@ static acpi_status intel_pmic_thermal_handler(u32 function, { struct intel_pmic_opregion *opregion = region_context; struct intel_pmic_opregion_data *d = opregion->data; - int reg, result; + int reg, bit, result; if (bits != 32 || !value64) return AE_BAD_PARAMETER; result = pmic_get_reg_bit(address, d->thermal_table, - d->thermal_table_count, ®, NULL); + d->thermal_table_count, ®, &bit); if (result == -ENOENT) return AE_BAD_PARAMETER; @@ -187,7 +187,8 @@ static acpi_status intel_pmic_thermal_handler(u32 function, else if (pmic_thermal_is_aux(address)) result = pmic_thermal_aux(opregion, reg, function, value64); else if (pmic_thermal_is_pen(address)) - result = pmic_thermal_pen(opregion, reg, function, value64); + result = pmic_thermal_pen(opregion, reg, bit, + function, value64); else result = -EINVAL; diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h index d4e90af..e8bfa7b 100644 --- a/drivers/acpi/pmic/intel_pmic.h +++ b/drivers/acpi/pmic/intel_pmic.h @@ -12,8 +12,8 @@ struct intel_pmic_opregion_data { int (*update_power)(struct regmap *r, int reg, int bit, bool on); int (*get_raw_temp)(struct regmap *r, int reg); int (*update_aux)(struct regmap *r, int reg, int raw_temp); - int (*get_policy)(struct regmap *r, int reg, u64 *value); - int (*update_policy)(struct regmap *r, int reg, int enable); + int (*get_policy)(struct regmap *r, int reg, int bit, u64 *value); + int (*update_policy)(struct regmap *r, int reg, int bit, int enable); struct pmic_table *power_table; int power_table_count; struct pmic_table *thermal_table; diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c index fcd1852..d7f1761 100644 --- a/drivers/acpi/pmic/intel_pmic_crc.c +++ b/drivers/acpi/pmic/intel_pmic_crc.c @@ -141,7 +141,8 @@ static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw) regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0; } -static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value) +static int intel_crc_pmic_get_policy(struct regmap *regmap, + int reg, int bit, u64 *value) { int pen; @@ -152,7 +153,7 @@ static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value) } static int intel_crc_pmic_update_policy(struct regmap *regmap, - int reg, int enable) + int reg, int bit, int enable) { int alert0; -- cgit v0.10.2 From 9b928c78bb3cc2ffdd73dad172820554e480c8a6 Mon Sep 17 00:00:00 2001 From: Bin Gao Date: Thu, 23 Jun 2016 17:48:51 -0700 Subject: ACPI / PMIC: Add opregion driver for Intel BXT WhiskeyCove PMIC This patch adds operation region driver for Intel BXT WhiskeyCove PMIC. The register mapping is done as per the BXT WC data sheet. Signed-off-by: Ajay Thomas Signed-off-by: Bin Gao Reviewed-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..47cb6f6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -519,6 +519,12 @@ config XPOWER_PMIC_OPREGION help This config adds ACPI operation region support for XPower AXP288 PMIC. +config BXT_WC_PMIC_OPREGION + bool "ACPI operation region support for BXT WhiskeyCove PMIC" + depends on INTEL_SOC_PMIC + help + This config adds ACPI operation region support for BXT WhiskeyCove PMIC. + endif endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85..5da9d4b 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -99,5 +99,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o +obj-$(CONFIG_BXT_WC_PMIC_OPREGION) += pmic/intel_pmic_bxtwc.o video-objs += acpi_video.o video_detect.o diff --git a/drivers/acpi/pmic/intel_pmic_bxtwc.c b/drivers/acpi/pmic/intel_pmic_bxtwc.c new file mode 100644 index 0000000..ebe6f09 --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic_bxtwc.c @@ -0,0 +1,424 @@ +/* + * intel_pmic_bxtwc.c - Intel BXT WhiskeyCove PMIC operation region driver + * + * Copyright (C) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include "intel_pmic.h" + +#define WHISKEY_COVE_ALRT_HIGH_BIT_MASK 0x0F +#define WHISKEY_COVE_ADC_HIGH_BIT(x) (((x & 0x0F) << 8)) +#define WHISKEY_COVE_ADC_CURSRC(x) (((x & 0xF0) >> 4)) +#define VR_MODE_DISABLED 0 +#define VR_MODE_AUTO BIT(0) +#define VR_MODE_NORMAL BIT(1) +#define VR_MODE_SWITCH BIT(2) +#define VR_MODE_ECO (BIT(0)|BIT(1)) +#define VSWITCH2_OUTPUT BIT(5) +#define VSWITCH1_OUTPUT BIT(4) +#define VUSBPHY_CHARGE BIT(1) + +static struct pmic_table power_table[] = { + { + .address = 0x0, + .reg = 0x63, + .bit = VR_MODE_AUTO, + }, /* VDD1 -> VDD1CNT */ + { + .address = 0x04, + .reg = 0x65, + .bit = VR_MODE_AUTO, + }, /* VDD2 -> VDD2CNT */ + { + .address = 0x08, + .reg = 0x67, + .bit = VR_MODE_AUTO, + }, /* VDD3 -> VDD3CNT */ + { + .address = 0x0c, + .reg = 0x6d, + .bit = VR_MODE_AUTO, + }, /* VLFX -> VFLEXCNT */ + { + .address = 0x10, + .reg = 0x6f, + .bit = VR_MODE_NORMAL, + }, /* VP1A -> VPROG1ACNT */ + { + .address = 0x14, + .reg = 0x70, + .bit = VR_MODE_NORMAL, + }, /* VP1B -> VPROG1BCNT */ + { + .address = 0x18, + .reg = 0x71, + .bit = VR_MODE_NORMAL, + }, /* VP1C -> VPROG1CCNT */ + { + .address = 0x1c, + .reg = 0x72, + .bit = VR_MODE_NORMAL, + }, /* VP1D -> VPROG1DCNT */ + { + .address = 0x20, + .reg = 0x73, + .bit = VR_MODE_NORMAL, + }, /* VP2A -> VPROG2ACNT */ + { + .address = 0x24, + .reg = 0x74, + .bit = VR_MODE_NORMAL, + }, /* VP2B -> VPROG2BCNT */ + { + .address = 0x28, + .reg = 0x75, + .bit = VR_MODE_NORMAL, + }, /* VP2C -> VPROG2CCNT */ + { + .address = 0x2c, + .reg = 0x76, + .bit = VR_MODE_NORMAL, + }, /* VP3A -> VPROG3ACNT */ + { + .address = 0x30, + .reg = 0x77, + .bit = VR_MODE_NORMAL, + }, /* VP3B -> VPROG3BCNT */ + { + .address = 0x34, + .reg = 0x78, + .bit = VSWITCH2_OUTPUT, + }, /* VSW2 -> VLD0CNT Bit 5*/ + { + .address = 0x38, + .reg = 0x78, + .bit = VSWITCH1_OUTPUT, + }, /* VSW1 -> VLD0CNT Bit 4 */ + { + .address = 0x3c, + .reg = 0x78, + .bit = VUSBPHY_CHARGE, + }, /* VUPY -> VLDOCNT Bit 1 */ + { + .address = 0x40, + .reg = 0x7b, + .bit = VR_MODE_NORMAL, + }, /* VRSO -> VREFSOCCNT*/ + { + .address = 0x44, + .reg = 0xA0, + .bit = VR_MODE_NORMAL, + }, /* VP1E -> VPROG1ECNT */ + { + .address = 0x48, + .reg = 0xA1, + .bit = VR_MODE_NORMAL, + }, /* VP1F -> VPROG1FCNT */ + { + .address = 0x4c, + .reg = 0xA2, + .bit = VR_MODE_NORMAL, + }, /* VP2D -> VPROG2DCNT */ + { + .address = 0x50, + .reg = 0xA3, + .bit = VR_MODE_NORMAL, + }, /* VP4A -> VPROG4ACNT */ + { + .address = 0x54, + .reg = 0xA4, + .bit = VR_MODE_NORMAL, + }, /* VP4B -> VPROG4BCNT */ + { + .address = 0x58, + .reg = 0xA5, + .bit = VR_MODE_NORMAL, + }, /* VP4C -> VPROG4CCNT */ + { + .address = 0x5c, + .reg = 0xA6, + .bit = VR_MODE_NORMAL, + }, /* VP4D -> VPROG4DCNT */ + { + .address = 0x60, + .reg = 0xA7, + .bit = VR_MODE_NORMAL, + }, /* VP5A -> VPROG5ACNT */ + { + .address = 0x64, + .reg = 0xA8, + .bit = VR_MODE_NORMAL, + }, /* VP5B -> VPROG5BCNT */ + { + .address = 0x68, + .reg = 0xA9, + .bit = VR_MODE_NORMAL, + }, /* VP6A -> VPROG6ACNT */ + { + .address = 0x6c, + .reg = 0xAA, + .bit = VR_MODE_NORMAL, + }, /* VP6B -> VPROG6BCNT */ + { + .address = 0x70, + .reg = 0x36, + .bit = BIT(2), + }, /* SDWN_N -> MODEMCTRL Bit 2 */ + { + .address = 0x74, + .reg = 0x36, + .bit = BIT(0), + } /* MOFF -> MODEMCTRL Bit 0 */ +}; + +static struct pmic_table thermal_table[] = { + { + .address = 0x00, + .reg = 0x4F39 + }, + { + .address = 0x04, + .reg = 0x4F24 + }, + { + .address = 0x08, + .reg = 0x4F26 + }, + { + .address = 0x0c, + .reg = 0x4F3B + }, + { + .address = 0x10, + .reg = 0x4F28 + }, + { + .address = 0x14, + .reg = 0x4F2A + }, + { + .address = 0x18, + .reg = 0x4F3D + }, + { + .address = 0x1c, + .reg = 0x4F2C + }, + { + .address = 0x20, + .reg = 0x4F2E + }, + { + .address = 0x24, + .reg = 0x4F3F + }, + { + .address = 0x28, + .reg = 0x4F30 + }, + { + .address = 0x30, + .reg = 0x4F41 + }, + { + .address = 0x34, + .reg = 0x4F32 + }, + { + .address = 0x3c, + .reg = 0x4F43 + }, + { + .address = 0x40, + .reg = 0x4F34 + }, + { + .address = 0x48, + .reg = 0x4F6A, + .bit = 0, + }, + { + .address = 0x4C, + .reg = 0x4F6A, + .bit = 1 + }, + { + .address = 0x50, + .reg = 0x4F6A, + .bit = 2 + }, + { + .address = 0x54, + .reg = 0x4F6A, + .bit = 4 + }, + { + .address = 0x58, + .reg = 0x4F6A, + .bit = 5 + }, + { + .address = 0x5C, + .reg = 0x4F6A, + .bit = 3 + }, +}; + +static int intel_bxtwc_pmic_get_power(struct regmap *regmap, int reg, + int bit, u64 *value) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & bit) ? 1 : 0; + return 0; +} + +static int intel_bxtwc_pmic_update_power(struct regmap *regmap, int reg, + int bit, bool on) +{ + u8 val, mask = bit; + + if (on) + val = 0xFF; + else + val = 0x0; + + return regmap_update_bits(regmap, reg, mask, val); +} + +static int intel_bxtwc_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ + unsigned int val, adc_val, reg_val; + u8 temp_l, temp_h, cursrc; + unsigned long rlsb; + static const unsigned long rlsb_array[] = { + 0, 260420, 130210, 65100, 32550, 16280, + 8140, 4070, 2030, 0, 260420, 130210 }; + + if (regmap_read(regmap, reg, &val)) + return -EIO; + temp_l = (u8) val; + + if (regmap_read(regmap, (reg - 1), &val)) + return -EIO; + temp_h = (u8) val; + + reg_val = temp_l | WHISKEY_COVE_ADC_HIGH_BIT(temp_h); + cursrc = WHISKEY_COVE_ADC_CURSRC(temp_h); + rlsb = rlsb_array[cursrc]; + adc_val = reg_val * rlsb / 1000; + + return adc_val; +} + +static int +intel_bxtwc_pmic_update_aux(struct regmap *regmap, int reg, int raw) +{ + u32 bsr_num; + u16 resi_val, count = 0, thrsh = 0; + u8 alrt_h, alrt_l, cursel = 0; + + bsr_num = raw; + bsr_num /= (1 << 5); + + count = fls(bsr_num) - 1; + + cursel = clamp_t(s8, (count - 7), 0, 7); + thrsh = raw / (1 << (4 + cursel)); + + resi_val = (cursel << 9) | thrsh; + alrt_h = (resi_val >> 8) & WHISKEY_COVE_ALRT_HIGH_BIT_MASK; + if (regmap_update_bits(regmap, + reg - 1, + WHISKEY_COVE_ALRT_HIGH_BIT_MASK, + alrt_h)) + return -EIO; + + alrt_l = (u8)resi_val; + return regmap_write(regmap, reg, alrt_l); +} + +static int +intel_bxtwc_pmic_get_policy(struct regmap *regmap, int reg, int bit, u64 *value) +{ + u8 mask = BIT(bit); + unsigned int val; + + if (regmap_read(regmap, reg, &val)) + return -EIO; + + *value = (val & mask) >> bit; + return 0; +} + +static int +intel_bxtwc_pmic_update_policy(struct regmap *regmap, + int reg, int bit, int enable) +{ + u8 mask = BIT(bit), val = enable << bit; + + return regmap_update_bits(regmap, reg, mask, val); +} + +static struct intel_pmic_opregion_data intel_bxtwc_pmic_opregion_data = { + .get_power = intel_bxtwc_pmic_get_power, + .update_power = intel_bxtwc_pmic_update_power, + .get_raw_temp = intel_bxtwc_pmic_get_raw_temp, + .update_aux = intel_bxtwc_pmic_update_aux, + .get_policy = intel_bxtwc_pmic_get_policy, + .update_policy = intel_bxtwc_pmic_update_policy, + .power_table = power_table, + .power_table_count = ARRAY_SIZE(power_table), + .thermal_table = thermal_table, + .thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static int intel_bxtwc_pmic_opregion_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + + return intel_pmic_install_opregion_handler(&pdev->dev, + ACPI_HANDLE(pdev->dev.parent), + pmic->regmap, + &intel_bxtwc_pmic_opregion_data); +} + +static struct platform_device_id bxt_wc_opregion_id_table[] = { + { .name = "bxt_wcove_region" }, + {}, +}; + +static struct platform_driver intel_bxtwc_pmic_opregion_driver = { + .probe = intel_bxtwc_pmic_opregion_probe, + .driver = { + .name = "bxt_whiskey_cove_pmic", + }, + .id_table = bxt_wc_opregion_id_table, +}; + +static int __init intel_bxtwc_pmic_opregion_driver_init(void) +{ + return platform_driver_register(&intel_bxtwc_pmic_opregion_driver); +} + +device_initcall(intel_bxtwc_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("BXT WhiskeyCove ACPI opregion driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 2324d15447a9db168b1f85e3feac635b1ff8edb8 Mon Sep 17 00:00:00 2001 From: Hoan Tran Date: Wed, 25 May 2016 12:09:23 -0700 Subject: ACPI / CPPC: Prevent cpc_desc_ptr points to the invalid data When CPPC fails to request a PCC channel, the CPC data is freed and cpc_desc_ptr points to the invalid data. Avoid this issue by moving the cpc_desc_ptr assignment after the PCC channel request. Signed-off-by: Hoan Tran Acked-by: Ashwin Chaugule Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index f9be315..2e98173 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -601,9 +601,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) /* Store CPU Logical ID */ cpc_ptr->cpu_id = pr->id; - /* Plug it into this CPUs CPC descriptor. */ - per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; - /* Parse PSD data for this CPU */ ret = acpi_get_psd(cpc_ptr, handle); if (ret) @@ -616,6 +613,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) goto out_free; } + /* Plug PSD data into this CPUs CPC descriptor. */ + per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; + /* Everything looks okay */ pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); -- cgit v0.10.2 From 866ae696e26ee2b1fa0d04e67d6dafc477543fd6 Mon Sep 17 00:00:00 2001 From: Hoan Tran Date: Thu, 16 Jun 2016 14:09:38 -0700 Subject: mailbox: pcc: Add PCC request and free channel declarations Exports pcc_mbox_request_channel() and pcc_mbox_free_channel() declarations into a pcc.h header file. Looks-good-to: Prashanth Prakash Signed-off-by: Hoan Tran Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index dad8af3..284965c 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -15,10 +15,9 @@ #define _CPPC_ACPI_H #include -#include -#include #include +#include #include /* Only support CPPCv2 for now. */ @@ -130,8 +129,4 @@ extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); extern int acpi_get_psd_map(struct cpudata **); -/* Methods to interact with the PCC mailbox controller. */ -extern struct mbox_chan * - pcc_mbox_request_channel(struct mbox_client *, unsigned int); - #endif /* _CPPC_ACPI_H*/ diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h new file mode 100644 index 0000000..17a940a --- /dev/null +++ b/include/acpi/pcc.h @@ -0,0 +1,29 @@ +/* + * PCC (Platform Communications Channel) methods + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#ifndef _PCC_H +#define _PCC_H + +#include +#include + +#ifdef CONFIG_PCC +extern struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, + int subspace_id); +extern void pcc_mbox_free_channel(struct mbox_chan *chan); +#else +static inline struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, + int subspace_id) +{ + return NULL; +} +static inline void pcc_mbox_free_channel(struct mbox_chan *chan) { } +#endif + +#endif /* _PCC_H */ -- cgit v0.10.2 From 0346223a2fefeda6e453e3dde44dc95c32d54d04 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 23 Jun 2016 00:26:01 +0530 Subject: ACPI / video: Dummy acpi_video_register should return error code The inline acpi_video_register stub simply allows compilation on systems with CONFIG_ACPI_VIDEO disabled. the dummy acpi_video_register does not register an acpi_bus_driver at all. The inline acpi_video_register should return to indicate lack of support when attempting to register an acpi_bus_driver on such a system with CONFIG_ACPI_VIDEO disabled. Signed-off-by: Arvind Yadav Reviewed-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/video.h b/include/acpi/video.h index 5731ccb..4536bd3 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -54,7 +54,7 @@ extern int acpi_video_get_levels(struct acpi_device *device, struct acpi_video_device_brightness **dev_br, int *pmax_level); #else -static inline int acpi_video_register(void) { return 0; } +static inline int acpi_video_register(void) { return -ENODEV; } static inline void acpi_video_unregister(void) { return; } static inline int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid) -- cgit v0.10.2 From 0afa877a5650dd046500b2f8ef08c90862e7ab6b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Jun 2016 17:55:39 +0300 Subject: ACPI / PMIC: intel: add REGS operation region support At least some of the Broxtons have a third custom OpRegion named REGS. This adds handling for it. Signed-off-by: Felipe Balbi Signed-off-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index 410e96f..e107e27 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -21,12 +21,19 @@ #define PMIC_POWER_OPREGION_ID 0x8d #define PMIC_THERMAL_OPREGION_ID 0x8c +#define PMIC_REGS_OPREGION_ID 0x8f + +struct intel_pmic_regs_handler_ctx { + unsigned int val; + u16 addr; +}; struct intel_pmic_opregion { struct mutex lock; struct acpi_lpat_conversion_table *lpat_table; struct regmap *regmap; struct intel_pmic_opregion_data *data; + struct intel_pmic_regs_handler_ctx ctx; }; static int pmic_get_reg_bit(int address, struct pmic_table *table, @@ -204,6 +211,48 @@ static acpi_status intel_pmic_thermal_handler(u32 function, return AE_OK; } +static acpi_status intel_pmic_regs_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + int result; + + switch (address) { + case 0: + return AE_OK; + case 1: + opregion->ctx.addr |= (*value64 & 0xff) << 8; + return AE_OK; + case 2: + opregion->ctx.addr |= *value64 & 0xff; + return AE_OK; + case 3: + opregion->ctx.val = *value64 & 0xff; + return AE_OK; + case 4: + if (*value64) { + result = regmap_write(opregion->regmap, opregion->ctx.addr, + opregion->ctx.val); + } else { + result = regmap_read(opregion->regmap, opregion->ctx.addr, + &opregion->ctx.val); + if (result == 0) + *value64 = opregion->ctx.val; + } + memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); + } + + if (result < 0) { + if (result == -EINVAL) + return AE_BAD_PARAMETER; + else + return AE_ERROR; + } + + return AE_OK; +} + int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d) @@ -243,12 +292,28 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, intel_pmic_power_handler); ret = -ENODEV; - goto out_error; + goto out_remove_power_handler; + } + + status = acpi_install_address_space_handler(handle, + PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL, + opregion); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_remove_thermal_handler; } opregion->data = d; return 0; +out_remove_thermal_handler: + acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, + intel_pmic_thermal_handler); + +out_remove_power_handler: + acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler); + out_error: acpi_lpat_free_conversion_table(opregion->lpat_table); return ret; -- cgit v0.10.2 From 730de199b389e83da8d3935f82e78e30b9876b56 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 22 Jun 2016 17:55:40 +0300 Subject: ACPI / PMIC: intel: initialize result to 0 Fixes compiler warning. Signed-off-by: Felipe Balbi Signed-off-by: Heikki Krogerus Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index e107e27..6585aff 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -216,7 +216,7 @@ static acpi_status intel_pmic_regs_handler(u32 function, void *handler_context, void *region_context) { struct intel_pmic_opregion *opregion = region_context; - int result; + int result = 0; switch (address) { case 0: -- cgit v0.10.2 From a3e2acc5e37b22b6808a0b8e46887c3577de9c9e Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Wed, 29 Jun 2016 13:04:29 -0700 Subject: ACPI / APEI: Add Boot Error Record Table (BERT) support ACPI/APEI is designed to verifiy/report H/W errors, like Corrected Error(CE) and Uncorrected Error(UC). It contains four tables: HEST, ERST, EINJ and BERT. The first three tables have been merged for a long time, but because of lacking BIOS support for BERT, the support for BERT is pending until now. Recently on ARM 64 platform it is has been supported. So here we come. Under normal circumstances, when a hardware error occurs, kernel will be notified via NMI, MCE or some other method, then kernel will process the error condition, report it, and recover it if possible. But sometime, the situation is so bad, so that firmware may choose to reset directly without notifying Linux kernel. Linux kernel can use the Boot Error Record Table (BERT) to get the un-notified hardware errors that occurred in a previous boot. In this patch, the error information is reported via printk. For more information about BERT, please refer to ACPI Specification version 6.0, section 18.3.1: http://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf The following log is a BERT record after system reboot because of hitting a fatal memory error: BERT: Error records from previous boot: [Hardware Error]: It has been corrected by h/w and requires no further action [Hardware Error]: event severity: corrected [Hardware Error]: Error 0, type: recoverable [Hardware Error]: section_type: memory error [Hardware Error]: error_status: 0x0000000000000400 [Hardware Error]: physical_address: 0xffffffffffffffff [Hardware Error]: card: 1 module: 2 bank: 3 row: 1 column: 2 bit_position: 5 [Hardware Error]: error_type: 2, single-bit ECC [Tomasz Nowicki: Clear error status at the end of error handling] [Tony: Applied some cleanups suggested by Fu Wei] [Fu Wei: delete EXPORT_SYMBOL_GPL(bert_disable), improve the code] Signed-off-by: Huang Ying Signed-off-by: Tomasz Nowicki Signed-off-by: Chen, Gong Tested-by: Jonathan (Zhixiong) Zhang Signed-off-by: Fu Wei Tested-by: Tyler Baicar Reviewed-by: Borislav Petkov Signed-off-by: Tony Luck Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c9..f565259 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -582,6 +582,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. bootmem_debug [KNL] Enable bootmem allocator debug messages. + bert_disable [ACPI] + Disable BERT OS support on buggy BIOSes. + bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards) bttv.radio= Most important insmod options are available as kernel args too. diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index 5d575a9..e50573d 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o -apei-y := apei-base.o hest.o erst.o +apei-y := apei-base.o hest.o erst.o bert.o diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index 16129c7..6e9f14c 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -1,6 +1,6 @@ /* * apei-internal.h - ACPI Platform Error Interface internal - * definations. + * definitions. */ #ifndef APEI_INTERNAL_H diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c new file mode 100644 index 0000000..a05b5c0 --- /dev/null +++ b/drivers/acpi/apei/bert.c @@ -0,0 +1,150 @@ +/* + * APEI Boot Error Record Table (BERT) support + * + * Copyright 2011 Intel Corp. + * Author: Huang Ying + * + * Under normal circumstances, when a hardware error occurs, the error + * handler receives control and processes the error. This gives OSPM a + * chance to process the error condition, report it, and optionally attempt + * recovery. In some cases, the system is unable to process an error. + * For example, system firmware or a management controller may choose to + * reset the system or the system might experience an uncontrolled crash + * or reset.The boot error source is used to report unhandled errors that + * occurred in a previous boot. This mechanism is described in the BERT + * table. + * + * For more information about BERT, please refer to ACPI Specification + * version 4.0, section 17.3.1 + * + * This file is licensed under GPLv2. + * + */ + +#include +#include +#include +#include +#include + +#include "apei-internal.h" + +#undef pr_fmt +#define pr_fmt(fmt) "BERT: " fmt + +static int bert_disable; + +static void __init bert_print_all(struct acpi_bert_region *region, + unsigned int region_len) +{ + struct acpi_hest_generic_status *estatus = + (struct acpi_hest_generic_status *)region; + int remain = region_len; + u32 estatus_len; + + if (!estatus->block_status) + return; + + while (remain > sizeof(struct acpi_bert_region)) { + if (cper_estatus_check(estatus)) { + pr_err(FW_BUG "Invalid error record.\n"); + return; + } + + estatus_len = cper_estatus_len(estatus); + if (remain < estatus_len) { + pr_err(FW_BUG "Truncated status block (length: %u).\n", + estatus_len); + return; + } + + pr_info_once("Error records from previous boot:\n"); + + cper_estatus_print(KERN_INFO HW_ERR, estatus); + + /* + * Because the boot error source is "one-time polled" type, + * clear Block Status of current Generic Error Status Block, + * once it's printed. + */ + estatus->block_status = 0; + + estatus = (void *)estatus + estatus_len; + /* No more error records. */ + if (!estatus->block_status) + return; + + remain -= estatus_len; + } +} + +static int __init setup_bert_disable(char *str) +{ + bert_disable = 1; + + return 0; +} +__setup("bert_disable", setup_bert_disable); + +static int __init bert_check_table(struct acpi_table_bert *bert_tab) +{ + if (bert_tab->header.length < sizeof(struct acpi_table_bert) || + bert_tab->region_length < sizeof(struct acpi_bert_region)) + return -EINVAL; + + return 0; +} + +static int __init bert_init(void) +{ + struct acpi_bert_region *boot_error_region; + struct acpi_table_bert *bert_tab; + unsigned int region_len; + acpi_status status; + int rc = 0; + + if (acpi_disabled) + return 0; + + if (bert_disable) { + pr_info("Boot Error Record Table support is disabled.\n"); + return 0; + } + + status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab); + if (status == AE_NOT_FOUND) + return 0; + + if (ACPI_FAILURE(status)) { + pr_err("get table failed, %s.\n", acpi_format_exception(status)); + return -EINVAL; + } + + rc = bert_check_table(bert_tab); + if (rc) { + pr_err(FW_BUG "table invalid.\n"); + return rc; + } + + region_len = bert_tab->region_length; + if (!request_mem_region(bert_tab->address, region_len, "APEI BERT")) { + pr_err("Can't request iomem region <%016llx-%016llx>.\n", + (unsigned long long)bert_tab->address, + (unsigned long long)bert_tab->address + region_len - 1); + return -EIO; + } + + boot_error_region = ioremap_cache(bert_tab->address, region_len); + if (boot_error_region) { + bert_print_all(boot_error_region, region_len); + iounmap(boot_error_region); + } else { + rc = -ENOMEM; + } + + release_mem_region(bert_tab->address, region_len); + + return rc; +} + +late_initcall(bert_init); -- cgit v0.10.2 From 2c85025c75dfe7ddc2bb33363a998dad59383f94 Mon Sep 17 00:00:00 2001 From: Ocean He Date: Mon, 27 Jun 2016 14:50:16 +0000 Subject: ACPI: Execute _PTS before system reboot The _PTS control method is defined in the section 7.4.1 of acpi 6.0 spec. The _PTS control method is executed by the OS during the sleep transition process for S1, S2, S3, S4, and for orderly S5 shutdown. The _PTS control method provides the BIOS a mechanism for performing some housekeeping, such as writing the sleep type value to the embedded controller, before entering the system sleeping state. Note that some Lenovo Server BIOS use this mechanism to detect reboot event and prompt user by popped dialog box. According to section 7.5 of acpi 6.0 spec, _PTS should run after _TTS. Add a _PTS evaulation to the existing _TTS reboot notifier and change the notifier name to reflect the fact that it's not for _TTS only any more. Signed-off-by: Ocean He Signed-off-by: Nagananda Chumbalkar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 7a2e4d4..2b38c1b 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -47,15 +47,32 @@ static void acpi_sleep_tts_switch(u32 acpi_state) } } -static int tts_notify_reboot(struct notifier_block *this, +static void acpi_sleep_pts_switch(u32 acpi_state) +{ + acpi_status status; + + status = acpi_execute_simple_method(NULL, "\\_PTS", acpi_state); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + /* + * OS can't evaluate the _PTS object correctly. Some warning + * message will be printed. But it won't break anything. + */ + printk(KERN_NOTICE "Failure in evaluating _PTS object\n"); + } +} + +static int sleep_notify_reboot(struct notifier_block *this, unsigned long code, void *x) { acpi_sleep_tts_switch(ACPI_STATE_S5); + + acpi_sleep_pts_switch(ACPI_STATE_S5); + return NOTIFY_DONE; } -static struct notifier_block tts_notifier = { - .notifier_call = tts_notify_reboot, +static struct notifier_block sleep_notifier = { + .notifier_call = sleep_notify_reboot, .next = NULL, .priority = 0, }; @@ -899,9 +916,9 @@ int __init acpi_sleep_init(void) pr_info(PREFIX "(supports%s)\n", supported); /* - * Register the tts_notifier to reboot notifier list so that the _TTS - * object can also be evaluated when the system enters S5. + * Register the sleep_notifier to reboot notifier list so that the _TTS + * and _PTS object can also be evaluated when the system enters S5. */ - register_reboot_notifier(&tts_notifier); + register_reboot_notifier(&sleep_notifier); return 0; } -- cgit v0.10.2 From fd6231e785f1287446fc3c8430dba0ae1119d522 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 21 Jun 2016 17:42:05 +0800 Subject: ACPI / EC: Cleanup boot EC code using acpi_ec_alloc() Failure handling of the boot EC code is not tidy. This patch cleans them up with acpi_ec_alloc(). This patch also changes acpi_ec_dsdt_probe(), always switches the boot EC from the ECDT one to the DSDT one in this function. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 73c76d6..f63a43a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1348,13 +1348,9 @@ static void ec_remove_handlers(struct acpi_ec *ec) } } -static int acpi_ec_add(struct acpi_device *device) +static struct acpi_ec *acpi_ec_alloc(void) { - struct acpi_ec *ec = NULL; - int ret; - - strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_EC_CLASS); + struct acpi_ec *ec; /* Check for boot EC */ if (boot_ec) { @@ -1365,9 +1361,21 @@ static int acpi_ec_add(struct acpi_device *device) first_ec = NULL; } else { ec = make_acpi_ec(); - if (!ec) - return -ENOMEM; } + return ec; +} + +static int acpi_ec_add(struct acpi_device *device) +{ + struct acpi_ec *ec = NULL; + int ret; + + strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_CLASS); + + ec = acpi_ec_alloc(); + if (!ec) + return -ENOMEM; if (ec_parse_device(device->handle, 0, ec, NULL) != AE_CTRL_TERMINATE) { kfree(ec); @@ -1454,27 +1462,31 @@ static const struct acpi_device_id ec_device_ids[] = { int __init acpi_ec_dsdt_probe(void) { acpi_status status; + struct acpi_ec *ec; + int ret; - if (boot_ec) - return 0; - + ec = acpi_ec_alloc(); + if (!ec) + return -ENOMEM; /* * Finding EC from DSDT if there is no ECDT EC available. When this * function is invoked, ACPI tables have been fully loaded, we can * walk namespace now. */ - boot_ec = make_acpi_ec(); - if (!boot_ec) - return -ENOMEM; status = acpi_get_devices(ec_device_ids[0].id, - ec_parse_device, boot_ec, NULL); - if (ACPI_FAILURE(status) || !boot_ec->handle) - return -ENODEV; - if (!ec_install_handlers(boot_ec)) { - first_ec = boot_ec; - return 0; + ec_parse_device, ec, NULL); + if (ACPI_FAILURE(status) || !ec->handle) { + ret = -ENODEV; + goto error; } - return -EFAULT; + ret = ec_install_handlers(ec); + +error: + if (ret) + kfree(ec); + else + first_ec = boot_ec = ec; + return ret; } #if 0 @@ -1548,12 +1560,13 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { int __init acpi_ec_ecdt_probe(void) { - int ret = 0; + int ret; acpi_status status; struct acpi_table_ecdt *ecdt_ptr; + struct acpi_ec *ec; - boot_ec = make_acpi_ec(); - if (!boot_ec) + ec = acpi_ec_alloc(); + if (!ec) return -ENOMEM; /* * Generate a boot ec context @@ -1583,22 +1596,20 @@ int __init acpi_ec_ecdt_probe(void) * MSI MS-171F * https://bugzilla.kernel.org/show_bug.cgi?id=12461 */ - boot_ec->command_addr = ecdt_ptr->data.address; - boot_ec->data_addr = ecdt_ptr->control.address; + ec->command_addr = ecdt_ptr->data.address; + ec->data_addr = ecdt_ptr->control.address; } else { - boot_ec->command_addr = ecdt_ptr->control.address; - boot_ec->data_addr = ecdt_ptr->data.address; + ec->command_addr = ecdt_ptr->control.address; + ec->data_addr = ecdt_ptr->data.address; } - boot_ec->gpe = ecdt_ptr->gpe; - boot_ec->handle = ACPI_ROOT_OBJECT; - ret = ec_install_handlers(boot_ec); - if (!ret) - first_ec = boot_ec; + ec->gpe = ecdt_ptr->gpe; + ec->handle = ACPI_ROOT_OBJECT; + ret = ec_install_handlers(ec); error: - if (ret) { - kfree(boot_ec); - boot_ec = NULL; - } + if (ret) + kfree(ec); + else + first_ec = boot_ec = ec; return ret; } -- cgit v0.10.2 From bc539567ab521ff10c6060ccb6f7665561551f72 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 21 Jun 2016 17:42:12 +0800 Subject: ACPI / EC: Remove wrong ECDT correction quirks Our Windows probe result shows that EC._REG is evaluated after evaluating all _INI/_STA control methods. With boot EC always switched in acpi_ec_dsdt_probe(), we can see that as long as there is no EC opregion accesses in the MLC (module level code, AML code out of any control methods) and in _INI/_STA, there is no need to make sure that ECDT must be correct. Bugs of 9399/12461 were reported against an order issue that BAT0/1._STA evaluations contain EC accesses while the ECDT setting is wrong. >From the acpidump output posted on bug 9399, we can see that it is actually a different issue. In this table, if EC._REG is not executed, EC accesses will be done in a platform specific manner. As we've already ensured not to execute EC._REG during the eary stage, we can remove the quirks for bug 9399. From the acpidump output posted on bug 12461, we can see that it still needs the quirk. In this table, EC._REG flags a named object whose default value is One, thus BAT1._STA surely should invoke EC accesses whatever we invoke EC._REG or not. We have to keep the quirk for it before we can root cause the issue. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index f63a43a..b1050a0 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1530,6 +1530,11 @@ static int ec_clear_on_resume(const struct dmi_system_id *id) return 0; } +/* + * Some ECDTs contain wrong register addresses. + * MSI MS-171F + * https://bugzilla.kernel.org/show_bug.cgi?id=12461 + */ static int ec_correct_ecdt(const struct dmi_system_id *id) { pr_debug("Detected system needing ECDT address correction.\n"); @@ -1539,16 +1544,6 @@ static int ec_correct_ecdt(const struct dmi_system_id *id) static struct dmi_system_id ec_dmi_table[] __initdata = { { - ec_correct_ecdt, "Asus L4R", { - DMI_MATCH(DMI_BIOS_VERSION, "1008.006"), - DMI_MATCH(DMI_PRODUCT_NAME, "L4R"), - DMI_MATCH(DMI_BOARD_NAME, "L4R") }, NULL}, - { - ec_correct_ecdt, "Asus M6R", { - DMI_MATCH(DMI_BIOS_VERSION, "0207"), - DMI_MATCH(DMI_PRODUCT_NAME, "M6R"), - DMI_MATCH(DMI_BOARD_NAME, "M6R") }, NULL}, - { ec_correct_ecdt, "MSI MS-171F", { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, @@ -1590,12 +1585,6 @@ int __init acpi_ec_ecdt_probe(void) pr_info("EC description table is found, configuring boot EC\n"); if (EC_FLAGS_CORRECT_ECDT) { - /* - * Asus L4R, Asus M6R - * https://bugzilla.kernel.org/show_bug.cgi?id=9399 - * MSI MS-171F - * https://bugzilla.kernel.org/show_bug.cgi?id=12461 - */ ec->command_addr = ecdt_ptr->data.address; ec->data_addr = ecdt_ptr->control.address; } else { -- cgit v0.10.2 From 3af03f77cb6638c8384c712f8fc7febe07ae2424 Mon Sep 17 00:00:00 2001 From: Baoquan He Date: Mon, 4 Jul 2016 11:13:59 +0800 Subject: ACPI / bus: Correct the comments about acpi_subsystem_init() In acpi_subsystem_init(), function acpi_enable_subsystem() is called to do the real job. However with different flags passed to acpi_enable_subsystem(), different code is executed. In acpi_subsystem_init(), with "~ACPI_NO_ACPI_ENABLE" passed in, it will only switch over the platform to the ACPI mode. The remaining part of acpi_enable_subsystem() is done when acpi_bus_init() is called. So the comments above acpi_subsystem_init() is not exact, change it here. Signed-off-by: Baoquan He Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a6bb8ae..9342f4b 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -956,8 +956,7 @@ void __init acpi_early_init(void) /** * acpi_subsystem_init - Finalize the early initialization of ACPI. * - * Switch over the platform to the ACPI mode (if possible), initialize the - * handling of ACPI events, install the interrupt and global lock handlers. + * Switch over the platform to the ACPI mode (if possible). * * Doing this too early is generally unsafe, but at the same time it needs to be * done before all things that really depend on ACPI. The right spot appears to -- cgit v0.10.2 From baa0c019b4c6eb1759cf55e7858badca4758c143 Mon Sep 17 00:00:00 2001 From: "Prakash, Prashanth" Date: Thu, 23 Jun 2016 12:24:26 -0600 Subject: ACPI / bus: Support for platform initiated graceful shutdown This patch adds support for platform initited graceful shutdown as described in sections 5.6.6(Table-143) and 6.3.5.1 of ACPI 6.1 spec The OSPM will get a graceful shutdown request via a Notify operator on \_SB device with a value of 0x81 per section 5.6.6. Following the shutdown request from platform the OSPM needs to follow the processing sequence as described in section 6.2.5.1. v3 * Switched to regular work with delays from delayed work * Dropped changes to actypes.h * Small style changes v2 * Switched from standalone driver to a simple notify handler v1 * Initial Cc: "Rafael J. Wysocki" Signed-off-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 9342f4b..64a3fb0 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #ifdef CONFIG_X86 #include #endif @@ -470,6 +473,56 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device) acpi_device_notify); } +/* Handle events targeting \_SB device (at present only graceful shutdown) */ + +#define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81 +#define ACPI_SB_INDICATE_INTERVAL 10000 + +static void sb_notify_work(struct work_struct *dummy) +{ + acpi_handle sb_handle; + + orderly_poweroff(true); + + /* + * After initiating graceful shutdown, the ACPI spec requires OSPM + * to evaluate _OST method once every 10seconds to indicate that + * the shutdown is in progress + */ + acpi_get_handle(NULL, "\\_SB", &sb_handle); + while (1) { + pr_info("Graceful shutdown in progress.\n"); + acpi_evaluate_ost(sb_handle, ACPI_OST_EC_OSPM_SHUTDOWN, + ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS, NULL); + msleep(ACPI_SB_INDICATE_INTERVAL); + } +} + +static void acpi_sb_notify(acpi_handle handle, u32 event, void *data) +{ + static DECLARE_WORK(acpi_sb_work, sb_notify_work); + + if (event == ACPI_SB_NOTIFY_SHUTDOWN_REQUEST) { + if (!work_busy(&acpi_sb_work)) + schedule_work(&acpi_sb_work); + } else + pr_warn("event %x is not supported by \\_SB device\n", event); +} + +static int __init acpi_setup_sb_notify_handler(void) +{ + acpi_handle sb_handle; + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &sb_handle))) + return -ENXIO; + + if (ACPI_FAILURE(acpi_install_notify_handler(sb_handle, ACPI_DEVICE_NOTIFY, + acpi_sb_notify, NULL))) + return -EINVAL; + + return 0; +} + /* -------------------------------------------------------------------------- Device Matching -------------------------------------------------------------------------- */ @@ -1118,6 +1171,7 @@ static int __init acpi_init(void) acpi_sleep_proc_init(); acpi_wakeup_device_init(); acpi_debugger_init(); + acpi_setup_sb_notify_handler(); return 0; } -- cgit v0.10.2 From 14d24c39e7f4de1bd3aa3a0379ad3c0d8129a155 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:07 +0300 Subject: ACPI / documentation: add SSDT overlays documentation Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt new file mode 100644 index 0000000..8050259 --- /dev/null +++ b/Documentation/acpi/ssdt-overlays.txt @@ -0,0 +1,91 @@ + +In order to support ACPI open-ended hardware configurations (e.g. development +boards) we need a way to augment the ACPI configuration provided by the firmware +image. A common example is connecting sensors on I2C / SPI buses on development +boards. + +Although this can be accomplished by creating a kernel platform driver or +recompiling the firmware image with updated ACPI tables, neither is practical: +the former proliferates board specific kernel code while the latter requires +access to firmware tools which are often not publicly available. + +Because ACPI supports external references in AML code a more practical +way to augment firmware ACPI configuration is by dynamically loading +user defined SSDT tables that contain the board specific information. + +For example, to enumerate a Bosch BMA222E accelerometer on the I2C bus of the +Minnowboard MAX development board exposed via the LSE connector [1], the +following ASL code can be used: + +DefinitionBlock ("minnowmax.aml", "SSDT", 1, "Vendor", "Accel", 0x00000003) +{ + External (\_SB.I2C6, DeviceObj) + + Scope (\_SB.I2C6) + { + Device (STAC) + { + Name (_ADR, Zero) + Name (_HID, "BMA222E") + + Method (_CRS, 0, Serialized) + { + Name (RBUF, ResourceTemplate () + { + I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80, + AddressingMode7Bit, "\\_SB.I2C6", 0x00, + ResourceConsumer, ,) + GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000, + "\\_SB.GPO2", 0x00, ResourceConsumer, , ) + { // Pin list + 0 + } + }) + Return (RBUF) + } + } + } +} + +which can then be compiled to AML binary format: + +$ iasl minnowmax.asl + +Intel ACPI Component Architecture +ASL Optimizing Compiler version 20140214-64 [Mar 29 2014] +Copyright (c) 2000 - 2014 Intel Corporation + +ASL Input: minnomax.asl - 30 lines, 614 bytes, 7 keywords +AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes + +[1] http://wiki.minnowboard.org/MinnowBoard_MAX#Low_Speed_Expansion_Connector_.28Top.29 + +The resulting AML code can then be loaded by the kernel using one of the methods +below. + +== Loading ACPI SSDTs from initrd == + +This option allows loading of user defined SSDTs from initrd and it is useful +when the system does not support EFI or when there is not enough EFI storage. + +It works in a similar way with initrd based ACPI tables override/upgrade: SSDT +aml code must be placed in the first, uncompressed, initrd under the +"kernel/firmware/acpi" path. Multiple files can be used and this will translate +in loading multiple tables. Only SSDT and OEM tables are allowed. See +initrd_table_override.txt for more details. + +Here is an example: + +# Add the raw ACPI tables to an uncompressed cpio archive. +# They must be put into a /kernel/firmware/acpi directory inside the +# cpio archive. +# The uncompressed cpio archive must be the first. +# Other, typically compressed cpio archives, must be +# concatenated on top of the uncompressed one. +mkdir -p kernel/firmware/acpi +cp ssdt.aml kernel/firmware/acpi + +# Create the uncompressed cpio archive and concatenate the original initrd +# on top: +find kernel | cpio -H newc --create > /boot/instrumented_initrd +cat /boot/initrd >>/boot/instrumented_initrd -- cgit v0.10.2 From 10c7e20b2ff3caa5a8c0e7d60aef5a9c86e60ce8 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:08 +0300 Subject: ACPI / scan: fix enumeration (visited) flags for bus rescans If the ACPI tables change as a result of a dinamically loaded table and a bus rescan is required the enumeration/visited flag are not consistent. I2C/SPI are not directly enumerated in acpi_bus_attach(), however the visited flag is set. This makes it impossible to check if an ACPI device has already been enumerated by the I2C and SPI subsystems. To fix this issue we only set the visited flags if the device is not I2C or SPI. With this change we also need to remove setting visited to false from acpi_bus_attach(), otherwise if we rescan already enumerated I2C/SPI devices we try to re-enumerate them. Note that I2C/SPI devices can be enumerated either via a scan handler (when using PRP0001) or via regular device_attach(). In either case the flow goes through acpi_default_enumeration() which makes it the ideal place to mark the ACPI device as enumerated. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5f28cf7..f80f8a7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1406,7 +1406,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; - device->flags.visited = false; + acpi_device_clear_enumerated(device); device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); acpi_init_coherency(device); @@ -1683,8 +1683,10 @@ static void acpi_default_enumeration(struct acpi_device *device) acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, &is_spi_i2c_slave); acpi_dev_free_resource_list(&resource_list); - if (!is_spi_i2c_slave) + if (!is_spi_i2c_slave) { acpi_create_platform_device(device); + acpi_device_set_enumerated(device); + } } static const struct acpi_device_id generic_device_ids[] = { @@ -1751,7 +1753,7 @@ static void acpi_bus_attach(struct acpi_device *device) acpi_bus_get_status(device); /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) { - device->flags.visited = false; + acpi_device_clear_enumerated(device); device->flags.power_manageable = 0; return; } @@ -1766,7 +1768,7 @@ static void acpi_bus_attach(struct acpi_device *device) device->flags.initialized = true; } - device->flags.visited = false; + ret = acpi_scan_attach_handler(device); if (ret < 0) return; @@ -1780,7 +1782,6 @@ static void acpi_bus_attach(struct acpi_device *device) if (!ret && device->pnp.type.platform_id) acpi_default_enumeration(device); } - device->flags.visited = true; ok: list_for_each_entry(child, &device->children, node) @@ -1872,7 +1873,7 @@ void acpi_bus_trim(struct acpi_device *adev) */ acpi_device_set_power(adev, ACPI_STATE_D3_COLD); adev->flags.initialized = false; - adev->flags.visited = false; + acpi_device_clear_enumerated(adev); } EXPORT_SYMBOL_GPL(acpi_bus_trim); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index ef2ad26..db680e8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -531,6 +531,16 @@ void acpi_walk_dep_device_list(acpi_handle handle); struct platform_device *acpi_create_platform_device(struct acpi_device *); #define ACPI_PTR(_ptr) (_ptr) +static inline void acpi_device_set_enumerated(struct acpi_device *adev) +{ + adev->flags.visited = true; +} + +static inline void acpi_device_clear_enumerated(struct acpi_device *adev) +{ + adev->flags.visited = false; +} + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 @@ -676,6 +686,14 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev) #define ACPI_PTR(_ptr) (NULL) +static inline void acpi_device_set_enumerated(struct acpi_device *adev) +{ +} + +static inline void acpi_device_clear_enumerated(struct acpi_device *adev) +{ +} + #endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI -- cgit v0.10.2 From 68bdb6773289f8c9a36633f9f6525b127c093258 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:09 +0300 Subject: ACPI: add support for ACPI reconfiguration notifiers Add support for ACPI reconfiguration notifiers to allow subsystems to react to changes in the ACPI tables that happen after the initial enumeration. This is similar with the way dynamic device tree notifications work. The reconfigure notifications supported for now are device add and device remove. Since ACPICA allows only one table notification handler, this patch makes the table notifier function generic and moves it out of the sysfs specific code. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 262ca31..97e270e 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -990,6 +990,13 @@ void __init acpi_subsystem_init(void) } } +static acpi_status acpi_bus_table_handler(u32 event, void *table, void *context) +{ + acpi_scan_table_handler(event, table, context); + + return acpi_sysfs_table_handler(event, table, context); +} + static int __init acpi_bus_init(void) { int result; @@ -1043,6 +1050,8 @@ static int __init acpi_bus_init(void) * _PDC control method may load dynamic SSDT tables, * and we need to install the table handler before that. */ + status = acpi_install_table_handler(acpi_bus_table_handler, NULL); + acpi_sysfs_init(); acpi_early_processor_set_pdc(); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 27cc7fe..940218f 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -87,6 +87,9 @@ bool acpi_queue_hotplug_work(struct work_struct *work); void acpi_device_hotplug(struct acpi_device *adev, u32 src); bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); +acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context); +void acpi_scan_table_handler(u32 event, void *table, void *context); + /* -------------------------------------------------------------------------- Device Node Initialization / Removal -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f80f8a7..405056b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -494,6 +494,8 @@ static void acpi_device_del(struct acpi_device *device) device_del(&device->dev); } +static BLOCKING_NOTIFIER_HEAD(acpi_reconfig_chain); + static LIST_HEAD(acpi_device_del_list); static DEFINE_MUTEX(acpi_device_del_lock); @@ -514,6 +516,9 @@ static void acpi_device_del_work_fn(struct work_struct *work_not_used) mutex_unlock(&acpi_device_del_lock); + blocking_notifier_call_chain(&acpi_reconfig_chain, + ACPI_RECONFIG_DEVICE_REMOVE, adev); + acpi_device_del(adev); /* * Drop references to all power resources that might have been @@ -1676,7 +1681,7 @@ static void acpi_default_enumeration(struct acpi_device *device) bool is_spi_i2c_slave = false; /* - * Do not enemerate SPI/I2C slaves as they will be enuerated by their + * Do not enumerate SPI/I2C slaves as they will be enumerated by their * respective parents. */ INIT_LIST_HEAD(&resource_list); @@ -1686,6 +1691,9 @@ static void acpi_default_enumeration(struct acpi_device *device) if (!is_spi_i2c_slave) { acpi_create_platform_device(device); acpi_device_set_enumerated(device); + } else { + blocking_notifier_call_chain(&acpi_reconfig_chain, + ACPI_RECONFIG_DEVICE_ADD, device); } } @@ -1917,6 +1925,8 @@ static int acpi_bus_scan_fixed(void) return result < 0 ? result : 0; } +static bool acpi_scan_initialized; + int __init acpi_scan_init(void) { int result; @@ -1961,6 +1971,8 @@ int __init acpi_scan_init(void) acpi_update_all_gpes(); + acpi_scan_initialized = true; + out: mutex_unlock(&acpi_scan_lock); return result; @@ -2004,3 +2016,57 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr) return count; } + +struct acpi_table_events_work { + struct work_struct work; + void *table; + u32 event; +}; + +static void acpi_table_events_fn(struct work_struct *work) +{ + struct acpi_table_events_work *tew; + + tew = container_of(work, struct acpi_table_events_work, work); + + if (tew->event == ACPI_TABLE_EVENT_LOAD) { + acpi_scan_lock_acquire(); + acpi_bus_scan(ACPI_ROOT_OBJECT); + acpi_scan_lock_release(); + } + + kfree(tew); +} + +void acpi_scan_table_handler(u32 event, void *table, void *context) +{ + struct acpi_table_events_work *tew; + + if (!acpi_scan_initialized) + return; + + if (event != ACPI_TABLE_EVENT_LOAD) + return; + + tew = kmalloc(sizeof(*tew), GFP_KERNEL); + if (!tew) + return; + + INIT_WORK(&tew->work, acpi_table_events_fn); + tew->table = table; + tew->event = event; + + schedule_work(&tew->work); +} + +int acpi_reconfig_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&acpi_reconfig_chain, nb); +} +EXPORT_SYMBOL(acpi_reconfig_notifier_register); + +int acpi_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&acpi_reconfig_chain, nb); +} +EXPORT_SYMBOL(acpi_reconfig_notifier_unregister); diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 4b3a9e2..358165e 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -378,8 +378,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr, return; } -static acpi_status -acpi_sysfs_table_handler(u32 event, void *table, void *context) +acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) { struct acpi_table_attr *table_attr; @@ -452,9 +451,8 @@ static int acpi_tables_sysfs_init(void) kobject_uevent(tables_kobj, KOBJ_ADD); kobject_uevent(dynamic_tables_kobj, KOBJ_ADD); - status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL); - return ACPI_FAILURE(status) ? -EINVAL : 0; + return 0; err_dynamic_tables: kobject_put(tables_kobj); err: diff --git a/include/linux/acpi.h b/include/linux/acpi.h index db680e8..8b48de1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -541,6 +541,14 @@ static inline void acpi_device_clear_enumerated(struct acpi_device *adev) adev->flags.visited = false; } +enum acpi_reconfig_event { + ACPI_RECONFIG_DEVICE_ADD = 0, + ACPI_RECONFIG_DEVICE_REMOVE, +}; + +int acpi_reconfig_notifier_register(struct notifier_block *nb); +int acpi_reconfig_notifier_unregister(struct notifier_block *nb); + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 @@ -694,6 +702,16 @@ static inline void acpi_device_clear_enumerated(struct acpi_device *adev) { } +static inline int acpi_reconfig_notifier_register(struct notifier_block *nb) +{ + return -EINVAL; +} + +static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return -EINVAL; +} + #endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI -- cgit v0.10.2 From 525e6fabeae286848592363bda13bc34b59bb5ac Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:10 +0300 Subject: i2c / ACPI: add support for ACPI reconfigure notifications This patch adds supports for I2C device enumeration and removal via ACPI reconfiguration notifications that are send as a result of an ACPI table load or unload operation. The code is very similar with the device tree reconfiguration code with only small differences in the way we test and set the enumerated state of the device: * the equivalent of device tree's OF_POPULATED flag is the flags.visited field in the ACPI device and the following wrappers are used to manipulate it: acpi_device_enumerated(), acpi_device_set_enumerated() and acpi_device_clear_enumerated() * the device tree code checks of status of the OF_POPULATED flag to avoid trying to create duplicate Linux devices in two places: once when the controller is probed, and once when the reconfigure event is received; in the ACPI code the check is performed only once when the ACPI namespace is searched because this code path is invoked in both of the two mentioned cases The rest of the enumeration handling is similar with device tree: when the Linux device is unregistered the ACPI device is marked as not enumerated; also, when a device remove notification is received we check that the device is in the enumerated state before continuing with the removal of the Linux device. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Acked-by: Wolfram Sang Signed-off-by: Rafael J. Wysocki diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index af11b65..74e5aea 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -107,12 +107,11 @@ struct acpi_i2c_lookup { acpi_handle device_handle; }; -static int acpi_i2c_find_address(struct acpi_resource *ares, void *data) +static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data) { struct acpi_i2c_lookup *lookup = data; struct i2c_board_info *info = lookup->info; struct acpi_resource_i2c_serialbus *sb; - acpi_handle adapter_handle; acpi_status status; if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) @@ -122,80 +121,102 @@ static int acpi_i2c_find_address(struct acpi_resource *ares, void *data) if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) return 1; - /* - * Extract the ResourceSource and make sure that the handle matches - * with the I2C adapter handle. - */ status = acpi_get_handle(lookup->device_handle, sb->resource_source.string_ptr, - &adapter_handle); - if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) { - info->addr = sb->slave_address; - if (sb->access_mode == ACPI_I2C_10BIT_MODE) - info->flags |= I2C_CLIENT_TEN; - } + &lookup->adapter_handle); + if (!ACPI_SUCCESS(status)) + return 1; + + info->addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; return 1; } -static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) +static int acpi_i2c_get_info(struct acpi_device *adev, + struct i2c_board_info *info, + acpi_handle *adapter_handle) { - struct i2c_adapter *adapter = data; struct list_head resource_list; - struct acpi_i2c_lookup lookup; struct resource_entry *entry; - struct i2c_board_info info; - struct acpi_device *adev; + struct acpi_i2c_lookup lookup; int ret; - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - if (acpi_bus_get_status(adev) || !adev->status.present) - return AE_OK; + if (acpi_bus_get_status(adev) || !adev->status.present || + acpi_device_enumerated(adev)) + return -EINVAL; - memset(&info, 0, sizeof(info)); - info.fwnode = acpi_fwnode_handle(adev); + memset(info, 0, sizeof(*info)); + info->fwnode = acpi_fwnode_handle(adev); memset(&lookup, 0, sizeof(lookup)); - lookup.adapter_handle = ACPI_HANDLE(&adapter->dev); - lookup.device_handle = handle; - lookup.info = &info; + lookup.device_handle = acpi_device_handle(adev); + lookup.info = info; - /* - * Look up for I2cSerialBus resource with ResourceSource that - * matches with this adapter. - */ + /* Look up for I2cSerialBus resource */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - acpi_i2c_find_address, &lookup); + acpi_i2c_fill_info, &lookup); acpi_dev_free_resource_list(&resource_list); - if (ret < 0 || !info.addr) - return AE_OK; + if (ret < 0 || !info->addr) + return -EINVAL; + + *adapter_handle = lookup.adapter_handle; /* Then fill IRQ number if any */ ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); if (ret < 0) - return AE_OK; + return -EINVAL; resource_list_for_each_entry(entry, &resource_list) { if (resource_type(entry->res) == IORESOURCE_IRQ) { - info.irq = entry->res->start; + info->irq = entry->res->start; break; } } acpi_dev_free_resource_list(&resource_list); + strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type)); + + return 0; +} + +static void acpi_i2c_register_device(struct i2c_adapter *adapter, + struct acpi_device *adev, + struct i2c_board_info *info) +{ adev->power.flags.ignore_parent = true; - strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); - if (!i2c_new_device(adapter, &info)) { + acpi_device_set_enumerated(adev); + + if (!i2c_new_device(adapter, info)) { adev->power.flags.ignore_parent = false; dev_err(&adapter->dev, "failed to add I2C device %s from ACPI\n", dev_name(&adev->dev)); } +} + +static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct acpi_device *adev; + acpi_handle adapter_handle; + struct i2c_board_info info; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (acpi_i2c_get_info(adev, &info, &adapter_handle)) + return AE_OK; + + if (adapter_handle != ACPI_HANDLE(&adapter->dev)) + return AE_OK; + + acpi_i2c_register_device(adapter, adev, &info); return AE_OK; } @@ -225,8 +246,80 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } +static int acpi_i2c_match_adapter(struct device *dev, void *data) +{ + struct i2c_adapter *adapter = i2c_verify_adapter(dev); + + if (!adapter) + return 0; + + return ACPI_HANDLE(dev) == (acpi_handle)data; +} + +static int acpi_i2c_match_device(struct device *dev, void *data) +{ + return ACPI_COMPANION(dev) == data; +} + +static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, handle, + acpi_i2c_match_adapter); + return dev ? i2c_verify_adapter(dev) : NULL; +} + +static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device); + return dev ? i2c_verify_client(dev) : NULL; +} + +static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value, + void *arg) +{ + struct acpi_device *adev = arg; + struct i2c_board_info info; + acpi_handle adapter_handle; + struct i2c_adapter *adapter; + struct i2c_client *client; + + switch (value) { + case ACPI_RECONFIG_DEVICE_ADD: + if (acpi_i2c_get_info(adev, &info, &adapter_handle)) + break; + + adapter = acpi_i2c_find_adapter_by_handle(adapter_handle); + if (!adapter) + break; + + acpi_i2c_register_device(adapter, adev, &info); + break; + case ACPI_RECONFIG_DEVICE_REMOVE: + if (!acpi_device_enumerated(adev)) + break; + + client = acpi_i2c_find_client_by_adev(adev); + if (!client) + break; + + i2c_unregister_device(client); + put_device(&client->dev); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block i2c_acpi_notifier = { + .notifier_call = acpi_i2c_notify, +}; #else /* CONFIG_ACPI */ static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } +extern struct notifier_block i2c_acpi_notifier; #endif /* CONFIG_ACPI */ #ifdef CONFIG_ACPI_I2C_OPREGION @@ -1089,6 +1182,8 @@ void i2c_unregister_device(struct i2c_client *client) { if (client->dev.of_node) of_node_clear_flag(client->dev.of_node, OF_POPULATED); + if (ACPI_COMPANION(&client->dev)) + acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); device_unregister(&client->dev); } EXPORT_SYMBOL_GPL(i2c_unregister_device); @@ -2117,6 +2212,8 @@ static int __init i2c_init(void) if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier)); return 0; @@ -2132,6 +2229,8 @@ bus_err: static void __exit i2c_exit(void) { + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier)); if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier)); i2c_del_driver(&dummy_driver); -- cgit v0.10.2 From 7f24467f3b357ab6abc146c47fcedd7d57a189b6 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:11 +0300 Subject: spi / ACPI: add support for ACPI reconfigure notifications This patch adds supports for SPI device enumeration and removal via ACPI reconfiguration notifications that are send as a result of an ACPI table load or unload operation. The code is very similar with the device tree reconfiguration code with only small differences in the way we test and set the enumerated state of the device: * the equivalent of device tree's OF_POPULATED flag is the flags.visited field in the ACPI device and the following wrappers are used to manipulate it: acpi_device_enumerated(), acpi_device_set_enumerated() and acpi_device_clear_enumerated() * the device tree code checks of status of the OF_POPULATED flag to avoid trying to create duplicate Linux devices in two places: once when the controller is probed, and once when the reconfigure event is received; in the ACPI code the check is performed only once when the ACPI namespace is searched because this code path is invoked in both of the two mentioned cases The rest of the enumeration handling is similar with device tree: when the Linux device is unregistered the ACPI device is marked as not enumerated; also, when a device remove notification is received we check that the device is in the enumerated state before continuing with the removal of the Linux device. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Acked-by: Mark Brown Signed-off-by: Rafael J. Wysocki diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 77e6e45..7589c8a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -622,6 +622,8 @@ void spi_unregister_device(struct spi_device *spi) if (spi->dev.of_node) of_node_clear_flag(spi->dev.of_node, OF_POPULATED); + if (ACPI_COMPANION(&spi->dev)) + acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev)); device_unregister(&spi->dev); } EXPORT_SYMBOL_GPL(spi_unregister_device); @@ -1646,18 +1648,15 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) return 1; } -static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) +static acpi_status acpi_register_spi_device(struct spi_master *master, + struct acpi_device *adev) { - struct spi_master *master = data; struct list_head resource_list; - struct acpi_device *adev; struct spi_device *spi; int ret; - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - if (acpi_bus_get_status(adev) || !adev->status.present) + if (acpi_bus_get_status(adev) || !adev->status.present || + acpi_device_enumerated(adev)) return AE_OK; spi = spi_alloc_device(master); @@ -1683,6 +1682,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, if (spi->irq < 0) spi->irq = acpi_dev_gpio_irq_get(adev, 0); + acpi_device_set_enumerated(adev); + adev->power.flags.ignore_parent = true; strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias)); if (spi_add_device(spi)) { @@ -1695,6 +1696,18 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, return AE_OK; } +static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct spi_master *master = data; + struct acpi_device *adev; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + return acpi_register_spi_device(master, adev); +} + static void acpi_register_spi_devices(struct spi_master *master) { acpi_status status; @@ -3107,6 +3120,77 @@ static struct notifier_block spi_of_notifier = { extern struct notifier_block spi_of_notifier; #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ +#if IS_ENABLED(CONFIG_ACPI) +static int spi_acpi_master_match(struct device *dev, const void *data) +{ + return ACPI_COMPANION(dev->parent) == data; +} + +static int spi_acpi_device_match(struct device *dev, void *data) +{ + return ACPI_COMPANION(dev) == data; +} + +static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev) +{ + struct device *dev; + + dev = class_find_device(&spi_master_class, NULL, adev, + spi_acpi_master_match); + if (!dev) + return NULL; + + return container_of(dev, struct spi_master, dev); +} + +static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev) +{ + struct device *dev; + + dev = bus_find_device(&spi_bus_type, NULL, adev, spi_acpi_device_match); + + return dev ? to_spi_device(dev) : NULL; +} + +static int acpi_spi_notify(struct notifier_block *nb, unsigned long value, + void *arg) +{ + struct acpi_device *adev = arg; + struct spi_master *master; + struct spi_device *spi; + + switch (value) { + case ACPI_RECONFIG_DEVICE_ADD: + master = acpi_spi_find_master_by_adev(adev->parent); + if (!master) + break; + + acpi_register_spi_device(master, adev); + put_device(&master->dev); + break; + case ACPI_RECONFIG_DEVICE_REMOVE: + if (!acpi_device_enumerated(adev)) + break; + + spi = acpi_spi_find_device_by_adev(adev); + if (!spi) + break; + + spi_unregister_device(spi); + put_device(&spi->dev); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block spi_acpi_notifier = { + .notifier_call = acpi_spi_notify, +}; +#else +extern struct notifier_block spi_acpi_notifier; +#endif + static int __init spi_init(void) { int status; @@ -3127,6 +3211,8 @@ static int __init spi_init(void) if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); + if (IS_ENABLED(CONFIG_ACPI)) + WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier)); return 0; -- cgit v0.10.2 From 475fb4e8b2f4444d1d7b406ff3a7d21bc89a1e6f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:12 +0300 Subject: efi / ACPI: load SSTDs from EFI variables This patch allows SSDTs to be loaded from EFI variables. It works by specifying the EFI variable name containing the SSDT to be loaded. All variables with the same name (regardless of the vendor GUID) will be loaded. Note that we can't use acpi_install_table and we must rely on the dynamic ACPI table loading and bus re-scanning mechanisms. That is because I2C/SPI controllers are initialized earlier then the EFI subsystems and all I2C/SPI ACPI devices are enumerated when the I2C/SPI controllers are initialized. Signed-off-by: Octavian Purdila Reviewed-by: Matt Fleming Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt index 8050259..c4b57ba 100644 --- a/Documentation/acpi/ssdt-overlays.txt +++ b/Documentation/acpi/ssdt-overlays.txt @@ -89,3 +89,70 @@ cp ssdt.aml kernel/firmware/acpi # on top: find kernel | cpio -H newc --create > /boot/instrumented_initrd cat /boot/initrd >>/boot/instrumented_initrd + +== Loading ACPI SSDTs from EFI variables == + +This is the preferred method, when EFI is supported on the platform, because it +allows a persistent, OS independent way of storing the user defined SSDTs. There +is also work underway to implement EFI support for loading user defined SSDTs +and using this method will make it easier to convert to the EFI loading +mechanism when that will arrive. + +In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line +parameter can be used. The argument for the option is the variable name to +use. If there are multiple variables with the same name but with different +vendor GUIDs, all of them will be loaded. + +In order to store the AML code in an EFI variable the efivarfs filesystem can be +used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all +recent distribution. + +Creating a new file in /sys/firmware/efi/efivars will automatically create a new +EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI +variable. Please note that the file name needs to be specially formatted as +"Name-GUID" and that the first 4 bytes in the file (little-endian format) +represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in +include/linux/efi.h). Writing to the file must also be done with one write +operation. + +For example, you can use the following bash script to create/update an EFI +variable with the content from a given file: + +#!/bin/sh -e + +while ! [ -z "$1" ]; do + case "$1" in + "-f") filename="$2"; shift;; + "-g") guid="$2"; shift;; + *) name="$1";; + esac + shift +done + +usage() +{ + echo "Syntax: ${0##*/} -f filename [ -g guid ] name" + exit 1 +} + +[ -n "$name" -a -f "$filename" ] || usage + +EFIVARFS="/sys/firmware/efi/efivars" + +[ -d "$EFIVARFS" ] || exit 2 + +if stat -tf $EFIVARFS | grep -q -v de5e81e4; then + mount -t efivarfs none $EFIVARFS +fi + +# try to pick up an existing GUID +[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-) + +# use a randomly generated GUID +[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)" + +# efivarfs expects all of the data in one write +tmp=$(mktemp) +/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp +dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp) +rm $tmp diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c9..bbfb56f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1185,6 +1185,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Address Range Mirroring feature even if your box doesn't support it. + efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT + that is to be dynamically loaded by Linux. If there are + multiple variables with the same name but with different + vendor GUIDs, all of them will be loaded. See + Documentation/acpi/ssdt-overlays.txt for details. + + eisa_irq_edge= [PARISC,HW] See header of drivers/parisc/eisa.c. diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 05509f3..8730fd4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include @@ -195,6 +198,96 @@ static void generic_ops_unregister(void) efivars_unregister(&generic_efivars); } +#if IS_ENABLED(CONFIG_ACPI) +#define EFIVAR_SSDT_NAME_MAX 16 +static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata; +static int __init efivar_ssdt_setup(char *str) +{ + if (strlen(str) < sizeof(efivar_ssdt)) + memcpy(efivar_ssdt, str, strlen(str)); + else + pr_warn("efivar_ssdt: name too long: %s\n", str); + return 0; +} +__setup("efivar_ssdt=", efivar_ssdt_setup); + +static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor, + unsigned long name_size, void *data) +{ + struct efivar_entry *entry; + struct list_head *list = data; + char utf8_name[EFIVAR_SSDT_NAME_MAX]; + int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size); + + ucs2_as_utf8(utf8_name, name, limit - 1); + if (strncmp(utf8_name, efivar_ssdt, limit) != 0) + return 0; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return 0; + + memcpy(entry->var.VariableName, name, name_size); + memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t)); + + efivar_entry_add(entry, list); + + return 0; +} + +static __init int efivar_ssdt_load(void) +{ + LIST_HEAD(entries); + struct efivar_entry *entry, *aux; + unsigned long size; + void *data; + int ret; + + ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries); + + list_for_each_entry_safe(entry, aux, &entries, list) { + pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, + &entry->var.VendorGuid); + + list_del(&entry->list); + + ret = efivar_entry_size(entry, &size); + if (ret) { + pr_err("failed to get var size\n"); + goto free_entry; + } + + data = kmalloc(size, GFP_KERNEL); + if (!data) + goto free_entry; + + ret = efivar_entry_get(entry, NULL, &size, data); + if (ret) { + pr_err("failed to get var data\n"); + goto free_data; + } + + ret = acpi_load_table(data); + if (ret) { + pr_err("failed to load table: %d\n", ret); + goto free_data; + } + + goto free_entry; + +free_data: + kfree(data); + +free_entry: + kfree(entry); + } + + return ret; +} +#else +static inline int efivar_ssdt_load(void) { return 0; } +#endif + /* * We register the efi subsystem with the firmware subsystem and the * efivars subsystem with the efi subsystem, if the system was booted with @@ -218,6 +311,9 @@ static int __init efisubsys_init(void) if (error) goto err_put; + if (efi_enabled(EFI_RUNTIME_SERVICES)) + efivar_ssdt_load(); + error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); if (error) { pr_err("efi: Sysfs attribute export failed with error %d.\n", -- cgit v0.10.2 From 0bf54fcd95042bd178cb25368422cf4474fc8492 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:13 +0300 Subject: ACPI: add support for configfs Register the ACPI subsystem with configfs. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/configfs-acpi b/Documentation/ABI/testing/configfs-acpi new file mode 100644 index 0000000..17b19dc --- /dev/null +++ b/Documentation/ABI/testing/configfs-acpi @@ -0,0 +1,7 @@ +What: /config/acpi +Date: July 2016 +KernelVersion: 4.8 +Contact: linux-acpi@vger.kernel.org +Description: + This represents the ACPI subsystem entry point directory. It + contains sub-groups corresponding to ACPI configurable options. diff --git a/MAINTAINERS b/MAINTAINERS index e1b090f..d01d8e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -288,6 +288,7 @@ F: include/linux/acpi.h F: include/acpi/ F: Documentation/acpi/ F: Documentation/ABI/testing/sysfs-bus-acpi +F: Documentation/ABI/testing/configfs-acpi F: drivers/pci/*acpi* F: drivers/pci/*/*acpi* F: drivers/pci/*/*/*acpi* diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0a8e41a..b420e5d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -524,4 +524,12 @@ config XPOWER_PMIC_OPREGION endif +config ACPI_CONFIGFS + tristate "ACPI configfs support" + select CONFIGFS_FS + help + Select this option to enable support for ACPI configuration from + userspace. The configurable ACPI groups will be visible under + /config/acpi, assuming configfs is mounted under /config. + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85..1dc2173 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -99,5 +99,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o +obj-$(CONFIG_ACPI_CONFIGFS) += configfs.o video-objs += acpi_video.o video_detect.o diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c new file mode 100644 index 0000000..44b463f --- /dev/null +++ b/drivers/acpi/configfs.c @@ -0,0 +1,53 @@ +/* + * ACPI configfs support + * + * Copyright (c) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include + +static struct config_item_type acpi_root_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem acpi_configfs = { + .su_group = { + .cg_item = { + .ci_namebuf = "acpi", + .ci_type = &acpi_root_group_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), +}; + +static int __init acpi_configfs_init(void) +{ + int ret; + struct config_group *root = &acpi_configfs.su_group; + + config_group_init(root); + + ret = configfs_register_subsystem(&acpi_configfs); + if (ret) + return ret; + + return 0; +} +module_init(acpi_configfs_init); + +static void __exit acpi_configfs_exit(void) +{ + configfs_unregister_subsystem(&acpi_configfs); +} +module_exit(acpi_configfs_exit); + +MODULE_AUTHOR("Octavian Purdila "); +MODULE_DESCRIPTION("ACPI configfs support"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 612bd01fc6e04c3ce9eb59587b4a7e4ebd6aff35 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 8 Jul 2016 19:13:14 +0300 Subject: ACPI: add support for loading SSDTs via configfs New tables can be loaded by creating directories under /config/table/ and writing the AML code into the aml table attribute. Various table attributes will be readable once the table is successfully loaded. Unloading tables is not supported at the moment, but it can be easily implemented once ACPI loading functions provide a table handle to be used for unloading. Signed-off-by: Octavian Purdila Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/ABI/testing/configfs-acpi b/Documentation/ABI/testing/configfs-acpi index 17b19dc..4ab4e99 100644 --- a/Documentation/ABI/testing/configfs-acpi +++ b/Documentation/ABI/testing/configfs-acpi @@ -5,3 +5,32 @@ Contact: linux-acpi@vger.kernel.org Description: This represents the ACPI subsystem entry point directory. It contains sub-groups corresponding to ACPI configurable options. + +What: /config/acpi/table +Date: July 2016 +KernelVersion: 4.8 +Description: + + This group contains the configuration for user defined ACPI + tables. The attributes of a user define table are: + + aml - a binary attribute that the user can use to + fill in the ACPI aml definitions. Once the aml + data is written to this file and the file is + closed the table will be loaded and ACPI devices + will be enumerated. To check if the operation is + successful the user must check the error code + for close(). If the operation is successful, + subsequent writes to this attribute will fail. + + The rest of the attributes are read-only and are valid only + after the table has been loaded by filling the aml entry: + + signature - ASCII table signature + length - length of table in bytes, including the header + revision - ACPI Specification minor version number + oem_id - ASCII OEM identification + oem_table_id - ASCII OEM table identification + oem_revision - OEM revision number + asl_compiler_id - ASCII ASL compiler vendor ID + asl_compiler_revision - ASL compiler version diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt index c4b57ba..5ae13f1 100644 --- a/Documentation/acpi/ssdt-overlays.txt +++ b/Documentation/acpi/ssdt-overlays.txt @@ -156,3 +156,17 @@ tmp=$(mktemp) /bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp) rm $tmp + +== Loading ACPI SSDTs from configfs == + +This option allows loading of user defined SSDTs from userspace via the configfs +interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be +mounted. In the following examples, we assume that configfs has been mounted in +/config. + +New tables can be loading by creating new directories in /config/acpi/table/ and +writing the SSDT aml code in the aml attribute: + +cd /config/acpi/table +mkdir my_ssdt +cat ~/ssdt.aml > my_ssdt/aml diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c index 44b463f..146a77fb 100644 --- a/drivers/acpi/configfs.c +++ b/drivers/acpi/configfs.c @@ -8,11 +8,222 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) "ACPI configfs: " fmt + #include #include #include #include +static struct config_group *acpi_table_group; + +struct acpi_table { + struct config_item cfg; + struct acpi_table_header *header; +}; + +static ssize_t acpi_table_aml_write(struct config_item *cfg, + const void *data, size_t size) +{ + const struct acpi_table_header *header = data; + struct acpi_table *table; + int ret; + + table = container_of(cfg, struct acpi_table, cfg); + + if (table->header) { + pr_err("table already loaded\n"); + return -EBUSY; + } + + if (header->length != size) { + pr_err("invalid table length\n"); + return -EINVAL; + } + + if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { + pr_err("invalid table signature\n"); + return -EINVAL; + } + + table = container_of(cfg, struct acpi_table, cfg); + + table->header = kmemdup(header, header->length, GFP_KERNEL); + if (!table->header) + return -ENOMEM; + + ret = acpi_load_table(table->header); + if (ret) { + kfree(table->header); + table->header = NULL; + } + + return ret; +} + +static inline struct acpi_table_header *get_header(struct config_item *cfg) +{ + struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); + + if (!table->header) + pr_err("table not loaded\n"); + + return table->header; +} + +static ssize_t acpi_table_aml_read(struct config_item *cfg, + void *data, size_t size) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + if (data) + memcpy(data, h, h->length); + + return h->length; +} + +#define MAX_ACPI_TABLE_SIZE (128 * 1024) + +CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); + +struct configfs_bin_attribute *acpi_table_bin_attrs[] = { + &acpi_table_attr_aml, + NULL, +}; + +ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature); +} + +ssize_t acpi_table_length_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->length); +} + +ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->revision); +} + +ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); +} + +ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); +} + +ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->oem_revision); +} + +ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id); +} + +ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, + char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->asl_compiler_revision); +} + +CONFIGFS_ATTR_RO(acpi_table_, signature); +CONFIGFS_ATTR_RO(acpi_table_, length); +CONFIGFS_ATTR_RO(acpi_table_, revision); +CONFIGFS_ATTR_RO(acpi_table_, oem_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_table_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_revision); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); + +struct configfs_attribute *acpi_table_attrs[] = { + &acpi_table_attr_signature, + &acpi_table_attr_length, + &acpi_table_attr_revision, + &acpi_table_attr_oem_id, + &acpi_table_attr_oem_table_id, + &acpi_table_attr_oem_revision, + &acpi_table_attr_asl_compiler_id, + &acpi_table_attr_asl_compiler_revision, + NULL, +}; + +static struct config_item_type acpi_table_type = { + .ct_owner = THIS_MODULE, + .ct_bin_attrs = acpi_table_bin_attrs, + .ct_attrs = acpi_table_attrs, +}; + +static struct config_item *acpi_table_make_item(struct config_group *group, + const char *name) +{ + struct acpi_table *table; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&table->cfg, name, &acpi_table_type); + return &table->cfg; +} + +struct configfs_group_operations acpi_table_group_ops = { + .make_item = acpi_table_make_item, +}; + +static struct config_item_type acpi_tables_type = { + .ct_owner = THIS_MODULE, + .ct_group_ops = &acpi_table_group_ops, +}; + static struct config_item_type acpi_root_group_type = { .ct_owner = THIS_MODULE, }; @@ -38,12 +249,15 @@ static int __init acpi_configfs_init(void) if (ret) return ret; - return 0; + acpi_table_group = configfs_register_default_group(root, "table", + &acpi_tables_type); + return PTR_ERR_OR_ZERO(acpi_table_group); } module_init(acpi_configfs_init); static void __exit acpi_configfs_exit(void) { + configfs_unregister_default_group(acpi_table_group); configfs_unregister_subsystem(&acpi_configfs); } module_exit(acpi_configfs_exit); -- cgit v0.10.2 From 9305aa6fe9069970e5cb8428ca4817b4aeaee271 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 7 Jul 2016 15:10:36 +0800 Subject: ACPI: Add documentation describing ACPICA release automation This patch documents the ACPICA release automation. Signed-off-by: Lv Zheng [ rjw: Editorial changes ] Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/linuxized-acpica.txt b/Documentation/acpi/linuxized-acpica.txt new file mode 100644 index 0000000..defe2ee --- /dev/null +++ b/Documentation/acpi/linuxized-acpica.txt @@ -0,0 +1,262 @@ +Linuxized ACPICA - Introduction to ACPICA Release Automation + +Copyright (C) 2013-2016, Intel Corporation +Author: Lv Zheng + + +Abstract: + +This document describes the ACPICA project and the relationship between +ACPICA and Linux. It also describes how ACPICA code in drivers/acpi/acpica, +include/acpi and tools/power/acpi is automatically updated to follow the +upstream. + + +1. ACPICA Project + + The ACPI Component Architecture (ACPICA) project provides an operating + system (OS)-independent reference implementation of the Advanced + Configuration and Power Interface Specification (ACPI). It has been + adapted by various host OSes. By directly integrating ACPICA, Linux can + also benefit from the application experiences of ACPICA from other host + OSes. + + The homepage of ACPICA project is: www.acpica.org, it is maintained and + supported by Intel Corporation. + + The following figure depicts the Linux ACPI subystem where the ACPICA + adaptation is included: + + +---------------------------------------------------------+ + | | + | +---------------------------------------------------+ | + | | +------------------+ | | + | | | Table Management | | | + | | +------------------+ | | + | | +----------------------+ | | + | | | Namespace Management | | | + | | +----------------------+ | | + | | +------------------+ ACPICA Components | | + | | | Event Management | | | + | | +------------------+ | | + | | +---------------------+ | | + | | | Resource Management | | | + | | +---------------------+ | | + | | +---------------------+ | | + | | | Hardware Management | | | + | | +---------------------+ | | + | +---------------------------------------------------+ | | + | | | +------------------+ | | | + | | | | OS Service Layer | | | | + | | | +------------------+ | | | + | | +-------------------------------------------------|-+ | + | | +--------------------+ | | + | | | Device Enumeration | | | + | | +--------------------+ | | + | | +------------------+ | | + | | | Power Management | | | + | | +------------------+ Linux/ACPI Components | | + | | +--------------------+ | | + | | | Thermal Management | | | + | | +--------------------+ | | + | | +--------------------------+ | | + | | | Drivers for ACPI Devices | | | + | | +--------------------------+ | | + | | +--------+ | | + | | | ...... | | | + | | +--------+ | | + | +---------------------------------------------------+ | + | | + +---------------------------------------------------------+ + + Figure 1. Linux ACPI Software Components + + NOTE: + A. OS Service Layer - Provided by Linux to offer OS dependent + implementation of the predefined ACPICA interfaces (acpi_os_*). + include/acpi/acpiosxf.h + drivers/acpi/osl.c + include/acpi/platform + include/asm/acenv.h + B. ACPICA Functionality - Released from ACPICA code base to offer + OS independent implementation of the ACPICA interfaces (acpi_*). + drivers/acpi/acpica + include/acpi/ac*.h + tools/power/acpi + C. Linux/ACPI Functionality - Providing Linux specific ACPI + functionality to the other Linux kernel subsystems and user space + programs. + drivers/acpi + include/linux/acpi.h + include/linux/acpi*.h + include/acpi + tools/power/acpi + D. Architecture Specific ACPICA/ACPI Functionalities - Provided by the + ACPI subsystem to offer architecture specific implementation of the + ACPI interfaces. They are Linux specific components and are out of + the scope of this document. + include/asm/acpi.h + include/asm/acpi*.h + arch/*/acpi + +2. ACPICA Release + + The ACPICA project maintains its code base at the following repository URL: + https://github.com/acpica/acpica.git. As a rule, a release is made every + month. + + As the coding style adopted by the ACPICA project is not acceptable by + Linux, there is a release process to convert the ACPICA git commits into + Linux patches. The patches generated by this process are referred to as + "linuxized ACPICA patches". The release process is carried out on a local + copy the ACPICA git repository. Each commit in the monthly release is + converted into a linuxized ACPICA patch. Together, they form the montly + ACPICA release patchset for the Linux ACPI community. This process is + illustrated in the following figure: + + +-----------------------------+ + | acpica / master (-) commits | + +-----------------------------+ + /|\ | + | \|/ + | /---------------------\ +----------------------+ + | < Linuxize repo Utility >-->| old linuxized acpica |--+ + | \---------------------/ +----------------------+ | + | | + /---------\ | + < git reset > \ + \---------/ \ + /|\ /+-+ + | / | + +-----------------------------+ | | + | acpica / master (+) commits | | | + +-----------------------------+ | | + | | | + \|/ | | + /-----------------------\ +----------------------+ | | + < Linuxize repo Utilities >-->| new linuxized acpica |--+ | + \-----------------------/ +----------------------+ | + \|/ + +--------------------------+ /----------------------\ + | Linuxized ACPICA Patches |<----------------< Linuxize patch Utility > + +--------------------------+ \----------------------/ + | + \|/ + /---------------------------\ + < Linux ACPI Community Review > + \---------------------------/ + | + \|/ + +-----------------------+ /------------------\ +----------------+ + | linux-pm / linux-next |-->< Linux Merge Window >-->| linux / master | + +-----------------------+ \------------------/ +----------------+ + + Figure 2. ACPICA -> Linux Upstream Process + + NOTE: + A. Linuxize Utilities - Provided by the ACPICA repository, including a + utility located in source/tools/acpisrc folder and a number of + scripts located in generate/linux folder. + B. acpica / master - "master" branch of the git repository at + . + C. linux-pm / linux-next - "linux-next" branch of the git repository at + . + D. linux / master - "master" branch of the git repository at + . + + Before the linuxized ACPICA patches are sent to the Linux ACPI community + for review, there is a quality ensurance build test process to reduce + porting issues. Currently this build process only takes care of the + following kernel configuration options: + CONFIG_ACPI/CONFIG_ACPI_DEBUG/CONFIG_ACPI_DEBUGGER + +3. ACPICA Divergences + + Ideally, all of the ACPICA commits should be converted into Linux patches + automatically without manual modifications, the "linux / master" tree should + contain the ACPICA code that exactly corresponds to the ACPICA code + contained in "new linuxized acpica" tree and it should be possible to run + the release process fully automatically. + + As a matter of fact, however, there are source code differences between + the ACPICA code in Linux and the upstream ACPICA code, referred to as + "ACPICA Divergences". + + The various sources of ACPICA divergences include: + 1. Legacy divergences - Before the current ACPICA release process was + established, there already had been divergences between Linux and + ACPICA. Over the past several years those divergences have been greatly + reduced, but there still are several ones and it takes time to figure + out the underlying reasons for their existence. + 2. Manual modifications - Any manual modification (eg. coding style fixes) + made directly in the Linux sources obviously hurts the ACPICA release + automation. Thus it is recommended to fix such issues in the ACPICA + upstream source code and generate the linuxized fix using the ACPICA + release utilities (please refer to Section 4 below for the details). + 3. Linux specific features - Sometimes it's impossible to use the + current ACPICA APIs to implement features required by the Linux kernel, + so Linux developers occasionaly have to change ACPICA code directly. + Those changes may not be acceptable by ACPICA upstream and in such cases + they are left as committed ACPICA divergences unless the ACPICA side can + implement new mechanisms as replacements for them. + 4. ACPICA release fixups - ACPICA only tests commits using a set of the + user space simulation utilies, thus the linuxized ACPICA patches may + break the Linux kernel, leaving us build/boot failures. In order to + avoid breaking Linux bisection, fixes are applied directly to the + linuxized ACPICA patches during the release process. When the release + fixups are backported to the upstream ACPICA sources, they must follow + the upstream ACPICA rules and so further modifications may appear. + That may result in the appearance of new divergences. + 5. Fast tracking of ACPICA commits - Some ACPICA commits are regression + fixes or stable-candidate material, so they are applied in advance with + respect to the ACPICA release process. If such commits are reverted or + rebased on the ACPICA side in order to offer better solutions, new ACPICA + divergences are generated. + +4. ACPICA Development + + This paragraph guides Linux developers to use the ACPICA upstream release + utilities to obtain Linux patches corresponding to upstream ACPICA commits + before they become available from the ACPICA release process. + + 1. Cherry-pick an ACPICA commit + + First you need to git clone the ACPICA repository and the ACPICA change + you want to cherry pick must be committed into the local repository. + + Then the gen-patch.sh command can help to cherry-pick an ACPICA commit + from the ACPICA local repository: + + $ git clone https://github.com/acpica/acpica + $ cd acpica + $ generate/linux/gen-patch.sh -u [commit ID] + + Here the commit ID is the ACPICA local repository commit ID you want to + cherry pick. It can be omitted if the commit is "HEAD". + + 2. Cherry-pick recent ACPICA commits + + Sometimes you need to rebase your code on top of the most recent ACPICA + changes that haven't been applied to Linux yet. + + You can generate the ACPICA release series yourself and rebase your code on + top of the generated ACPICA release patches: + + $ git clone https://github.com/acpica/acpica + $ cd acpica + $ generate/linux/make-patches.sh -u [commit ID] + + The commit ID should be the last ACPICA commit accepted by Linux. Usually, + it is the commit modifying ACPI_CA_VERSION. It can be found by executing + "git blame source/include/acpixf.h" and referencing the line that contains + "ACPI_CA_VERSION". + + 3. Inspect the current divergences + + If you have local copies of both Linux and upstream ACPICA, you can generate + a diff file indicating the state of the current divergences: + + # git clone https://github.com/acpica/acpica + # git clone http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + # cd acpica + # generate/linux/divergences.sh -s ../linux -- cgit v0.10.2 From 1461d8a5a0e4dad98a38a0932950293692e555d3 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 7 Jul 2016 15:10:45 +0800 Subject: ACPI / debugger: Add AML debugger documentation This patch documents the AML debugger. Signed-off-by: Lv Zheng [ rjw: Editorial changes ] Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/aml-debugger.txt b/Documentation/acpi/aml-debugger.txt new file mode 100644 index 0000000..5f62aa4 --- /dev/null +++ b/Documentation/acpi/aml-debugger.txt @@ -0,0 +1,66 @@ +The AML Debugger + +Copyright (C) 2016, Intel Corporation +Author: Lv Zheng + + +This document describes the usage of the AML debugger embedded in the Linux +kernel. + +1. Build the debugger + + The following kernel configuration items are required to enable the AML + debugger interface from the Linux kernel: + + CONFIG_ACPI_DEBUGGER=y + CONFIG_ACPI_DEBUGGER_USER=m + + The userspace utlities can be built from the kernel source tree using + the following commands: + + $ cd tools + $ make acpi + + The resultant userspace tool binary is then located at: + + tools/acpi/power/acpi/acpidbg/acpidbg + + It can be installed to system directories by running "make install" (as a + sufficiently privileged user). + +2. Start the userspace debugger interface + + After booting the kernel with the debugger built-in, the debugger can be + started by using the following commands: + + # mount -t debugfs none /sys/kernel/debug + # modprobe acpi_dbg + # tools/acpi/power/acpi/acpidbg/acpidbg + + That spawns the interactive AML debugger environment where you can execute + debugger commands. + + The commands are documented in the "ACPICA Overview and Programmer Reference" + that can be downloaded from + + https://acpica.org/documentation + + The detailed debugger commands reference is located in Chapter 12 "ACPICA + Debugger Reference". The "help" command can be used for a quick reference. + +3. Stop the userspace debugger interface + + The interactive debugger interface can be closed by pressing Ctrl+C or using + the "quit" or "exit" commands. When finished, unload the module with: + + # rmmod acpi_dbg + + The module unloading may fail if there is an acpidbg instance running. + +4. Run the debugger in a script + + It may be useful to run the AML debugger in a test script. "acpidbg" supports + this in a special "batch" mode. For example, the following command outputs + the entire ACPI namespace: + + # acpidbg -b "namespace" -- cgit v0.10.2 From fafe5306f201ae3ad3f81f62d203c86952444e12 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 11 Jul 2016 15:13:36 +0200 Subject: ACPI: Rename configfs.c to acpi_configfs.c to prevent link error If we compile ACPI configfs.c as module it will confuse the linker as it hides symbols from the actual configfs: Kernel: arch/x86/boot/bzImage is ready (#1236) MODPOST 5739 modules ERROR: "configfs_unregister_subsystem" [samples/configfs/configfs_sample.ko] undefined! ERROR: "configfs_register_subsystem" [samples/configfs/configfs_sample.ko] undefined! ERROR: "config_group_init" [samples/configfs/configfs_sample.ko] undefined! ERROR: "config_item_init_type_name" [samples/configfs/configfs_sample.ko] undefined! ERROR: "config_group_init_type_name" [samples/configfs/configfs_sample.ko] undefined! ERROR: "configfs_undepend_item" [fs/ocfs2/cluster/ocfs2_nodemanager.ko] undefined! ... Prevent these by renaming the file to acpi_configfs.c instead. Reported-by: Scott Lawson Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 1dc2173..f72a83d 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -99,6 +99,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o -obj-$(CONFIG_ACPI_CONFIGFS) += configfs.o +obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o video-objs += acpi_video.o video_detect.o diff --git a/drivers/acpi/acpi_configfs.c b/drivers/acpi/acpi_configfs.c new file mode 100644 index 0000000..146a77fb --- /dev/null +++ b/drivers/acpi/acpi_configfs.c @@ -0,0 +1,267 @@ +/* + * ACPI configfs support + * + * Copyright (c) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define pr_fmt(fmt) "ACPI configfs: " fmt + +#include +#include +#include +#include + +static struct config_group *acpi_table_group; + +struct acpi_table { + struct config_item cfg; + struct acpi_table_header *header; +}; + +static ssize_t acpi_table_aml_write(struct config_item *cfg, + const void *data, size_t size) +{ + const struct acpi_table_header *header = data; + struct acpi_table *table; + int ret; + + table = container_of(cfg, struct acpi_table, cfg); + + if (table->header) { + pr_err("table already loaded\n"); + return -EBUSY; + } + + if (header->length != size) { + pr_err("invalid table length\n"); + return -EINVAL; + } + + if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { + pr_err("invalid table signature\n"); + return -EINVAL; + } + + table = container_of(cfg, struct acpi_table, cfg); + + table->header = kmemdup(header, header->length, GFP_KERNEL); + if (!table->header) + return -ENOMEM; + + ret = acpi_load_table(table->header); + if (ret) { + kfree(table->header); + table->header = NULL; + } + + return ret; +} + +static inline struct acpi_table_header *get_header(struct config_item *cfg) +{ + struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); + + if (!table->header) + pr_err("table not loaded\n"); + + return table->header; +} + +static ssize_t acpi_table_aml_read(struct config_item *cfg, + void *data, size_t size) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + if (data) + memcpy(data, h, h->length); + + return h->length; +} + +#define MAX_ACPI_TABLE_SIZE (128 * 1024) + +CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); + +struct configfs_bin_attribute *acpi_table_bin_attrs[] = { + &acpi_table_attr_aml, + NULL, +}; + +ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature); +} + +ssize_t acpi_table_length_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->length); +} + +ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->revision); +} + +ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); +} + +ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); +} + +ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->oem_revision); +} + +ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id); +} + +ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, + char *str) +{ + struct acpi_table_header *h = get_header(cfg); + + if (!h) + return -EINVAL; + + return sprintf(str, "%d\n", h->asl_compiler_revision); +} + +CONFIGFS_ATTR_RO(acpi_table_, signature); +CONFIGFS_ATTR_RO(acpi_table_, length); +CONFIGFS_ATTR_RO(acpi_table_, revision); +CONFIGFS_ATTR_RO(acpi_table_, oem_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_table_id); +CONFIGFS_ATTR_RO(acpi_table_, oem_revision); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); +CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); + +struct configfs_attribute *acpi_table_attrs[] = { + &acpi_table_attr_signature, + &acpi_table_attr_length, + &acpi_table_attr_revision, + &acpi_table_attr_oem_id, + &acpi_table_attr_oem_table_id, + &acpi_table_attr_oem_revision, + &acpi_table_attr_asl_compiler_id, + &acpi_table_attr_asl_compiler_revision, + NULL, +}; + +static struct config_item_type acpi_table_type = { + .ct_owner = THIS_MODULE, + .ct_bin_attrs = acpi_table_bin_attrs, + .ct_attrs = acpi_table_attrs, +}; + +static struct config_item *acpi_table_make_item(struct config_group *group, + const char *name) +{ + struct acpi_table *table; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&table->cfg, name, &acpi_table_type); + return &table->cfg; +} + +struct configfs_group_operations acpi_table_group_ops = { + .make_item = acpi_table_make_item, +}; + +static struct config_item_type acpi_tables_type = { + .ct_owner = THIS_MODULE, + .ct_group_ops = &acpi_table_group_ops, +}; + +static struct config_item_type acpi_root_group_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem acpi_configfs = { + .su_group = { + .cg_item = { + .ci_namebuf = "acpi", + .ci_type = &acpi_root_group_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), +}; + +static int __init acpi_configfs_init(void) +{ + int ret; + struct config_group *root = &acpi_configfs.su_group; + + config_group_init(root); + + ret = configfs_register_subsystem(&acpi_configfs); + if (ret) + return ret; + + acpi_table_group = configfs_register_default_group(root, "table", + &acpi_tables_type); + return PTR_ERR_OR_ZERO(acpi_table_group); +} +module_init(acpi_configfs_init); + +static void __exit acpi_configfs_exit(void) +{ + configfs_unregister_default_group(acpi_table_group); + configfs_unregister_subsystem(&acpi_configfs); +} +module_exit(acpi_configfs_exit); + +MODULE_AUTHOR("Octavian Purdila "); +MODULE_DESCRIPTION("ACPI configfs support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c deleted file mode 100644 index 146a77fb..0000000 --- a/drivers/acpi/configfs.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * ACPI configfs support - * - * Copyright (c) 2016 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#define pr_fmt(fmt) "ACPI configfs: " fmt - -#include -#include -#include -#include - -static struct config_group *acpi_table_group; - -struct acpi_table { - struct config_item cfg; - struct acpi_table_header *header; -}; - -static ssize_t acpi_table_aml_write(struct config_item *cfg, - const void *data, size_t size) -{ - const struct acpi_table_header *header = data; - struct acpi_table *table; - int ret; - - table = container_of(cfg, struct acpi_table, cfg); - - if (table->header) { - pr_err("table already loaded\n"); - return -EBUSY; - } - - if (header->length != size) { - pr_err("invalid table length\n"); - return -EINVAL; - } - - if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { - pr_err("invalid table signature\n"); - return -EINVAL; - } - - table = container_of(cfg, struct acpi_table, cfg); - - table->header = kmemdup(header, header->length, GFP_KERNEL); - if (!table->header) - return -ENOMEM; - - ret = acpi_load_table(table->header); - if (ret) { - kfree(table->header); - table->header = NULL; - } - - return ret; -} - -static inline struct acpi_table_header *get_header(struct config_item *cfg) -{ - struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); - - if (!table->header) - pr_err("table not loaded\n"); - - return table->header; -} - -static ssize_t acpi_table_aml_read(struct config_item *cfg, - void *data, size_t size) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - if (data) - memcpy(data, h, h->length); - - return h->length; -} - -#define MAX_ACPI_TABLE_SIZE (128 * 1024) - -CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); - -struct configfs_bin_attribute *acpi_table_bin_attrs[] = { - &acpi_table_attr_aml, - NULL, -}; - -ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature); -} - -ssize_t acpi_table_length_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%d\n", h->length); -} - -ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%d\n", h->revision); -} - -ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); -} - -ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); -} - -ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%d\n", h->oem_revision); -} - -ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id); -} - -ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, - char *str) -{ - struct acpi_table_header *h = get_header(cfg); - - if (!h) - return -EINVAL; - - return sprintf(str, "%d\n", h->asl_compiler_revision); -} - -CONFIGFS_ATTR_RO(acpi_table_, signature); -CONFIGFS_ATTR_RO(acpi_table_, length); -CONFIGFS_ATTR_RO(acpi_table_, revision); -CONFIGFS_ATTR_RO(acpi_table_, oem_id); -CONFIGFS_ATTR_RO(acpi_table_, oem_table_id); -CONFIGFS_ATTR_RO(acpi_table_, oem_revision); -CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); -CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); - -struct configfs_attribute *acpi_table_attrs[] = { - &acpi_table_attr_signature, - &acpi_table_attr_length, - &acpi_table_attr_revision, - &acpi_table_attr_oem_id, - &acpi_table_attr_oem_table_id, - &acpi_table_attr_oem_revision, - &acpi_table_attr_asl_compiler_id, - &acpi_table_attr_asl_compiler_revision, - NULL, -}; - -static struct config_item_type acpi_table_type = { - .ct_owner = THIS_MODULE, - .ct_bin_attrs = acpi_table_bin_attrs, - .ct_attrs = acpi_table_attrs, -}; - -static struct config_item *acpi_table_make_item(struct config_group *group, - const char *name) -{ - struct acpi_table *table; - - table = kzalloc(sizeof(*table), GFP_KERNEL); - if (!table) - return ERR_PTR(-ENOMEM); - - config_item_init_type_name(&table->cfg, name, &acpi_table_type); - return &table->cfg; -} - -struct configfs_group_operations acpi_table_group_ops = { - .make_item = acpi_table_make_item, -}; - -static struct config_item_type acpi_tables_type = { - .ct_owner = THIS_MODULE, - .ct_group_ops = &acpi_table_group_ops, -}; - -static struct config_item_type acpi_root_group_type = { - .ct_owner = THIS_MODULE, -}; - -static struct configfs_subsystem acpi_configfs = { - .su_group = { - .cg_item = { - .ci_namebuf = "acpi", - .ci_type = &acpi_root_group_type, - }, - }, - .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), -}; - -static int __init acpi_configfs_init(void) -{ - int ret; - struct config_group *root = &acpi_configfs.su_group; - - config_group_init(root); - - ret = configfs_register_subsystem(&acpi_configfs); - if (ret) - return ret; - - acpi_table_group = configfs_register_default_group(root, "table", - &acpi_tables_type); - return PTR_ERR_OR_ZERO(acpi_table_group); -} -module_init(acpi_configfs_init); - -static void __exit acpi_configfs_exit(void) -{ - configfs_unregister_default_group(acpi_table_group); - configfs_unregister_subsystem(&acpi_configfs); -} -module_exit(acpi_configfs_exit); - -MODULE_AUTHOR("Octavian Purdila "); -MODULE_DESCRIPTION("ACPI configfs support"); -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From b4219a898f487f454f4b58b0906d9e035ea58a12 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 5 Jul 2016 13:53:04 +0800 Subject: ACPICA: Linux: Enable ACPI_MUTEX_DEBUG for Linux kernel This patch enables ACPI_MUTEX_DEBUG for Linux kernel so that the ACPICA lock order issues can be captured by ACPICA itself. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 45c2d65..93b61b1 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -73,6 +73,10 @@ #define ACPI_DEBUGGER #endif +#ifdef CONFIG_ACPI_DEBUG +#define ACPI_MUTEX_DEBUG +#endif + #include #include #include -- cgit v0.10.2 From 6d3ef8d8f9ccd6d41b8f4836398875877b7a02b1 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 11 Jul 2016 18:05:16 -0400 Subject: ACPI / PMIC: remove modular references from non-modular code The Kconfig currently controlling compilation of these files are: drivers/acpi/Kconfig:menuconfig PMIC_OPREGION drivers/acpi/Kconfig: bool "PMIC (Power Management Integrated Circuit) operation region support" drivers/acpi/Kconfig:config BXT_WC_PMIC_OPREGION drivers/acpi/Kconfig: bool "ACPI operation region support for BXT WhiskeyCove PMIC" drivers/acpi/Kconfig:config XPOWER_PMIC_OPREGION drivers/acpi/Kconfig: bool "ACPI operation region support for XPower AXP288 PMIC" ...meaning they currently are not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the code there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. One file was using module_init. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. In one case we replace the module.h with export.h since that file is exporting some symbols, but does not use __init. The other two are using __init and so module.h gets replaced with init.h there. Signed-off-by: Paul Gortmaker Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index 6585aff..ca18e0d 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -319,5 +319,3 @@ out_error: return ret; } EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); - -MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic_bxtwc.c b/drivers/acpi/pmic/intel_pmic_bxtwc.c index ebe6f09..90011aa 100644 --- a/drivers/acpi/pmic/intel_pmic_bxtwc.c +++ b/drivers/acpi/pmic/intel_pmic_bxtwc.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -417,8 +417,4 @@ static int __init intel_bxtwc_pmic_opregion_driver_init(void) { return platform_driver_register(&intel_bxtwc_pmic_opregion_driver); } - device_initcall(intel_bxtwc_pmic_opregion_driver_init); - -MODULE_DESCRIPTION("BXT WhiskeyCove ACPI opregion driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c index 6a082d4..e6e991a 100644 --- a/drivers/acpi/pmic/intel_pmic_xpower.c +++ b/drivers/acpi/pmic/intel_pmic_xpower.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -262,7 +262,4 @@ static int __init intel_xpower_pmic_opregion_driver_init(void) { return platform_driver_register(&intel_xpower_pmic_opregion_driver); } -module_init(intel_xpower_pmic_opregion_driver_init); - -MODULE_DESCRIPTION("XPower AXP288 ACPI operation region driver"); -MODULE_LICENSE("GPL"); +device_initcall(intel_xpower_pmic_opregion_driver_init); -- cgit v0.10.2 From ffaab0aa2c3edd67a0cafbc66f9cc39276f233fa Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 11 Jul 2016 18:17:23 -0400 Subject: ACPI / PCI: make pci_slot explicitly non-modular The Kconfig for this file is: drivers/acpi/Kconfig:config ACPI_PCI_SLOT drivers/acpi/Kconfig: bool "PCI slot detection driver" ...and so it is not built as a module. Hence including module.h and everything that comes with it just for the no-op MODULE_LICENSE and friends is rather heavy handed. The license/author info is found at the top of the file, so we just remove the MODULE_LICENSE etc and the include of module.h We delete the DRIVER_VERSION macro as well since it is unused. Signed-off-by: Paul Gortmaker Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 1236fb0..f62c68e 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -25,7 +25,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include @@ -37,13 +36,6 @@ static int check_sta_before_sun; -#define DRIVER_VERSION "0.1" -#define DRIVER_AUTHOR "Alex Chiang " -#define DRIVER_DESC "ACPI PCI Slot Detection Driver" -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_slot"); -- cgit v0.10.2 From 0f093e95f5b631f35ef7a131a7a9ef48748e2623 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 11 Jul 2016 16:23:26 -0400 Subject: ACPI / dock: make dock explicitly non-modular The Kconfig for this file is: drivers/acpi/Kconfig:config ACPI_DOCK drivers/acpi/Kconfig: bool "Dock" ...and so it is not built as a module. Hence including module.h and everything that comes with it just for the no-op MODULE_LICENSE and friends is rather heavy handed. The license/author info is found at the top of the file, so we just remove the MODULE_LICENSE etc and the include of module.h The file does still have some module_param() so we add the right include for that infrastructure. Signed-off-by: Paul Gortmaker Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index e8e128d..0c00208 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include #include @@ -33,12 +33,7 @@ #include "internal.h" -#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver" - ACPI_MODULE_NAME("dock"); -MODULE_AUTHOR("Kristen Carlson Accardi"); -MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION); -MODULE_LICENSE("GPL"); static bool immediate_undock = 1; module_param(immediate_undock, bool, 0644); -- cgit v0.10.2 From c49b43ce03944bb7cfb75f8a4f8f6ba799fa517e Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 11 Jul 2016 17:22:41 -0400 Subject: ACPI / lpat: make it explicitly non-modular The Makefile for building this is "acpi-y" and so it is not built as a module. Hence including module.h and everything that comes with it just for the no-op MODULE_LICENSE is rather heavy handed. The license info is found at the top of the file, so we just remove the MODULE_LICENSE and the include of module.h We add an include of export.h since the file exports some symbols. Signed-off-by: Paul Gortmaker Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_lpat.c b/drivers/acpi/acpi_lpat.c index feb61c1..c1c4877 100644 --- a/drivers/acpi/acpi_lpat.c +++ b/drivers/acpi/acpi_lpat.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include @@ -157,5 +157,3 @@ void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table } } EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table); - -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 6256ebd5daf93505f12b66916fb1b961b38f4cf2 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sun, 17 Jul 2016 13:45:32 -0700 Subject: ACPI / DPTF: Add DPTF power participant driver This driver adds support for Dynamic Platform and Thermal Framework (DPTF) Platform Power Participant device (INT3407) support. This participant is responsible for exposing platform telemetry such as: max_platform_power platform_power_source adapter_rating battery_steady_power charger_type These attributes are presented via sysfs interface under the INT3407 platform device: $ls /sys/bus/platform/devices/INT3407\:00/dptf_power/ adapter_rating_mw battery_steady_power_mw charger_type max_platform_power_mw platform_power_source ` ACPI methods description used in this driver: PMAX: Maximum platform power that can be supported by the battery in mW. PSRC: System charge source, 0x00 = DC 0x01 = AC 0x02 = USB 0x03 = Wireless Charger ARTG: Adapter rating in mW (Maximum Adapter power) Must be 0 if no AC adapter is plugged in. CTYP: Charger Type, Traditional : 0x01 Hybrid: 0x02 NVDC: 0x03 PBSS: Returns max sustained power for battery in milliWatts. The INT3407 also contains _BTS and _BIX objects, which are compliant to ACPI 5.0, specification. Those objects are already used by ACPI battery (PNP0C0A) driver and information about them is exported via Linux power supply class registration. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..b91a35b 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -475,6 +475,7 @@ config ACPI_NFIT_DEBUG issue. source "drivers/acpi/apei/Kconfig" +source "drivers/acpi/dptf/Kconfig" config ACPI_EXTLOG tristate "Extended Error Log support" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 251ce85..f6d0cc0 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -101,3 +101,4 @@ obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o video-objs += acpi_video.o video_detect.o +obj-y += dptf/ diff --git a/drivers/acpi/dptf/Kconfig b/drivers/acpi/dptf/Kconfig new file mode 100644 index 0000000..ac0a6ed --- /dev/null +++ b/drivers/acpi/dptf/Kconfig @@ -0,0 +1,15 @@ +config DPTF_POWER + tristate "DPTF Platform Power Participant" + depends on X86 + help + This driver adds support for Dynamic Platform and Thermal Framework + (DPTF) Platform Power Participant device (INT3407) support. + This participant is responsible for exposing platform telemetry: + max_platform_power + platform_power_source + adapter_rating + battery_steady_power + charger_type + + To compile this driver as a module, choose M here: + the module will be called dptf_power. diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile new file mode 100644 index 0000000..62cbe87 --- /dev/null +++ b/drivers/acpi/dptf/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DPTF_POWER) += dptf_power.o diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c new file mode 100644 index 0000000..734642d --- /dev/null +++ b/drivers/acpi/dptf/dptf_power.c @@ -0,0 +1,128 @@ +/* + * dptf_power: DPTF platform power driver + * Copyright (c) 2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include + +/* + * Presentation of attributes which are defined for INT3407. They are: + * PMAX : Maximum platform powe + * PSRC : Platform power source + * ARTG : Adapter rating + * CTYP : Charger type + * PBSS : Battery steady power + */ +#define DPTF_POWER_SHOW(name, object) \ +static ssize_t name##_show(struct device *dev,\ + struct device_attribute *attr,\ + char *buf)\ +{\ + struct platform_device *pdev = to_platform_device(dev);\ + struct acpi_device *acpi_dev = platform_get_drvdata(pdev);\ + unsigned long long val;\ + acpi_status status;\ +\ + status = acpi_evaluate_integer(acpi_dev->handle, #object,\ + NULL, &val);\ + if (ACPI_SUCCESS(status))\ + return sprintf(buf, "%d\n", (int)val);\ + else \ + return -EINVAL;\ +} + +DPTF_POWER_SHOW(max_platform_power_mw, PMAX) +DPTF_POWER_SHOW(platform_power_source, PSRC) +DPTF_POWER_SHOW(adapter_rating_mw, ARTG) +DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) +DPTF_POWER_SHOW(charger_type, CTYP) + +static DEVICE_ATTR_RO(max_platform_power_mw); +static DEVICE_ATTR_RO(platform_power_source); +static DEVICE_ATTR_RO(adapter_rating_mw); +static DEVICE_ATTR_RO(battery_steady_power_mw); +static DEVICE_ATTR_RO(charger_type); + +static struct attribute *dptf_power_attrs[] = { + &dev_attr_max_platform_power_mw.attr, + &dev_attr_platform_power_source.attr, + &dev_attr_adapter_rating_mw.attr, + &dev_attr_battery_steady_power_mw.attr, + &dev_attr_charger_type.attr, + NULL +}; + +static struct attribute_group dptf_power_attribute_group = { + .attrs = dptf_power_attrs, + .name = "dptf_power" +}; + +static int dptf_power_add(struct platform_device *pdev) +{ + struct acpi_device *acpi_dev; + acpi_status status; + unsigned long long ptype; + int result; + + acpi_dev = ACPI_COMPANION(&(pdev->dev)); + if (!acpi_dev) + return -ENODEV; + + status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if (ptype != 0x11) + return -ENODEV; + + result = sysfs_create_group(&pdev->dev.kobj, + &dptf_power_attribute_group); + if (result) + return result; + + platform_set_drvdata(pdev, acpi_dev); + + return 0; +} + +static int dptf_power_remove(struct platform_device *pdev) +{ + + sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); + + return 0; +} + +static const struct acpi_device_id int3407_device_ids[] = { + {"INT3407", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, int3407_device_ids); + +static struct platform_driver dptf_power_driver = { + .probe = dptf_power_add, + .remove = dptf_power_remove, + .driver = { + .name = "DPTF Platform Power", + .acpi_match_table = int3407_device_ids, + }, +}; + +module_platform_driver(dptf_power_driver); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ACPI DPTF platform power driver"); -- cgit v0.10.2 From c11dd70fd7f50fca7dc05520ec8075f12d93cb63 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 19 Jul 2016 14:57:49 -0700 Subject: ACPI / DPTF: move int340x_thermal.c to the DPTF folder Since DPTF has its own folder under ACPI, move this file also there. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index f6d0cc0..0325a53 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -44,7 +44,6 @@ acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o -acpi-y += int340x_thermal.o acpi-y += power.o acpi-y += event.o acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile index 62cbe87..06ea880 100644 --- a/drivers/acpi/dptf/Makefile +++ b/drivers/acpi/dptf/Makefile @@ -1 +1,4 @@ +obj-$(CONFIG_ACPI) += int340x_thermal.o obj-$(CONFIG_DPTF_POWER) += dptf_power.o + +ccflags-y += -Idrivers/acpi diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c new file mode 100644 index 0000000..33505c6 --- /dev/null +++ b/drivers/acpi/dptf/int340x_thermal.c @@ -0,0 +1,53 @@ +/* + * ACPI support for int340x thermal drivers + * + * Copyright (C) 2014, Intel Corporation + * Authors: Zhang Rui + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "internal.h" + +#define INT3401_DEVICE 0X01 +static const struct acpi_device_id int340x_thermal_device_ids[] = { + {"INT3400"}, + {"INT3401", INT3401_DEVICE}, + {"INT3402"}, + {"INT3403"}, + {"INT3404"}, + {"INT3406"}, + {"INT3407"}, + {"INT3408"}, + {"INT3409"}, + {"INT340A"}, + {"INT340B"}, + {""}, +}; + +static int int340x_thermal_handler_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + if (IS_ENABLED(CONFIG_INT340X_THERMAL)) + acpi_create_platform_device(adev); + /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ + else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) && + id->driver_data == INT3401_DEVICE) + acpi_create_platform_device(adev); + return 1; +} + +static struct acpi_scan_handler int340x_thermal_handler = { + .ids = int340x_thermal_device_ids, + .attach = int340x_thermal_handler_attach, +}; + +void __init acpi_int340x_thermal_init(void) +{ + acpi_scan_add_handler(&int340x_thermal_handler); +} diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c deleted file mode 100644 index 33505c6..0000000 --- a/drivers/acpi/int340x_thermal.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ACPI support for int340x thermal drivers - * - * Copyright (C) 2014, Intel Corporation - * Authors: Zhang Rui - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include - -#include "internal.h" - -#define INT3401_DEVICE 0X01 -static const struct acpi_device_id int340x_thermal_device_ids[] = { - {"INT3400"}, - {"INT3401", INT3401_DEVICE}, - {"INT3402"}, - {"INT3403"}, - {"INT3404"}, - {"INT3406"}, - {"INT3407"}, - {"INT3408"}, - {"INT3409"}, - {"INT340A"}, - {"INT340B"}, - {""}, -}; - -static int int340x_thermal_handler_attach(struct acpi_device *adev, - const struct acpi_device_id *id) -{ - if (IS_ENABLED(CONFIG_INT340X_THERMAL)) - acpi_create_platform_device(adev); - /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ - else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) && - id->driver_data == INT3401_DEVICE) - acpi_create_platform_device(adev); - return 1; -} - -static struct acpi_scan_handler int340x_thermal_handler = { - .ids = int340x_thermal_device_ids, - .attach = int340x_thermal_handler_attach, -}; - -void __init acpi_int340x_thermal_init(void) -{ - acpi_scan_add_handler(&int340x_thermal_handler); -} -- cgit v0.10.2 From 35ae713355868aa493edbfbabf615473473215cc Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:53 +0100 Subject: ACPI / processor_idle: introduce ACPI_PROCESSOR_CSTATE ACPI 6.0 adds a new method to specify the CPU idle states(C-states) called Low Power Idle(LPI) states. Since new architectures like ARM64 use only LPIs, introduce ACPI_PROCESSOR_CSTATE to encapsulate all the code supporting the old style C-states(_CST). This patch will help to extend the processor_idle module to support LPI. Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b7e2e77..1358fb7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -213,6 +213,10 @@ config ACPI_CPU_FREQ_PSS bool select THERMAL +config ACPI_PROCESSOR_CSTATE + def_bool y + depends on IA64 || X86 + config ACPI_PROCESSOR_IDLE bool select CPU_IDLE diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 444e374..ca0de35 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -59,6 +59,12 @@ module_param(latency_factor, uint, 0644); static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); +struct cpuidle_driver acpi_idle_driver = { + .name = "acpi_idle", + .owner = THIS_MODULE, +}; + +#ifdef CONFIG_ACPI_PROCESSOR_CSTATE static DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate); @@ -804,11 +810,6 @@ static void acpi_idle_enter_freeze(struct cpuidle_device *dev, acpi_idle_do_entry(cx); } -struct cpuidle_driver acpi_idle_driver = { - .name = "acpi_idle", - .owner = THIS_MODULE, -}; - /** * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE * device i.e. per-cpu data @@ -925,6 +926,50 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) return 0; } +static inline void acpi_processor_cstate_first_run_checks(void) +{ + acpi_status status; + static int first_run; + + if (first_run) + return; + dmi_check_system(processor_power_dmi_table); + max_cstate = acpi_processor_cstate_check(max_cstate); + if (max_cstate < ACPI_C_STATES_MAX) + pr_notice("ACPI: processor limited to max C-state %d\n", + max_cstate); + first_run++; + + if (acpi_gbl_FADT.cst_control && !nocst) { + status = acpi_os_write_port(acpi_gbl_FADT.smi_command, + acpi_gbl_FADT.cst_control, 8); + if (ACPI_FAILURE(status)) + ACPI_EXCEPTION((AE_INFO, status, + "Notifying BIOS of _CST ability failed")); + } +} +#else + +static inline int disabled_by_idle_boot_param(void) { return 0; } +static inline void acpi_processor_cstate_first_run_checks(void) { } +static int acpi_processor_get_power_info(struct acpi_processor *pr) +{ + return -ENODEV; +} + +static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, + struct cpuidle_device *dev) +{ + return -EINVAL; +} + +static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +{ + return -EINVAL; +} + +#endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + int acpi_processor_hotplug(struct acpi_processor *pr) { int ret = 0; @@ -1015,35 +1060,16 @@ static int acpi_processor_registered; int acpi_processor_power_init(struct acpi_processor *pr) { - acpi_status status; int retval; struct cpuidle_device *dev; - static int first_run; if (disabled_by_idle_boot_param()) return 0; - if (!first_run) { - dmi_check_system(processor_power_dmi_table); - max_cstate = acpi_processor_cstate_check(max_cstate); - if (max_cstate < ACPI_C_STATES_MAX) - printk(KERN_NOTICE - "ACPI: processor limited to max C-state %d\n", - max_cstate); - first_run++; - } + acpi_processor_cstate_first_run_checks(); - if (acpi_gbl_FADT.cst_control && !nocst) { - status = - acpi_os_write_port(acpi_gbl_FADT.smi_command, acpi_gbl_FADT.cst_control, 8); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Notifying BIOS of _CST ability failed")); - } - } - - acpi_processor_get_power_info(pr); - pr->flags.power_setup_done = 1; + if (!acpi_processor_get_power_info(pr)) + pr->flags.power_setup_done = 1; /* * Install the idle handler if processor power management is supported. diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 6f1805d..48779d6 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -242,7 +242,7 @@ extern int acpi_processor_get_performance_info(struct acpi_processor *pr); DECLARE_PER_CPU(struct acpi_processor *, processors); extern struct acpi_processor_errata errata; -#ifdef ARCH_HAS_POWER_INIT +#if defined(ARCH_HAS_POWER_INIT) && defined(CONFIG_ACPI_PROCESSOR_CSTATE) void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, unsigned int cpu); int acpi_processor_ffh_cstate_probe(unsigned int cpu, -- cgit v0.10.2 From a36a7fecfe6071732075ad5aa31196adce13181b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 21 Jul 2016 17:18:07 +0100 Subject: ACPI / processor_idle: Add support for Low Power Idle(LPI) states ACPI 6.0 introduced an optional object _LPI that provides an alternate method to describe Low Power Idle states. It defines the local power states for each node in a hierarchical processor topology. The OSPM can use _LPI object to select a local power state for each level of processor hierarchy in the system. They used to produce a composite power state request that is presented to the platform by the OSPM. Since multiple processors affect the idle state for any non-leaf hierarchy node, coordination of idle state requests between the processors is required. ACPI supports two different coordination schemes: Platform coordinated and OS initiated. This patch adds initial support for Platform coordination scheme of LPI. Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 262ca31..80ebb05 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -302,6 +302,14 @@ out_kfree: EXPORT_SYMBOL(acpi_run_osc); bool osc_sb_apei_support_acked; + +/* + * ACPI 6.0 Section 8.4.4.2 Idle State Coordination + * OSPM supports platform coordinated low power idle(LPI) states + */ +bool osc_pc_lpi_support_confirmed; +EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); + static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; static void acpi_bus_osc_support(void) { @@ -322,6 +330,7 @@ static void acpi_bus_osc_support(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; if (!ghes_disable) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; @@ -329,9 +338,12 @@ static void acpi_bus_osc_support(void) return; if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) { u32 *capbuf_ret = context.ret.pointer; - if (context.ret.length > OSC_SUPPORT_DWORD) + if (context.ret.length > OSC_SUPPORT_DWORD) { osc_sb_apei_support_acked = capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; + } kfree(context.ret.pointer); } /* do we need to check other returned cap? Sounds no */ diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index d2fa8cb..0ca14ac 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -90,7 +90,7 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) pr->performance_platform_limit); break; case ACPI_PROCESSOR_NOTIFY_POWER: - acpi_processor_cst_has_changed(pr); + acpi_processor_power_state_has_changed(pr); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ca0de35..cea5252 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -303,7 +303,6 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *cst; - if (nocst) return -ENODEV; @@ -576,7 +575,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) return (working); } -static int acpi_processor_get_power_info(struct acpi_processor *pr) +static int acpi_processor_get_cstate_info(struct acpi_processor *pr) { unsigned int i; int result; @@ -810,31 +809,12 @@ static void acpi_idle_enter_freeze(struct cpuidle_device *dev, acpi_idle_do_entry(cx); } -/** - * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE - * device i.e. per-cpu data - * - * @pr: the ACPI processor - * @dev : the cpuidle device - */ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, struct cpuidle_device *dev) { int i, count = CPUIDLE_DRIVER_STATE_START; struct acpi_processor_cx *cx; - if (!pr->flags.power_setup_done) - return -EINVAL; - - if (pr->flags.power == 0) { - return -EINVAL; - } - - if (!dev) - return -EINVAL; - - dev->cpu = pr->id; - if (max_cstate == 0) max_cstate = 1; @@ -857,31 +837,13 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, return 0; } -/** - * acpi_processor_setup_cpuidle states- prepares and configures cpuidle - * global state data i.e. idle routines - * - * @pr: the ACPI processor - */ -static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +static int acpi_processor_setup_cstates(struct acpi_processor *pr) { int i, count = CPUIDLE_DRIVER_STATE_START; struct acpi_processor_cx *cx; struct cpuidle_state *state; struct cpuidle_driver *drv = &acpi_idle_driver; - if (!pr->flags.power_setup_done) - return -EINVAL; - - if (pr->flags.power == 0) - return -EINVAL; - - drv->safe_state_index = -1; - for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { - drv->states[i].name[0] = '\0'; - drv->states[i].desc[0] = '\0'; - } - if (max_cstate == 0) max_cstate = 1; @@ -893,7 +855,7 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state = &drv->states[count]; snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); - strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); + strlcpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); state->exit_latency = cx->latency; state->target_residency = cx->latency * latency_factor; state->enter = acpi_idle_enter; @@ -952,7 +914,7 @@ static inline void acpi_processor_cstate_first_run_checks(void) static inline int disabled_by_idle_boot_param(void) { return 0; } static inline void acpi_processor_cstate_first_run_checks(void) { } -static int acpi_processor_get_power_info(struct acpi_processor *pr) +static int acpi_processor_get_cstate_info(struct acpi_processor *pr) { return -ENODEV; } @@ -963,13 +925,413 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, return -EINVAL; } -static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +static int acpi_processor_setup_cstates(struct acpi_processor *pr) { return -EINVAL; } #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ +struct acpi_lpi_states_array { + unsigned int size; + unsigned int composite_states_size; + struct acpi_lpi_state *entries; + struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +}; + +static int obj_get_integer(union acpi_object *obj, u32 *value) +{ + if (obj->type != ACPI_TYPE_INTEGER) + return -EINVAL; + + *value = obj->integer.value; + return 0; +} + +static int acpi_processor_evaluate_lpi(acpi_handle handle, + struct acpi_lpi_states_array *info) +{ + acpi_status status; + int ret = 0; + int pkg_count, state_idx = 1, loop; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *lpi_data; + struct acpi_lpi_state *lpi_state; + + status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _LPI, giving up\n")); + return -ENODEV; + } + + lpi_data = buffer.pointer; + + /* There must be at least 4 elements = 3 elements + 1 package */ + if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || + lpi_data->package.count < 4) { + pr_debug("not enough elements in _LPI\n"); + ret = -ENODATA; + goto end; + } + + pkg_count = lpi_data->package.elements[2].integer.value; + + /* Validate number of power states. */ + if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { + pr_debug("count given by _LPI is not valid\n"); + ret = -ENODATA; + goto end; + } + + lpi_state = kcalloc(pkg_count, sizeof(*lpi_state), GFP_KERNEL); + if (!lpi_state) { + ret = -ENOMEM; + goto end; + } + + info->size = pkg_count; + info->entries = lpi_state; + + /* LPI States start at index 3 */ + for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { + union acpi_object *element, *pkg_elem, *obj; + + element = &lpi_data->package.elements[loop]; + if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) + continue; + + pkg_elem = element->package.elements; + + obj = pkg_elem + 6; + if (obj->type == ACPI_TYPE_BUFFER) { + struct acpi_power_register *reg; + + reg = (struct acpi_power_register *)obj->buffer.pointer; + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && + reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) + continue; + + lpi_state->address = reg->address; + lpi_state->entry_method = + reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? + ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; + } else if (obj->type == ACPI_TYPE_INTEGER) { + lpi_state->entry_method = ACPI_CSTATE_INTEGER; + lpi_state->address = obj->integer.value; + } else { + continue; + } + + /* elements[7,8] skipped for now i.e. Residency/Usage counter*/ + + obj = pkg_elem + 9; + if (obj->type == ACPI_TYPE_STRING) + strlcpy(lpi_state->desc, obj->string.pointer, + ACPI_CX_DESC_LEN); + + lpi_state->index = state_idx; + if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { + pr_debug("No min. residency found, assuming 10 us\n"); + lpi_state->min_residency = 10; + } + + if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { + pr_debug("No wakeup residency found, assuming 10 us\n"); + lpi_state->wake_latency = 10; + } + + if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) + lpi_state->flags = 0; + + if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) + lpi_state->arch_flags = 0; + + if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) + lpi_state->res_cnt_freq = 1; + + if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) + lpi_state->enable_parent_state = 0; + } + + acpi_handle_debug(handle, "Found %d power states\n", state_idx); +end: + kfree(buffer.pointer); + return ret; +} + +/* + * flat_state_cnt - the number of composite LPI states after the process of flattening + */ +static int flat_state_cnt; + +/** + * combine_lpi_states - combine local and parent LPI states to form a composite LPI state + * + * @local: local LPI state + * @parent: parent LPI state + * @result: composite LPI state + */ +static bool combine_lpi_states(struct acpi_lpi_state *local, + struct acpi_lpi_state *parent, + struct acpi_lpi_state *result) +{ + if (parent->entry_method == ACPI_CSTATE_INTEGER) { + if (!parent->address) /* 0 means autopromotable */ + return false; + result->address = local->address + parent->address; + } else { + result->address = parent->address; + } + + result->min_residency = max(local->min_residency, parent->min_residency); + result->wake_latency = local->wake_latency + parent->wake_latency; + result->enable_parent_state = parent->enable_parent_state; + result->entry_method = local->entry_method; + + result->flags = parent->flags; + result->arch_flags = parent->arch_flags; + result->index = parent->index; + + strlcpy(result->desc, local->desc, ACPI_CX_DESC_LEN); + strlcat(result->desc, "+", ACPI_CX_DESC_LEN); + strlcat(result->desc, parent->desc, ACPI_CX_DESC_LEN); + return true; +} + +#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) + +static void stash_composite_state(struct acpi_lpi_states_array *curr_level, + struct acpi_lpi_state *t) +{ + curr_level->composite_states[curr_level->composite_states_size++] = t; +} + +static int flatten_lpi_states(struct acpi_processor *pr, + struct acpi_lpi_states_array *curr_level, + struct acpi_lpi_states_array *prev_level) +{ + int i, j, state_count = curr_level->size; + struct acpi_lpi_state *p, *t = curr_level->entries; + + curr_level->composite_states_size = 0; + for (j = 0; j < state_count; j++, t++) { + struct acpi_lpi_state *flpi; + + if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) + continue; + + if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { + pr_warn("Limiting number of LPI states to max (%d)\n", + ACPI_PROCESSOR_MAX_POWER); + pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); + break; + } + + flpi = &pr->power.lpi_states[flat_state_cnt]; + + if (!prev_level) { /* leaf/processor node */ + memcpy(flpi, t, sizeof(*t)); + stash_composite_state(curr_level, flpi); + flat_state_cnt++; + continue; + } + + for (i = 0; i < prev_level->composite_states_size; i++) { + p = prev_level->composite_states[i]; + if (t->index <= p->enable_parent_state && + combine_lpi_states(p, t, flpi)) { + stash_composite_state(curr_level, flpi); + flat_state_cnt++; + flpi++; + } + } + } + + kfree(curr_level->entries); + return 0; +} + +static int acpi_processor_get_lpi_info(struct acpi_processor *pr) +{ + int ret, i; + acpi_status status; + acpi_handle handle = pr->handle, pr_ahandle; + struct acpi_device *d = NULL; + struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; + + if (!osc_pc_lpi_support_confirmed) + return -EOPNOTSUPP; + + if (!acpi_has_method(handle, "_LPI")) + return -EINVAL; + + flat_state_cnt = 0; + prev = &info[0]; + curr = &info[1]; + handle = pr->handle; + ret = acpi_processor_evaluate_lpi(handle, prev); + if (ret) + return ret; + flatten_lpi_states(pr, prev, NULL); + + status = acpi_get_parent(handle, &pr_ahandle); + while (ACPI_SUCCESS(status)) { + acpi_bus_get_device(pr_ahandle, &d); + handle = pr_ahandle; + + if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) + break; + + /* can be optional ? */ + if (!acpi_has_method(handle, "_LPI")) + break; + + ret = acpi_processor_evaluate_lpi(handle, curr); + if (ret) + break; + + /* flatten all the LPI states in this level of hierarchy */ + flatten_lpi_states(pr, curr, prev); + + tmp = prev, prev = curr, curr = tmp; + + status = acpi_get_parent(handle, &pr_ahandle); + } + + pr->power.count = flat_state_cnt; + /* reset the index after flattening */ + for (i = 0; i < pr->power.count; i++) + pr->power.lpi_states[i].index = i; + + /* Tell driver that _LPI is supported. */ + pr->flags.has_lpi = 1; + pr->flags.power = 1; + + return 0; +} + +int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) +{ + return -ENODEV; +} + +int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +{ + return -ENODEV; +} + +/** + * acpi_idle_lpi_enter - enters an ACPI any LPI state + * @dev: the target CPU + * @drv: cpuidle driver containing cpuidle state info + * @index: index of target state + * + * Return: 0 for success or negative value for error + */ +static int acpi_idle_lpi_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor *pr; + struct acpi_lpi_state *lpi; + + pr = __this_cpu_read(processors); + + if (unlikely(!pr)) + return -EINVAL; + + lpi = &pr->power.lpi_states[index]; + if (lpi->entry_method == ACPI_CSTATE_FFH) + return acpi_processor_ffh_lpi_enter(lpi); + + return -EINVAL; +} + +static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) +{ + int i; + struct acpi_lpi_state *lpi; + struct cpuidle_state *state; + struct cpuidle_driver *drv = &acpi_idle_driver; + + if (!pr->flags.has_lpi) + return -EOPNOTSUPP; + + for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) { + lpi = &pr->power.lpi_states[i]; + + state = &drv->states[i]; + snprintf(state->name, CPUIDLE_NAME_LEN, "LPI-%d", i); + strlcpy(state->desc, lpi->desc, CPUIDLE_DESC_LEN); + state->exit_latency = lpi->wake_latency; + state->target_residency = lpi->min_residency; + if (lpi->arch_flags) + state->flags |= CPUIDLE_FLAG_TIMER_STOP; + state->enter = acpi_idle_lpi_enter; + drv->safe_state_index = i; + } + + drv->state_count = i; + + return 0; +} + +/** + * acpi_processor_setup_cpuidle_states- prepares and configures cpuidle + * global state data i.e. idle routines + * + * @pr: the ACPI processor + */ +static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +{ + int i; + struct cpuidle_driver *drv = &acpi_idle_driver; + + if (!pr->flags.power_setup_done || !pr->flags.power) + return -EINVAL; + + drv->safe_state_index = -1; + for (i = CPUIDLE_DRIVER_STATE_START; i < CPUIDLE_STATE_MAX; i++) { + drv->states[i].name[0] = '\0'; + drv->states[i].desc[0] = '\0'; + } + + if (pr->flags.has_lpi) + return acpi_processor_setup_lpi_states(pr); + + return acpi_processor_setup_cstates(pr); +} + +/** + * acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE + * device i.e. per-cpu data + * + * @pr: the ACPI processor + * @dev : the cpuidle device + */ +static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr, + struct cpuidle_device *dev) +{ + if (!pr->flags.power_setup_done || !pr->flags.power || !dev) + return -EINVAL; + + dev->cpu = pr->id; + if (pr->flags.has_lpi) + return acpi_processor_ffh_lpi_probe(pr->id); + + return acpi_processor_setup_cpuidle_cx(pr, dev); +} + +static int acpi_processor_get_power_info(struct acpi_processor *pr) +{ + int ret; + + ret = acpi_processor_get_lpi_info(pr); + if (ret) + ret = acpi_processor_get_cstate_info(pr); + + return ret; +} + int acpi_processor_hotplug(struct acpi_processor *pr) { int ret = 0; @@ -978,18 +1340,15 @@ int acpi_processor_hotplug(struct acpi_processor *pr) if (disabled_by_idle_boot_param()) return 0; - if (nocst) - return -ENODEV; - if (!pr->flags.power_setup_done) return -ENODEV; dev = per_cpu(acpi_cpuidle_device, pr->id); cpuidle_pause_and_lock(); cpuidle_disable_device(dev); - acpi_processor_get_power_info(pr); - if (pr->flags.power) { - acpi_processor_setup_cpuidle_cx(pr, dev); + ret = acpi_processor_get_power_info(pr); + if (!ret && pr->flags.power) { + acpi_processor_setup_cpuidle_dev(pr, dev); ret = cpuidle_enable_device(dev); } cpuidle_resume_and_unlock(); @@ -997,7 +1356,7 @@ int acpi_processor_hotplug(struct acpi_processor *pr) return ret; } -int acpi_processor_cst_has_changed(struct acpi_processor *pr) +int acpi_processor_power_state_has_changed(struct acpi_processor *pr) { int cpu; struct acpi_processor *_pr; @@ -1006,9 +1365,6 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) if (disabled_by_idle_boot_param()) return 0; - if (nocst) - return -ENODEV; - if (!pr->flags.power_setup_done) return -ENODEV; @@ -1045,7 +1401,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr) acpi_processor_get_power_info(_pr); if (_pr->flags.power) { dev = per_cpu(acpi_cpuidle_device, cpu); - acpi_processor_setup_cpuidle_cx(_pr, dev); + acpi_processor_setup_cpuidle_dev(_pr, dev); cpuidle_enable_device(dev); } } @@ -1092,7 +1448,7 @@ int acpi_processor_power_init(struct acpi_processor *pr) return -ENOMEM; per_cpu(acpi_cpuidle_device, pr->id) = dev; - acpi_processor_setup_cpuidle_cx(pr, dev); + acpi_processor_setup_cpuidle_dev(pr, dev); /* Register per-cpu cpuidle_device. Cpuidle driver * must already be registered before registering device diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 48779d6..e2dcb0c 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -39,6 +39,7 @@ #define ACPI_CSTATE_SYSTEMIO 0 #define ACPI_CSTATE_FFH 1 #define ACPI_CSTATE_HALT 2 +#define ACPI_CSTATE_INTEGER 3 #define ACPI_CX_DESC_LEN 32 @@ -67,9 +68,25 @@ struct acpi_processor_cx { char desc[ACPI_CX_DESC_LEN]; }; +struct acpi_lpi_state { + u32 min_residency; + u32 wake_latency; /* worst case */ + u32 flags; + u32 arch_flags; + u32 res_cnt_freq; + u32 enable_parent_state; + u64 address; + u8 index; + u8 entry_method; + char desc[ACPI_CX_DESC_LEN]; +}; + struct acpi_processor_power { int count; - struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER]; + union { + struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER]; + struct acpi_lpi_state lpi_states[ACPI_PROCESSOR_MAX_POWER]; + }; int timer_broadcast_on_state; }; @@ -189,6 +206,7 @@ struct acpi_processor_flags { u8 bm_control:1; u8 bm_check:1; u8 has_cst:1; + u8 has_lpi:1; u8 power_setup_done:1; u8 bm_rld_set:1; u8 need_hotplug_init:1; @@ -371,7 +389,7 @@ extern struct cpuidle_driver acpi_idle_driver; #ifdef CONFIG_ACPI_PROCESSOR_IDLE int acpi_processor_power_init(struct acpi_processor *pr); int acpi_processor_power_exit(struct acpi_processor *pr); -int acpi_processor_cst_has_changed(struct acpi_processor *pr); +int acpi_processor_power_state_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); #else static inline int acpi_processor_power_init(struct acpi_processor *pr) @@ -384,7 +402,7 @@ static inline int acpi_processor_power_exit(struct acpi_processor *pr) return -ENODEV; } -static inline int acpi_processor_cst_has_changed(struct acpi_processor *pr) +static inline int acpi_processor_power_state_has_changed(struct acpi_processor *pr) { return -ENODEV; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 288fac5..6575456 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -444,8 +444,12 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_HOTPLUG_OST_SUPPORT 0x00000008 #define OSC_SB_APEI_SUPPORT 0x00000010 #define OSC_SB_CPC_SUPPORT 0x00000020 +#define OSC_SB_CPCV2_SUPPORT 0x00000040 +#define OSC_SB_PCLPI_SUPPORT 0x00000080 +#define OSC_SB_OSLPI_SUPPORT 0x00000100 extern bool osc_sb_apei_support_acked; +extern bool osc_pc_lpi_support_confirmed; /* PCI Host Bridge _OSC: Capabilities DWORD 2: Support Field */ #define OSC_PCI_EXT_CONFIG_SUPPORT 0x00000001 -- cgit v0.10.2 From ce3ad71026b10f12b46cb88226e547be886b2251 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:55 +0100 Subject: arm64: cpuidle: drop __init section marker to arm_cpuidle_init Commit ea389daa7fd9 (arm64: cpuidle: add __init section marker to arm_cpuidle_init) added the __init annotation to arm_cpuidle_init as it was not needed after booting which was correct at that time. However with the introduction of ACPI LPI support, this will be used from cpuhotplug path in ACPI processor driver. This patch drops the __init annotation from arm_cpuidle_init to avoid the following warning: WARNING: vmlinux.o(.text+0x113c8): Section mismatch in reference from the function acpi_processor_ffh_lpi_probe() to the function .init.text:arm_cpuidle_init() The function acpi_processor_ffh_lpi_probe() references the function __init arm_cpuidle_init(). This is often because acpi_processor_ffh_lpi_probe() lacks a __init annotation or the annotation of arm_cpuidle_init is wrong. Acked-by: Catalin Marinas Acked-by: Lorenzo Pieralisi Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c index e11857f..06786fd 100644 --- a/arch/arm64/kernel/cpuidle.c +++ b/arch/arm64/kernel/cpuidle.c @@ -15,7 +15,7 @@ #include #include -int __init arm_cpuidle_init(unsigned int cpu) +int arm_cpuidle_init(unsigned int cpu) { int ret = -EOPNOTSUPP; -- cgit v0.10.2 From 220276e09bd1ccf7563b8092f7bd794336420eb1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:56 +0100 Subject: cpuidle: introduce CPU_PM_CPU_IDLE_ENTER macro for ARM{32, 64} The function arm_enter_idle_state is exactly the same in both generic ARM{32,64} CPUIdle driver and will be the same even on ARM64 backend for ACPI processor idle driver. So we can unify it and move it to a common place by introducing CPU_PM_CPU_IDLE_ENTER macro that can be used in all places avoiding duplication. This is in preparation of reuse of the generic cpuidle entry function for ACPI LPI support on ARM64. Suggested-by: Rafael J. Wysocki Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index e342565e..4ba3d3f 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -36,26 +36,12 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx) { - int ret; - - if (!idx) { - cpu_do_idle(); - return idx; - } - - ret = cpu_pm_enter(); - if (!ret) { - /* - * Pass idle state index to cpu_suspend which in turn will - * call the CPU ops suspend protocol with idle index as a - * parameter. - */ - ret = arm_cpuidle_suspend(idx); - - cpu_pm_exit(); - } - - return ret ? -1 : idx; + /* + * Pass idle state index to arm_cpuidle_suspend which in turn + * will call the CPU ops suspend protocol with idle index as a + * parameter. + */ + return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx); } static struct cpuidle_driver arm_idle_driver = { diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 07b83d3..bb31373 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -252,4 +252,22 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) #define CPUIDLE_DRIVER_STATE_START 0 #endif +#define CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx) \ +({ \ + int __ret; \ + \ + if (!idx) { \ + cpu_do_idle(); \ + return idx; \ + } \ + \ + __ret = cpu_pm_enter(); \ + if (!__ret) { \ + __ret = low_level_idle_enter(idx); \ + cpu_pm_exit(); \ + } \ + \ + __ret ? -1 : idx; \ +}) + #endif /* _LINUX_CPUIDLE_H */ -- cgit v0.10.2 From c2a25c141f4e80debd8eec3e9a5f9d25daedb54f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:57 +0100 Subject: drivers: firmware: psci: initialise idle states using ACPI LPI This patch adds support for initialisation of PSCI CPUIdle states from Low Power Idle(_LPI) entries in the ACPI tables when acpi is enabled. Acked-by: Lorenzo Pieralisi Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index 03e0458..8263429 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "psci: " fmt +#include #include #include #include @@ -256,13 +257,6 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) u32 *psci_states; struct device_node *state_node; - /* - * If the PSCI cpu_suspend function hook has not been initialized - * idle states must not be enabled, so bail out - */ - if (!psci_ops.cpu_suspend) - return -EOPNOTSUPP; - /* Count idle states */ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", count))) { @@ -310,11 +304,69 @@ free_mem: return ret; } +#ifdef CONFIG_ACPI +#include + +static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu) +{ + int i, count; + u32 *psci_states; + struct acpi_lpi_state *lpi; + struct acpi_processor *pr = per_cpu(processors, cpu); + + if (unlikely(!pr || !pr->flags.has_lpi)) + return -EINVAL; + + count = pr->power.count - 1; + if (count <= 0) + return -ENODEV; + + psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); + if (!psci_states) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u32 state; + + lpi = &pr->power.lpi_states[i + 1]; + /* + * Only bits[31:0] represent a PSCI power_state while + * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification + */ + state = lpi->address; + if (!psci_power_state_is_valid(state)) { + pr_warn("Invalid PSCI power state %#x\n", state); + kfree(psci_states); + return -EINVAL; + } + psci_states[i] = state; + } + /* Idle states parsed correctly, initialize per-cpu pointer */ + per_cpu(psci_power_state, cpu) = psci_states; + return 0; +} +#else +static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu) +{ + return -EINVAL; +} +#endif + int psci_cpu_init_idle(unsigned int cpu) { struct device_node *cpu_node; int ret; + /* + * If the PSCI cpu_suspend function hook has not been initialized + * idle states must not be enabled, so bail out + */ + if (!psci_ops.cpu_suspend) + return -EOPNOTSUPP; + + if (!acpi_disabled) + return psci_acpi_cpu_init_idle(cpu); + cpu_node = of_get_cpu_node(cpu, NULL); if (!cpu_node) return -ENODEV; -- cgit v0.10.2 From 5a611ed969b04269512ba4c07466bd5f4eaab8a7 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:58 +0100 Subject: arm64: add support for ACPI Low Power Idle(LPI) This patch adds appropriate callbacks to support ACPI Low Power Idle (LPI) on ARM64. Acked-by: Catalin Marinas Acked-by: Lorenzo Pieralisi Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c index 06786fd..75a0f8a 100644 --- a/arch/arm64/kernel/cpuidle.c +++ b/arch/arm64/kernel/cpuidle.c @@ -9,6 +9,9 @@ * published by the Free Software Foundation. */ +#include +#include +#include #include #include @@ -39,3 +42,18 @@ int arm_cpuidle_suspend(int index) return cpu_ops[cpu]->cpu_suspend(index); } + +#ifdef CONFIG_ACPI + +#include + +int acpi_processor_ffh_lpi_probe(unsigned int cpu) +{ + return arm_cpuidle_init(cpu); +} + +int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +{ + return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, lpi->index); +} +#endif -- cgit v0.10.2 From 8fc85c6ad849055c248b3d39d8c14c06dc6a7989 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 19 Jul 2016 18:52:59 +0100 Subject: ACPI: enable ACPI_PROCESSOR_IDLE on ARM64 Now that ACPI processor idle driver supports LPI(Low Power Idle), lets enable ACPI_PROCESSOR_IDLE for ARM64 too. This patch just removes the IA64 and X86 dependency on ACPI_PROCESSOR_IDLE Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 1358fb7..d74275c 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -238,7 +238,7 @@ config ACPI_CPPC_LIB config ACPI_PROCESSOR tristate "Processor" depends on X86 || IA64 || ARM64 - select ACPI_PROCESSOR_IDLE if X86 || IA64 + select ACPI_PROCESSOR_IDLE select ACPI_CPU_FREQ_PSS if X86 || IA64 default y help -- cgit v0.10.2