1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21c8fce27SZhang Rui /*
31c8fce27SZhang Rui * sysfs.c - ACPI sysfs interface to userspace.
41c8fce27SZhang Rui */
51c8fce27SZhang Rui
67db8d1d9SRafael J. Wysocki #define pr_fmt(fmt) "ACPI: " fmt
77db8d1d9SRafael J. Wysocki
844497fabSAndy Shevchenko #include <linux/acpi.h>
9d3121e64SAndy Shevchenko #include <linux/bitmap.h>
101c8fce27SZhang Rui #include <linux/init.h>
111c8fce27SZhang Rui #include <linux/kernel.h>
124612c7f1SChristophe JAILLET #include <linux/kstrtox.h>
131c8fce27SZhang Rui #include <linux/moduleparam.h>
141c8fce27SZhang Rui
153f8055c3SRafael J. Wysocki #include "internal.h"
163f8055c3SRafael J. Wysocki
171c8fce27SZhang Rui #ifdef CONFIG_ACPI_DEBUG
181c8fce27SZhang Rui /*
191c8fce27SZhang Rui * ACPI debug sysfs I/F, including:
201c8fce27SZhang Rui * /sys/modules/acpi/parameters/debug_layer
211c8fce27SZhang Rui * /sys/modules/acpi/parameters/debug_level
221c8fce27SZhang Rui * /sys/modules/acpi/parameters/trace_method_name
231c8fce27SZhang Rui * /sys/modules/acpi/parameters/trace_state
241c8fce27SZhang Rui * /sys/modules/acpi/parameters/trace_debug_layer
251c8fce27SZhang Rui * /sys/modules/acpi/parameters/trace_debug_level
261c8fce27SZhang Rui */
271c8fce27SZhang Rui
281c8fce27SZhang Rui struct acpi_dlayer {
291c8fce27SZhang Rui const char *name;
301c8fce27SZhang Rui unsigned long value;
311c8fce27SZhang Rui };
321c8fce27SZhang Rui struct acpi_dlevel {
331c8fce27SZhang Rui const char *name;
341c8fce27SZhang Rui unsigned long value;
351c8fce27SZhang Rui };
361c8fce27SZhang Rui #define ACPI_DEBUG_INIT(v) { .name = #v, .value = v }
371c8fce27SZhang Rui
381c8fce27SZhang Rui static const struct acpi_dlayer acpi_debug_layers[] = {
391c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_UTILITIES),
401c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_HARDWARE),
411c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_EVENTS),
421c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_TABLES),
431c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_NAMESPACE),
441c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_PARSER),
451c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_DISPATCHER),
461c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_EXECUTER),
471c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_RESOURCES),
481c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_CA_DEBUGGER),
491c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_OS_SERVICES),
501c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER),
511c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_COMPILER),
521c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_TOOLS),
531c8fce27SZhang Rui };
541c8fce27SZhang Rui
551c8fce27SZhang Rui static const struct acpi_dlevel acpi_debug_levels[] = {
561c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_INIT),
571c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_DEBUG_OBJECT),
581c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_INFO),
5993d98831SLv Zheng ACPI_DEBUG_INIT(ACPI_LV_REPAIR),
607901a052SLv Zheng ACPI_DEBUG_INIT(ACPI_LV_TRACE_POINT),
611c8fce27SZhang Rui
621c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_INIT_NAMES),
631c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_PARSE),
641c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_LOAD),
651c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_DISPATCH),
661c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_EXEC),
671c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_NAMES),
681c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_OPREGION),
691c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_BFIELD),
701c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_TABLES),
711c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_VALUES),
721c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_OBJECTS),
731c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_RESOURCES),
741c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_USER_REQUESTS),
751c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_PACKAGE),
761c8fce27SZhang Rui
771c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_ALLOCATIONS),
781c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_FUNCTIONS),
791c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_OPTIMIZATIONS),
801c8fce27SZhang Rui
811c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_MUTEX),
821c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_THREADS),
831c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_IO),
841c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_INTERRUPTS),
851c8fce27SZhang Rui
861c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_AML_DISASSEMBLE),
871c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_VERBOSE_INFO),
881c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_FULL_TABLES),
891c8fce27SZhang Rui ACPI_DEBUG_INIT(ACPI_LV_EVENTS),
901c8fce27SZhang Rui };
911c8fce27SZhang Rui
param_get_debug_layer(char * buffer,const struct kernel_param * kp)92ec652b35SZhang Rui static int param_get_debug_layer(char *buffer, const struct kernel_param *kp)
931c8fce27SZhang Rui {
941c8fce27SZhang Rui int result = 0;
951c8fce27SZhang Rui int i;
961c8fce27SZhang Rui
971c8fce27SZhang Rui result = sprintf(buffer, "%-25s\tHex SET\n", "Description");
981c8fce27SZhang Rui
991c8fce27SZhang Rui for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) {
1001c8fce27SZhang Rui result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",
1011c8fce27SZhang Rui acpi_debug_layers[i].name,
1021c8fce27SZhang Rui acpi_debug_layers[i].value,
1031c8fce27SZhang Rui (acpi_dbg_layer & acpi_debug_layers[i].value)
1041c8fce27SZhang Rui ? '*' : ' ');
1051c8fce27SZhang Rui }
1061c8fce27SZhang Rui result +=
1071c8fce27SZhang Rui sprintf(buffer + result, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS",
1081c8fce27SZhang Rui ACPI_ALL_DRIVERS,
1091c8fce27SZhang Rui (acpi_dbg_layer & ACPI_ALL_DRIVERS) ==
1101c8fce27SZhang Rui ACPI_ALL_DRIVERS ? '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS)
1111c8fce27SZhang Rui == 0 ? ' ' : '-');
1121c8fce27SZhang Rui result +=
1131c8fce27SZhang Rui sprintf(buffer + result,
1141c8fce27SZhang Rui "--\ndebug_layer = 0x%08X ( * = enabled)\n",
1151c8fce27SZhang Rui acpi_dbg_layer);
1161c8fce27SZhang Rui
1171c8fce27SZhang Rui return result;
1181c8fce27SZhang Rui }
1191c8fce27SZhang Rui
param_get_debug_level(char * buffer,const struct kernel_param * kp)120ec652b35SZhang Rui static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
1211c8fce27SZhang Rui {
1221c8fce27SZhang Rui int result = 0;
1231c8fce27SZhang Rui int i;
1241c8fce27SZhang Rui
1251c8fce27SZhang Rui result = sprintf(buffer, "%-25s\tHex SET\n", "Description");
1261c8fce27SZhang Rui
1271c8fce27SZhang Rui for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) {
1281c8fce27SZhang Rui result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",
1291c8fce27SZhang Rui acpi_debug_levels[i].name,
1301c8fce27SZhang Rui acpi_debug_levels[i].value,
1311c8fce27SZhang Rui (acpi_dbg_level & acpi_debug_levels[i].value)
1321c8fce27SZhang Rui ? '*' : ' ');
1331c8fce27SZhang Rui }
1341c8fce27SZhang Rui result +=
1351c8fce27SZhang Rui sprintf(buffer + result, "--\ndebug_level = 0x%08X (* = enabled)\n",
1361c8fce27SZhang Rui acpi_dbg_level);
1371c8fce27SZhang Rui
1381c8fce27SZhang Rui return result;
1391c8fce27SZhang Rui }
1401c8fce27SZhang Rui
1419c8b04beSVasiliy Kulikov static const struct kernel_param_ops param_ops_debug_layer = {
142ec652b35SZhang Rui .set = param_set_uint,
143ec652b35SZhang Rui .get = param_get_debug_layer,
144ec652b35SZhang Rui };
145ec652b35SZhang Rui
1469c8b04beSVasiliy Kulikov static const struct kernel_param_ops param_ops_debug_level = {
147ec652b35SZhang Rui .set = param_set_uint,
148ec652b35SZhang Rui .get = param_get_debug_level,
149ec652b35SZhang Rui };
150ec652b35SZhang Rui
151ec652b35SZhang Rui module_param_cb(debug_layer, ¶m_ops_debug_layer, &acpi_dbg_layer, 0644);
152ec652b35SZhang Rui module_param_cb(debug_level, ¶m_ops_debug_level, &acpi_dbg_level, 0644);
1531c8fce27SZhang Rui
154a0186bcfSLv Zheng static char trace_method_name[1024];
1557901a052SLv Zheng
param_set_trace_method_name(const char * val,const struct kernel_param * kp)1563e87ead4SColin Ian King static int param_set_trace_method_name(const char *val,
1573e87ead4SColin Ian King const struct kernel_param *kp)
1587901a052SLv Zheng {
1597901a052SLv Zheng u32 saved_flags = 0;
160a0186bcfSLv Zheng bool is_abs_path = true;
1617901a052SLv Zheng
162a0186bcfSLv Zheng if (*val != '\\')
163a0186bcfSLv Zheng is_abs_path = false;
164a0186bcfSLv Zheng
165a0186bcfSLv Zheng if ((is_abs_path && strlen(val) > 1023) ||
166a0186bcfSLv Zheng (!is_abs_path && strlen(val) > 1022)) {
1677901a052SLv Zheng pr_err("%s: string parameter too long\n", kp->name);
1687901a052SLv Zheng return -ENOSPC;
1697901a052SLv Zheng }
1707901a052SLv Zheng
1717901a052SLv Zheng /*
1727901a052SLv Zheng * It's not safe to update acpi_gbl_trace_method_name without
1737901a052SLv Zheng * having the tracer stopped, so we save the original tracer
1747901a052SLv Zheng * state and disable it.
1757901a052SLv Zheng */
1767901a052SLv Zheng saved_flags = acpi_gbl_trace_flags;
1777901a052SLv Zheng (void)acpi_debug_trace(NULL,
1787901a052SLv Zheng acpi_gbl_trace_dbg_level,
1797901a052SLv Zheng acpi_gbl_trace_dbg_layer,
1807901a052SLv Zheng 0);
1817901a052SLv Zheng
1827901a052SLv Zheng /* This is a hack. We can't kmalloc in early boot. */
183a0186bcfSLv Zheng if (is_abs_path)
184a0186bcfSLv Zheng strcpy(trace_method_name, val);
185a0186bcfSLv Zheng else {
186a0186bcfSLv Zheng trace_method_name[0] = '\\';
187a0186bcfSLv Zheng strcpy(trace_method_name+1, val);
188a0186bcfSLv Zheng }
1897901a052SLv Zheng
1907901a052SLv Zheng /* Restore the original tracer state */
1917901a052SLv Zheng (void)acpi_debug_trace(trace_method_name,
1927901a052SLv Zheng acpi_gbl_trace_dbg_level,
1937901a052SLv Zheng acpi_gbl_trace_dbg_layer,
1947901a052SLv Zheng saved_flags);
1957901a052SLv Zheng
1967901a052SLv Zheng return 0;
1977901a052SLv Zheng }
1987901a052SLv Zheng
param_get_trace_method_name(char * buffer,const struct kernel_param * kp)1997901a052SLv Zheng static int param_get_trace_method_name(char *buffer, const struct kernel_param *kp)
2007901a052SLv Zheng {
20182d08d61SXu Panda return sysfs_emit(buffer, "%s\n", acpi_gbl_trace_method_name);
2027901a052SLv Zheng }
2037901a052SLv Zheng
2047901a052SLv Zheng static const struct kernel_param_ops param_ops_trace_method = {
2057901a052SLv Zheng .set = param_set_trace_method_name,
2067901a052SLv Zheng .get = param_get_trace_method_name,
2077901a052SLv Zheng };
2087901a052SLv Zheng
2097901a052SLv Zheng static const struct kernel_param_ops param_ops_trace_attrib = {
2107901a052SLv Zheng .set = param_set_uint,
2117901a052SLv Zheng .get = param_get_uint,
2127901a052SLv Zheng };
2137901a052SLv Zheng
2147901a052SLv Zheng module_param_cb(trace_method_name, ¶m_ops_trace_method, &trace_method_name, 0644);
2157901a052SLv Zheng module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);
2167901a052SLv Zheng module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644);
2171c8fce27SZhang Rui
param_set_trace_state(const char * val,const struct kernel_param * kp)218e4dca7b7SKees Cook static int param_set_trace_state(const char *val,
219e4dca7b7SKees Cook const struct kernel_param *kp)
2201c8fce27SZhang Rui {
2217901a052SLv Zheng acpi_status status;
2227901a052SLv Zheng const char *method = trace_method_name;
2237901a052SLv Zheng u32 flags = 0;
2241c8fce27SZhang Rui
2257901a052SLv Zheng /* So "xxx-once" comparison should go prior than "xxx" comparison */
2267901a052SLv Zheng #define acpi_compare_param(val, key) \
2277901a052SLv Zheng strncmp((val), (key), sizeof(key) - 1)
2281c8fce27SZhang Rui
2297901a052SLv Zheng if (!acpi_compare_param(val, "enable")) {
2307901a052SLv Zheng method = NULL;
2317901a052SLv Zheng flags = ACPI_TRACE_ENABLED;
2327901a052SLv Zheng } else if (!acpi_compare_param(val, "disable"))
2337901a052SLv Zheng method = NULL;
2347901a052SLv Zheng else if (!acpi_compare_param(val, "method-once"))
2357901a052SLv Zheng flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT;
2367901a052SLv Zheng else if (!acpi_compare_param(val, "method"))
2377901a052SLv Zheng flags = ACPI_TRACE_ENABLED;
2387901a052SLv Zheng else if (!acpi_compare_param(val, "opcode-once"))
2397901a052SLv Zheng flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT | ACPI_TRACE_OPCODE;
2407901a052SLv Zheng else if (!acpi_compare_param(val, "opcode"))
2417901a052SLv Zheng flags = ACPI_TRACE_ENABLED | ACPI_TRACE_OPCODE;
2427901a052SLv Zheng else
2437901a052SLv Zheng return -EINVAL;
2441c8fce27SZhang Rui
2457901a052SLv Zheng status = acpi_debug_trace(method,
2467901a052SLv Zheng acpi_gbl_trace_dbg_level,
2477901a052SLv Zheng acpi_gbl_trace_dbg_layer,
2487901a052SLv Zheng flags);
2497901a052SLv Zheng if (ACPI_FAILURE(status))
2507901a052SLv Zheng return -EBUSY;
2511c8fce27SZhang Rui
2527901a052SLv Zheng return 0;
2531c8fce27SZhang Rui }
2541c8fce27SZhang Rui
param_get_trace_state(char * buffer,const struct kernel_param * kp)255e4dca7b7SKees Cook static int param_get_trace_state(char *buffer, const struct kernel_param *kp)
2561c8fce27SZhang Rui {
2577901a052SLv Zheng if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED))
2585ae318f6SXiongfeng Wang return sprintf(buffer, "disable\n");
2590023b28bSAndy Shevchenko if (!acpi_gbl_trace_method_name)
2600023b28bSAndy Shevchenko return sprintf(buffer, "enable\n");
2617901a052SLv Zheng if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT)
2625ae318f6SXiongfeng Wang return sprintf(buffer, "method-once\n");
2631c8fce27SZhang Rui else
2645ae318f6SXiongfeng Wang return sprintf(buffer, "method\n");
2651c8fce27SZhang Rui }
2661c8fce27SZhang Rui
2671c8fce27SZhang Rui module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
2681c8fce27SZhang Rui NULL, 0644);
2691c8fce27SZhang Rui #endif /* CONFIG_ACPI_DEBUG */
2701c8fce27SZhang Rui
271aecad432SThomas Renninger
272aecad432SThomas Renninger /* /sys/modules/acpi/parameters/aml_debug_output */
273aecad432SThomas Renninger
274aecad432SThomas Renninger module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object,
275481c1381SLv Zheng byte, 0644);
276aecad432SThomas Renninger MODULE_PARM_DESC(aml_debug_output,
277aecad432SThomas Renninger "To enable/disable the ACPI Debug Object output.");
278aecad432SThomas Renninger
2791c8fce27SZhang Rui /* /sys/module/acpi/parameters/acpica_version */
param_get_acpica_version(char * buffer,const struct kernel_param * kp)280e4dca7b7SKees Cook static int param_get_acpica_version(char *buffer,
281e4dca7b7SKees Cook const struct kernel_param *kp)
2821c8fce27SZhang Rui {
2831c8fce27SZhang Rui int result;
2841c8fce27SZhang Rui
2855ae318f6SXiongfeng Wang result = sprintf(buffer, "%x\n", ACPI_CA_VERSION);
2861c8fce27SZhang Rui
2871c8fce27SZhang Rui return result;
2881c8fce27SZhang Rui }
2891c8fce27SZhang Rui
2901c8fce27SZhang Rui module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);
2911c8fce27SZhang Rui
2921c8fce27SZhang Rui /*
2931c8fce27SZhang Rui * ACPI table sysfs I/F:
2941c8fce27SZhang Rui * /sys/firmware/acpi/tables/
2957dae6326STony Luck * /sys/firmware/acpi/tables/data/
2961c8fce27SZhang Rui * /sys/firmware/acpi/tables/dynamic/
2971c8fce27SZhang Rui */
2981c8fce27SZhang Rui
2991c8fce27SZhang Rui static LIST_HEAD(acpi_table_attr_list);
3001c8fce27SZhang Rui static struct kobject *tables_kobj;
3017dae6326STony Luck static struct kobject *tables_data_kobj;
3021c8fce27SZhang Rui static struct kobject *dynamic_tables_kobj;
3033f8055c3SRafael J. Wysocki static struct kobject *hotplug_kobj;
3041c8fce27SZhang Rui
305914c6194SLv Zheng #define ACPI_MAX_TABLE_INSTANCES 999
306914c6194SLv Zheng #define ACPI_INST_SIZE 4 /* including trailing 0 */
307914c6194SLv Zheng
3081c8fce27SZhang Rui struct acpi_table_attr {
3091c8fce27SZhang Rui struct bin_attribute attr;
31032786755SBob Moore char name[ACPI_NAMESEG_SIZE];
3111c8fce27SZhang Rui int instance;
31232786755SBob Moore char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE];
3131c8fce27SZhang Rui struct list_head node;
3141c8fce27SZhang Rui };
3151c8fce27SZhang Rui
3167dae6326STony Luck struct acpi_data_attr {
3177dae6326STony Luck struct bin_attribute attr;
3187dae6326STony Luck u64 addr;
3197dae6326STony Luck };
3207dae6326STony Luck
acpi_table_show(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t offset,size_t count)3211c8fce27SZhang Rui static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
3221c8fce27SZhang Rui struct bin_attribute *bin_attr, char *buf,
3231c8fce27SZhang Rui loff_t offset, size_t count)
3241c8fce27SZhang Rui {
3251c8fce27SZhang Rui struct acpi_table_attr *table_attr =
3261c8fce27SZhang Rui container_of(bin_attr, struct acpi_table_attr, attr);
3271c8fce27SZhang Rui struct acpi_table_header *table_header = NULL;
3281c8fce27SZhang Rui acpi_status status;
3290de0e198SDan Williams ssize_t rc;
3301c8fce27SZhang Rui
331914c6194SLv Zheng status = acpi_get_table(table_attr->name, table_attr->instance,
332914c6194SLv Zheng &table_header);
3331c8fce27SZhang Rui if (ACPI_FAILURE(status))
3341c8fce27SZhang Rui return -ENODEV;
3351c8fce27SZhang Rui
3360de0e198SDan Williams rc = memory_read_from_buffer(buf, count, &offset, table_header,
3370de0e198SDan Williams table_header->length);
3380de0e198SDan Williams acpi_put_table(table_header);
3390de0e198SDan Williams return rc;
3401c8fce27SZhang Rui }
3411c8fce27SZhang Rui
acpi_table_attr_init(struct kobject * tables_obj,struct acpi_table_attr * table_attr,struct acpi_table_header * table_header)342914c6194SLv Zheng static int acpi_table_attr_init(struct kobject *tables_obj,
343914c6194SLv Zheng struct acpi_table_attr *table_attr,
3441c8fce27SZhang Rui struct acpi_table_header *table_header)
3451c8fce27SZhang Rui {
3461c8fce27SZhang Rui struct acpi_table_header *header = NULL;
3471c8fce27SZhang Rui struct acpi_table_attr *attr = NULL;
348914c6194SLv Zheng char instance_str[ACPI_INST_SIZE];
3491c8fce27SZhang Rui
3501c8fce27SZhang Rui sysfs_attr_init(&table_attr->attr.attr);
3515599fb69SBob Moore ACPI_COPY_NAMESEG(table_attr->name, table_header->signature);
3521c8fce27SZhang Rui
3531c8fce27SZhang Rui list_for_each_entry(attr, &acpi_table_attr_list, node) {
3545599fb69SBob Moore if (ACPI_COMPARE_NAMESEG(table_attr->name, attr->name))
3551c8fce27SZhang Rui if (table_attr->instance < attr->instance)
3561c8fce27SZhang Rui table_attr->instance = attr->instance;
3571c8fce27SZhang Rui }
3581c8fce27SZhang Rui table_attr->instance++;
359914c6194SLv Zheng if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) {
36086ca3b0aSHanjun Guo pr_warn("%4.4s: too many table instances\n", table_attr->name);
361914c6194SLv Zheng return -ERANGE;
362914c6194SLv Zheng }
3631c8fce27SZhang Rui
3645599fb69SBob Moore ACPI_COPY_NAMESEG(table_attr->filename, table_header->signature);
36532786755SBob Moore table_attr->filename[ACPI_NAMESEG_SIZE] = '\0';
3661c8fce27SZhang Rui if (table_attr->instance > 1 || (table_attr->instance == 1 &&
3671c8fce27SZhang Rui !acpi_get_table
368914c6194SLv Zheng (table_header->signature, 2, &header))) {
369914c6194SLv Zheng snprintf(instance_str, sizeof(instance_str), "%u",
3701c8fce27SZhang Rui table_attr->instance);
371914c6194SLv Zheng strcat(table_attr->filename, instance_str);
372914c6194SLv Zheng }
3731c8fce27SZhang Rui
374083ca8c4SDaisuke HATAYAMA table_attr->attr.size = table_header->length;
3751c8fce27SZhang Rui table_attr->attr.read = acpi_table_show;
376914c6194SLv Zheng table_attr->attr.attr.name = table_attr->filename;
3771c8fce27SZhang Rui table_attr->attr.attr.mode = 0400;
3781c8fce27SZhang Rui
379914c6194SLv Zheng return sysfs_create_bin_file(tables_obj, &table_attr->attr);
3801c8fce27SZhang Rui }
3811c8fce27SZhang Rui
acpi_sysfs_table_handler(u32 event,void * table,void * context)38268bdb677SOctavian Purdila acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
3831c8fce27SZhang Rui {
3841c8fce27SZhang Rui struct acpi_table_attr *table_attr;
3851c8fce27SZhang Rui
3861c8fce27SZhang Rui switch (event) {
387307ecb0aSLv Zheng case ACPI_TABLE_EVENT_INSTALL:
388b272c059SAndy Shevchenko table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
3891c8fce27SZhang Rui if (!table_attr)
3901c8fce27SZhang Rui return AE_NO_MEMORY;
3911c8fce27SZhang Rui
392914c6194SLv Zheng if (acpi_table_attr_init(dynamic_tables_kobj,
393914c6194SLv Zheng table_attr, table)) {
3941c8fce27SZhang Rui kfree(table_attr);
3951c8fce27SZhang Rui return AE_ERROR;
396914c6194SLv Zheng }
3971c8fce27SZhang Rui list_add_tail(&table_attr->node, &acpi_table_attr_list);
3981c8fce27SZhang Rui break;
399307ecb0aSLv Zheng case ACPI_TABLE_EVENT_LOAD:
4001c8fce27SZhang Rui case ACPI_TABLE_EVENT_UNLOAD:
401307ecb0aSLv Zheng case ACPI_TABLE_EVENT_UNINSTALL:
4021c8fce27SZhang Rui /*
4031c8fce27SZhang Rui * we do not need to do anything right now
4041c8fce27SZhang Rui * because the table is not deleted from the
4051c8fce27SZhang Rui * global table list when unloading it.
4061c8fce27SZhang Rui */
4071c8fce27SZhang Rui break;
4081c8fce27SZhang Rui default:
4091c8fce27SZhang Rui return AE_BAD_PARAMETER;
4101c8fce27SZhang Rui }
4111c8fce27SZhang Rui return AE_OK;
4121c8fce27SZhang Rui }
4131c8fce27SZhang Rui
acpi_data_show(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t offset,size_t count)4147dae6326STony Luck static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj,
4157dae6326STony Luck struct bin_attribute *bin_attr, char *buf,
4167dae6326STony Luck loff_t offset, size_t count)
4177dae6326STony Luck {
4187dae6326STony Luck struct acpi_data_attr *data_attr;
4191bbc2178SLorenzo Pieralisi void __iomem *base;
4201bbc2178SLorenzo Pieralisi ssize_t size;
4217dae6326STony Luck
4227dae6326STony Luck data_attr = container_of(bin_attr, struct acpi_data_attr, attr);
4231bbc2178SLorenzo Pieralisi size = data_attr->attr.size;
4247dae6326STony Luck
4251bbc2178SLorenzo Pieralisi if (offset < 0)
4261bbc2178SLorenzo Pieralisi return -EINVAL;
4271bbc2178SLorenzo Pieralisi
4281bbc2178SLorenzo Pieralisi if (offset >= size)
4291bbc2178SLorenzo Pieralisi return 0;
4301bbc2178SLorenzo Pieralisi
4311bbc2178SLorenzo Pieralisi if (count > size - offset)
4321bbc2178SLorenzo Pieralisi count = size - offset;
4331bbc2178SLorenzo Pieralisi
4341bbc2178SLorenzo Pieralisi base = acpi_os_map_iomem(data_attr->addr, size);
4357dae6326STony Luck if (!base)
4367dae6326STony Luck return -ENOMEM;
4377dae6326STony Luck
4381bbc2178SLorenzo Pieralisi memcpy_fromio(buf, base + offset, count);
4391bbc2178SLorenzo Pieralisi
4401bbc2178SLorenzo Pieralisi acpi_os_unmap_iomem(base, size);
4411bbc2178SLorenzo Pieralisi
4421bbc2178SLorenzo Pieralisi return count;
4437dae6326STony Luck }
4447dae6326STony Luck
acpi_bert_data_init(void * th,struct acpi_data_attr * data_attr)4457dae6326STony Luck static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr)
4467dae6326STony Luck {
4477dae6326STony Luck struct acpi_table_bert *bert = th;
4487dae6326STony Luck
4497dae6326STony Luck if (bert->header.length < sizeof(struct acpi_table_bert) ||
4507dae6326STony Luck bert->region_length < sizeof(struct acpi_hest_generic_status)) {
4517dae6326STony Luck kfree(data_attr);
4527dae6326STony Luck return -EINVAL;
4537dae6326STony Luck }
4547dae6326STony Luck data_attr->addr = bert->address;
4557dae6326STony Luck data_attr->attr.size = bert->region_length;
4567dae6326STony Luck data_attr->attr.attr.name = "BERT";
4577dae6326STony Luck
4587dae6326STony Luck return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);
4597dae6326STony Luck }
4607dae6326STony Luck
acpi_ccel_data_init(void * th,struct acpi_data_attr * data_attr)461*4f855dceSKuppuswamy Sathyanarayanan static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr)
462*4f855dceSKuppuswamy Sathyanarayanan {
463*4f855dceSKuppuswamy Sathyanarayanan struct acpi_table_ccel *ccel = th;
464*4f855dceSKuppuswamy Sathyanarayanan
465*4f855dceSKuppuswamy Sathyanarayanan if (ccel->header.length < sizeof(struct acpi_table_ccel) ||
466*4f855dceSKuppuswamy Sathyanarayanan !ccel->log_area_start_address || !ccel->log_area_minimum_length) {
467*4f855dceSKuppuswamy Sathyanarayanan kfree(data_attr);
468*4f855dceSKuppuswamy Sathyanarayanan return -EINVAL;
469*4f855dceSKuppuswamy Sathyanarayanan }
470*4f855dceSKuppuswamy Sathyanarayanan data_attr->addr = ccel->log_area_start_address;
471*4f855dceSKuppuswamy Sathyanarayanan data_attr->attr.size = ccel->log_area_minimum_length;
472*4f855dceSKuppuswamy Sathyanarayanan data_attr->attr.attr.name = "CCEL";
473*4f855dceSKuppuswamy Sathyanarayanan
474*4f855dceSKuppuswamy Sathyanarayanan return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);
475*4f855dceSKuppuswamy Sathyanarayanan }
476*4f855dceSKuppuswamy Sathyanarayanan
4777dae6326STony Luck static struct acpi_data_obj {
4787dae6326STony Luck char *name;
4797dae6326STony Luck int (*fn)(void *, struct acpi_data_attr *);
4807dae6326STony Luck } acpi_data_objs[] = {
4817dae6326STony Luck { ACPI_SIG_BERT, acpi_bert_data_init },
482*4f855dceSKuppuswamy Sathyanarayanan { ACPI_SIG_CCEL, acpi_ccel_data_init },
4837dae6326STony Luck };
4847dae6326STony Luck
4857dae6326STony Luck #define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs)
4867dae6326STony Luck
acpi_table_data_init(struct acpi_table_header * th)4877dae6326STony Luck static int acpi_table_data_init(struct acpi_table_header *th)
4887dae6326STony Luck {
4897dae6326STony Luck struct acpi_data_attr *data_attr;
4907dae6326STony Luck int i;
4917dae6326STony Luck
4927dae6326STony Luck for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) {
4935599fb69SBob Moore if (ACPI_COMPARE_NAMESEG(th->signature, acpi_data_objs[i].name)) {
4947dae6326STony Luck data_attr = kzalloc(sizeof(*data_attr), GFP_KERNEL);
4957dae6326STony Luck if (!data_attr)
4967dae6326STony Luck return -ENOMEM;
4977dae6326STony Luck sysfs_attr_init(&data_attr->attr.attr);
4987dae6326STony Luck data_attr->attr.read = acpi_data_show;
4997dae6326STony Luck data_attr->attr.attr.mode = 0400;
5007dae6326STony Luck return acpi_data_objs[i].fn(th, data_attr);
5017dae6326STony Luck }
5027dae6326STony Luck }
5037dae6326STony Luck return 0;
5047dae6326STony Luck }
5057dae6326STony Luck
acpi_tables_sysfs_init(void)5061c8fce27SZhang Rui static int acpi_tables_sysfs_init(void)
5071c8fce27SZhang Rui {
5081c8fce27SZhang Rui struct acpi_table_attr *table_attr;
5091c8fce27SZhang Rui struct acpi_table_header *table_header = NULL;
510de03beedSJeremy Compostella int table_index;
511de03beedSJeremy Compostella acpi_status status;
512de03beedSJeremy Compostella int ret;
5131c8fce27SZhang Rui
5141c8fce27SZhang Rui tables_kobj = kobject_create_and_add("tables", acpi_kobj);
5151c8fce27SZhang Rui if (!tables_kobj)
5161c8fce27SZhang Rui goto err;
5171c8fce27SZhang Rui
5187dae6326STony Luck tables_data_kobj = kobject_create_and_add("data", tables_kobj);
5197dae6326STony Luck if (!tables_data_kobj)
5207dae6326STony Luck goto err_tables_data;
5217dae6326STony Luck
5221c8fce27SZhang Rui dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj);
5231c8fce27SZhang Rui if (!dynamic_tables_kobj)
5241c8fce27SZhang Rui goto err_dynamic_tables;
5251c8fce27SZhang Rui
526de03beedSJeremy Compostella for (table_index = 0;; table_index++) {
527de03beedSJeremy Compostella status = acpi_get_table_by_index(table_index, &table_header);
528de03beedSJeremy Compostella
529de03beedSJeremy Compostella if (status == AE_BAD_PARAMETER)
530de03beedSJeremy Compostella break;
531de03beedSJeremy Compostella
532de03beedSJeremy Compostella if (ACPI_FAILURE(status))
533de03beedSJeremy Compostella continue;
534de03beedSJeremy Compostella
535de03beedSJeremy Compostella table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
5361c8fce27SZhang Rui if (!table_attr)
5371c8fce27SZhang Rui return -ENOMEM;
5381c8fce27SZhang Rui
539914c6194SLv Zheng ret = acpi_table_attr_init(tables_kobj,
540914c6194SLv Zheng table_attr, table_header);
541de03beedSJeremy Compostella if (ret) {
5421c8fce27SZhang Rui kfree(table_attr);
543de03beedSJeremy Compostella return ret;
5441c8fce27SZhang Rui }
545de03beedSJeremy Compostella list_add_tail(&table_attr->node, &acpi_table_attr_list);
5467dae6326STony Luck acpi_table_data_init(table_header);
547de03beedSJeremy Compostella }
548de03beedSJeremy Compostella
5491c8fce27SZhang Rui kobject_uevent(tables_kobj, KOBJ_ADD);
5507dae6326STony Luck kobject_uevent(tables_data_kobj, KOBJ_ADD);
5511c8fce27SZhang Rui kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);
5521c8fce27SZhang Rui
55368bdb677SOctavian Purdila return 0;
5541c8fce27SZhang Rui err_dynamic_tables:
5557dae6326STony Luck kobject_put(tables_data_kobj);
5567dae6326STony Luck err_tables_data:
5571c8fce27SZhang Rui kobject_put(tables_kobj);
5581c8fce27SZhang Rui err:
5591c8fce27SZhang Rui return -ENOMEM;
5601c8fce27SZhang Rui }
5611c8fce27SZhang Rui
5621c8fce27SZhang Rui /*
5631c8fce27SZhang Rui * Detailed ACPI IRQ counters:
5641c8fce27SZhang Rui * /sys/firmware/acpi/interrupts/
5651c8fce27SZhang Rui */
5661c8fce27SZhang Rui
5671c8fce27SZhang Rui u32 acpi_irq_handled;
5681c8fce27SZhang Rui u32 acpi_irq_not_handled;
5691c8fce27SZhang Rui
5701c8fce27SZhang Rui #define COUNT_GPE 0
5711c8fce27SZhang Rui #define COUNT_SCI 1 /* acpi_irq_handled */
5721c8fce27SZhang Rui #define COUNT_SCI_NOT 2 /* acpi_irq_not_handled */
5731c8fce27SZhang Rui #define COUNT_ERROR 3 /* other */
5741c8fce27SZhang Rui #define NUM_COUNTERS_EXTRA 4
5751c8fce27SZhang Rui
5761c8fce27SZhang Rui struct event_counter {
5771c8fce27SZhang Rui u32 count;
5781c8fce27SZhang Rui u32 flags;
5791c8fce27SZhang Rui };
5801c8fce27SZhang Rui
5811c8fce27SZhang Rui static struct event_counter *all_counters;
5821c8fce27SZhang Rui static u32 num_gpes;
5831c8fce27SZhang Rui static u32 num_counters;
5841c8fce27SZhang Rui static struct attribute **all_attrs;
5851c8fce27SZhang Rui static u32 acpi_gpe_count;
5861c8fce27SZhang Rui
5871c8fce27SZhang Rui static struct attribute_group interrupt_stats_attr_group = {
5881c8fce27SZhang Rui .name = "interrupts",
5891c8fce27SZhang Rui };
5901c8fce27SZhang Rui
5911c8fce27SZhang Rui static struct kobj_attribute *counter_attrs;
5921c8fce27SZhang Rui
delete_gpe_attr_array(void)5931c8fce27SZhang Rui static void delete_gpe_attr_array(void)
5941c8fce27SZhang Rui {
5951c8fce27SZhang Rui struct event_counter *tmp = all_counters;
5961c8fce27SZhang Rui
5971c8fce27SZhang Rui all_counters = NULL;
5981c8fce27SZhang Rui kfree(tmp);
5991c8fce27SZhang Rui
6001c8fce27SZhang Rui if (counter_attrs) {
6011c8fce27SZhang Rui int i;
6021c8fce27SZhang Rui
6031c8fce27SZhang Rui for (i = 0; i < num_gpes; i++)
6041c8fce27SZhang Rui kfree(counter_attrs[i].attr.name);
6051c8fce27SZhang Rui
6061c8fce27SZhang Rui kfree(counter_attrs);
6071c8fce27SZhang Rui }
6081c8fce27SZhang Rui kfree(all_attrs);
6091c8fce27SZhang Rui }
6101c8fce27SZhang Rui
gpe_count(u32 gpe_number)611a0fcdb23SLin Ming static void gpe_count(u32 gpe_number)
6121c8fce27SZhang Rui {
6131c8fce27SZhang Rui acpi_gpe_count++;
6141c8fce27SZhang Rui
6151c8fce27SZhang Rui if (!all_counters)
6161c8fce27SZhang Rui return;
6171c8fce27SZhang Rui
6181c8fce27SZhang Rui if (gpe_number < num_gpes)
6191c8fce27SZhang Rui all_counters[gpe_number].count++;
6201c8fce27SZhang Rui else
6211c8fce27SZhang Rui all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +
6221c8fce27SZhang Rui COUNT_ERROR].count++;
6231c8fce27SZhang Rui }
6241c8fce27SZhang Rui
fixed_event_count(u32 event_number)625a0fcdb23SLin Ming static void fixed_event_count(u32 event_number)
6261c8fce27SZhang Rui {
6271c8fce27SZhang Rui if (!all_counters)
6281c8fce27SZhang Rui return;
6291c8fce27SZhang Rui
6301c8fce27SZhang Rui if (event_number < ACPI_NUM_FIXED_EVENTS)
6311c8fce27SZhang Rui all_counters[num_gpes + event_number].count++;
6321c8fce27SZhang Rui else
6331c8fce27SZhang Rui all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +
6341c8fce27SZhang Rui COUNT_ERROR].count++;
6351c8fce27SZhang Rui }
6361c8fce27SZhang Rui
acpi_global_event_handler(u32 event_type,acpi_handle device,u32 event_number,void * context)637644ef74eSLv Zheng static void acpi_global_event_handler(u32 event_type, acpi_handle device,
638a0fcdb23SLin Ming u32 event_number, void *context)
639a0fcdb23SLin Ming {
6407db8d1d9SRafael J. Wysocki if (event_type == ACPI_EVENT_TYPE_GPE) {
641a0fcdb23SLin Ming gpe_count(event_number);
6427db8d1d9SRafael J. Wysocki pr_debug("GPE event 0x%02x\n", event_number);
6437db8d1d9SRafael J. Wysocki } else if (event_type == ACPI_EVENT_TYPE_FIXED) {
644a0fcdb23SLin Ming fixed_event_count(event_number);
6457db8d1d9SRafael J. Wysocki pr_debug("Fixed event 0x%02x\n", event_number);
6467db8d1d9SRafael J. Wysocki } else {
6477db8d1d9SRafael J. Wysocki pr_debug("Other event 0x%02x\n", event_number);
6487db8d1d9SRafael J. Wysocki }
649a0fcdb23SLin Ming }
650a0fcdb23SLin Ming
get_status(u32 index,acpi_event_status * ret,acpi_handle * handle)6513fd16d70SRafael J. Wysocki static int get_status(u32 index, acpi_event_status *ret,
6521c8fce27SZhang Rui acpi_handle *handle)
6531c8fce27SZhang Rui {
6543fd16d70SRafael J. Wysocki acpi_status status;
6551c8fce27SZhang Rui
6561c8fce27SZhang Rui if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
657f18ebc21SDan Carpenter return -EINVAL;
6581c8fce27SZhang Rui
6591c8fce27SZhang Rui if (index < num_gpes) {
6603fd16d70SRafael J. Wysocki status = acpi_get_gpe_device(index, handle);
6613fd16d70SRafael J. Wysocki if (ACPI_FAILURE(status)) {
662e6a55ccbSRafael J. Wysocki pr_warn("Invalid GPE 0x%x", index);
6633fd16d70SRafael J. Wysocki return -ENXIO;
6641c8fce27SZhang Rui }
6653fd16d70SRafael J. Wysocki status = acpi_get_gpe_status(*handle, index, ret);
6663fd16d70SRafael J. Wysocki } else {
6673fd16d70SRafael J. Wysocki status = acpi_get_event_status(index - num_gpes, ret);
6683fd16d70SRafael J. Wysocki }
6693fd16d70SRafael J. Wysocki if (ACPI_FAILURE(status))
6703fd16d70SRafael J. Wysocki return -EIO;
6711c8fce27SZhang Rui
6723fd16d70SRafael J. Wysocki return 0;
6731c8fce27SZhang Rui }
6741c8fce27SZhang Rui
counter_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)6751c8fce27SZhang Rui static ssize_t counter_show(struct kobject *kobj,
6761c8fce27SZhang Rui struct kobj_attribute *attr, char *buf)
6771c8fce27SZhang Rui {
6781c8fce27SZhang Rui int index = attr - counter_attrs;
6791c8fce27SZhang Rui int size;
6801c8fce27SZhang Rui acpi_handle handle;
6811c8fce27SZhang Rui acpi_event_status status;
6821c8fce27SZhang Rui int result = 0;
6831c8fce27SZhang Rui
6841c8fce27SZhang Rui all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count =
6851c8fce27SZhang Rui acpi_irq_handled;
6861c8fce27SZhang Rui all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT].count =
6871c8fce27SZhang Rui acpi_irq_not_handled;
6881c8fce27SZhang Rui all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count =
6891c8fce27SZhang Rui acpi_gpe_count;
6907b1a1322SNan Li size = sprintf(buf, "%8u", all_counters[index].count);
6911c8fce27SZhang Rui
6921c8fce27SZhang Rui /* "gpe_all" or "sci" */
6931c8fce27SZhang Rui if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)
6941c8fce27SZhang Rui goto end;
6951c8fce27SZhang Rui
6961c8fce27SZhang Rui result = get_status(index, &status, &handle);
6971c8fce27SZhang Rui if (result)
6981c8fce27SZhang Rui goto end;
6991c8fce27SZhang Rui
70018864cc4SLv Zheng if (status & ACPI_EVENT_FLAG_ENABLE_SET)
70118864cc4SLv Zheng size += sprintf(buf + size, " EN");
70218864cc4SLv Zheng else
70318864cc4SLv Zheng size += sprintf(buf + size, " ");
70418864cc4SLv Zheng if (status & ACPI_EVENT_FLAG_STATUS_SET)
70518864cc4SLv Zheng size += sprintf(buf + size, " STS");
70618864cc4SLv Zheng else
70718864cc4SLv Zheng size += sprintf(buf + size, " ");
70818864cc4SLv Zheng
7092f857234SLv Zheng if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER))
7101c8fce27SZhang Rui size += sprintf(buf + size, " invalid ");
7111c8fce27SZhang Rui else if (status & ACPI_EVENT_FLAG_ENABLED)
7121c8fce27SZhang Rui size += sprintf(buf + size, " enabled ");
7131c8fce27SZhang Rui else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
7141c8fce27SZhang Rui size += sprintf(buf + size, " wake_enabled");
7151c8fce27SZhang Rui else
7161c8fce27SZhang Rui size += sprintf(buf + size, " disabled ");
71718864cc4SLv Zheng if (status & ACPI_EVENT_FLAG_MASKED)
71818864cc4SLv Zheng size += sprintf(buf + size, " masked ");
71918864cc4SLv Zheng else
72018864cc4SLv Zheng size += sprintf(buf + size, " unmasked");
7211c8fce27SZhang Rui
7221c8fce27SZhang Rui end:
7231c8fce27SZhang Rui size += sprintf(buf + size, "\n");
7241c8fce27SZhang Rui return result ? result : size;
7251c8fce27SZhang Rui }
7261c8fce27SZhang Rui
7271c8fce27SZhang Rui /*
7281c8fce27SZhang Rui * counter_set() sets the specified counter.
7291c8fce27SZhang Rui * setting the total "sci" file to any value clears all counters.
7301c8fce27SZhang Rui * enable/disable/clear a gpe/fixed event in user space.
7311c8fce27SZhang Rui */
counter_set(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t size)7321c8fce27SZhang Rui static ssize_t counter_set(struct kobject *kobj,
7331c8fce27SZhang Rui struct kobj_attribute *attr, const char *buf,
7341c8fce27SZhang Rui size_t size)
7351c8fce27SZhang Rui {
7361c8fce27SZhang Rui int index = attr - counter_attrs;
7371c8fce27SZhang Rui acpi_event_status status;
7381c8fce27SZhang Rui acpi_handle handle;
7391c8fce27SZhang Rui int result = 0;
740c48b1565SLan Tianyu unsigned long tmp;
7411c8fce27SZhang Rui
7421c8fce27SZhang Rui if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {
7431c8fce27SZhang Rui int i;
7441c8fce27SZhang Rui for (i = 0; i < num_counters; ++i)
7451c8fce27SZhang Rui all_counters[i].count = 0;
7461c8fce27SZhang Rui acpi_gpe_count = 0;
7471c8fce27SZhang Rui acpi_irq_handled = 0;
7481c8fce27SZhang Rui acpi_irq_not_handled = 0;
7491c8fce27SZhang Rui goto end;
7501c8fce27SZhang Rui }
7511c8fce27SZhang Rui
7521c8fce27SZhang Rui /* show the event status for both GPEs and Fixed Events */
7531c8fce27SZhang Rui result = get_status(index, &status, &handle);
7541c8fce27SZhang Rui if (result)
7551c8fce27SZhang Rui goto end;
7561c8fce27SZhang Rui
7572f857234SLv Zheng if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) {
75886ca3b0aSHanjun Guo pr_warn("Can not change Invalid GPE/Fixed Event status\n");
7591c8fce27SZhang Rui return -EINVAL;
7601c8fce27SZhang Rui }
7611c8fce27SZhang Rui
7621c8fce27SZhang Rui if (index < num_gpes) {
7631c8fce27SZhang Rui if (!strcmp(buf, "disable\n") &&
7641c8fce27SZhang Rui (status & ACPI_EVENT_FLAG_ENABLED))
7651c8fce27SZhang Rui result = acpi_disable_gpe(handle, index);
7661c8fce27SZhang Rui else if (!strcmp(buf, "enable\n") &&
7671c8fce27SZhang Rui !(status & ACPI_EVENT_FLAG_ENABLED))
7681c8fce27SZhang Rui result = acpi_enable_gpe(handle, index);
7691c8fce27SZhang Rui else if (!strcmp(buf, "clear\n") &&
77018864cc4SLv Zheng (status & ACPI_EVENT_FLAG_STATUS_SET))
7711c8fce27SZhang Rui result = acpi_clear_gpe(handle, index);
77218864cc4SLv Zheng else if (!strcmp(buf, "mask\n"))
77318864cc4SLv Zheng result = acpi_mask_gpe(handle, index, TRUE);
77418864cc4SLv Zheng else if (!strcmp(buf, "unmask\n"))
77518864cc4SLv Zheng result = acpi_mask_gpe(handle, index, FALSE);
776c48b1565SLan Tianyu else if (!kstrtoul(buf, 0, &tmp))
777c48b1565SLan Tianyu all_counters[index].count = tmp;
7781c8fce27SZhang Rui else
779c48b1565SLan Tianyu result = -EINVAL;
7801c8fce27SZhang Rui } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {
7811c8fce27SZhang Rui int event = index - num_gpes;
7821c8fce27SZhang Rui if (!strcmp(buf, "disable\n") &&
78318864cc4SLv Zheng (status & ACPI_EVENT_FLAG_ENABLE_SET))
7841c8fce27SZhang Rui result = acpi_disable_event(event, ACPI_NOT_ISR);
7851c8fce27SZhang Rui else if (!strcmp(buf, "enable\n") &&
78618864cc4SLv Zheng !(status & ACPI_EVENT_FLAG_ENABLE_SET))
7871c8fce27SZhang Rui result = acpi_enable_event(event, ACPI_NOT_ISR);
7881c8fce27SZhang Rui else if (!strcmp(buf, "clear\n") &&
78918864cc4SLv Zheng (status & ACPI_EVENT_FLAG_STATUS_SET))
7901c8fce27SZhang Rui result = acpi_clear_event(event);
791c48b1565SLan Tianyu else if (!kstrtoul(buf, 0, &tmp))
792c48b1565SLan Tianyu all_counters[index].count = tmp;
7931c8fce27SZhang Rui else
794c48b1565SLan Tianyu result = -EINVAL;
7951c8fce27SZhang Rui } else
7961c8fce27SZhang Rui all_counters[index].count = strtoul(buf, NULL, 0);
7971c8fce27SZhang Rui
7981c8fce27SZhang Rui if (ACPI_FAILURE(result))
7991c8fce27SZhang Rui result = -EINVAL;
8001c8fce27SZhang Rui end:
8011c8fce27SZhang Rui return result ? result : size;
8021c8fce27SZhang Rui }
8031c8fce27SZhang Rui
8049c4aa1eeSLv Zheng /*
8059c4aa1eeSLv Zheng * A Quirk Mechanism for GPE Flooding Prevention:
8069c4aa1eeSLv Zheng *
8079c4aa1eeSLv Zheng * Quirks may be needed to prevent GPE flooding on a specific GPE. The
8089c4aa1eeSLv Zheng * flooding typically cannot be detected and automatically prevented by
8099c4aa1eeSLv Zheng * ACPI_GPE_DISPATCH_NONE check because there is a _Lxx/_Exx prepared in
8109c4aa1eeSLv Zheng * the AML tables. This normally indicates a feature gap in Linux, thus
8119c4aa1eeSLv Zheng * instead of providing endless quirk tables, we provide a boot parameter
8129c4aa1eeSLv Zheng * for those who want this quirk. For example, if the users want to prevent
8139c4aa1eeSLv Zheng * the GPE flooding for GPE 00, they need to specify the following boot
8149c4aa1eeSLv Zheng * parameter:
8159c4aa1eeSLv Zheng * acpi_mask_gpe=0x00
816d3121e64SAndy Shevchenko * Note, the parameter can be a list (see bitmap_parselist() for the details).
8179c4aa1eeSLv Zheng * The masking status can be modified by the following runtime controlling
8189c4aa1eeSLv Zheng * interface:
8199c4aa1eeSLv Zheng * echo unmask > /sys/firmware/acpi/interrupts/gpe00
8209c4aa1eeSLv Zheng */
821a7583e72SYunfeng Ye #define ACPI_MASKABLE_GPE_MAX 0x100
8220f27cff8SPrarit Bhargava static DECLARE_BITMAP(acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) __initdata;
8239c4aa1eeSLv Zheng
acpi_gpe_set_masked_gpes(char * val)8249c4aa1eeSLv Zheng static int __init acpi_gpe_set_masked_gpes(char *val)
8259c4aa1eeSLv Zheng {
826d3121e64SAndy Shevchenko int ret;
8279c4aa1eeSLv Zheng u8 gpe;
8289c4aa1eeSLv Zheng
829d3121e64SAndy Shevchenko ret = kstrtou8(val, 0, &gpe);
830d3121e64SAndy Shevchenko if (ret) {
831d3121e64SAndy Shevchenko ret = bitmap_parselist(val, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX);
832d3121e64SAndy Shevchenko if (ret)
833d3121e64SAndy Shevchenko return ret;
834d3121e64SAndy Shevchenko } else
8350f27cff8SPrarit Bhargava set_bit(gpe, acpi_masked_gpes_map);
8369c4aa1eeSLv Zheng
8379c4aa1eeSLv Zheng return 1;
8389c4aa1eeSLv Zheng }
8399c4aa1eeSLv Zheng __setup("acpi_mask_gpe=", acpi_gpe_set_masked_gpes);
8409c4aa1eeSLv Zheng
acpi_gpe_apply_masked_gpes(void)8419c4aa1eeSLv Zheng void __init acpi_gpe_apply_masked_gpes(void)
8429c4aa1eeSLv Zheng {
8439c4aa1eeSLv Zheng acpi_handle handle;
8449c4aa1eeSLv Zheng acpi_status status;
845a7583e72SYunfeng Ye u16 gpe;
8469c4aa1eeSLv Zheng
8470f27cff8SPrarit Bhargava for_each_set_bit(gpe, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) {
8489c4aa1eeSLv Zheng status = acpi_get_gpe_device(gpe, &handle);
8499c4aa1eeSLv Zheng if (ACPI_SUCCESS(status)) {
8509c4aa1eeSLv Zheng pr_info("Masking GPE 0x%x.\n", gpe);
8519c4aa1eeSLv Zheng (void)acpi_mask_gpe(handle, gpe, TRUE);
8529c4aa1eeSLv Zheng }
8539c4aa1eeSLv Zheng }
8549c4aa1eeSLv Zheng }
8559c4aa1eeSLv Zheng
acpi_irq_stats_init(void)8561c8fce27SZhang Rui void acpi_irq_stats_init(void)
8571c8fce27SZhang Rui {
858a0fcdb23SLin Ming acpi_status status;
8591c8fce27SZhang Rui int i;
8601c8fce27SZhang Rui
8611c8fce27SZhang Rui if (all_counters)
8621c8fce27SZhang Rui return;
8631c8fce27SZhang Rui
8641c8fce27SZhang Rui num_gpes = acpi_current_gpe_count;
8651c8fce27SZhang Rui num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;
8661c8fce27SZhang Rui
867b272c059SAndy Shevchenko all_attrs = kcalloc(num_counters + 1, sizeof(*all_attrs), GFP_KERNEL);
8681c8fce27SZhang Rui if (all_attrs == NULL)
8691c8fce27SZhang Rui return;
8701c8fce27SZhang Rui
871b272c059SAndy Shevchenko all_counters = kcalloc(num_counters, sizeof(*all_counters), GFP_KERNEL);
8721c8fce27SZhang Rui if (all_counters == NULL)
8731c8fce27SZhang Rui goto fail;
8741c8fce27SZhang Rui
875644ef74eSLv Zheng status = acpi_install_global_event_handler(acpi_global_event_handler, NULL);
876a0fcdb23SLin Ming if (ACPI_FAILURE(status))
877a0fcdb23SLin Ming goto fail;
878a0fcdb23SLin Ming
879b272c059SAndy Shevchenko counter_attrs = kcalloc(num_counters, sizeof(*counter_attrs), GFP_KERNEL);
8801c8fce27SZhang Rui if (counter_attrs == NULL)
8811c8fce27SZhang Rui goto fail;
8821c8fce27SZhang Rui
8831c8fce27SZhang Rui for (i = 0; i < num_counters; ++i) {
8841c8fce27SZhang Rui char buffer[12];
8851c8fce27SZhang Rui char *name;
8861c8fce27SZhang Rui
8871c8fce27SZhang Rui if (i < num_gpes)
8881c8fce27SZhang Rui sprintf(buffer, "gpe%02X", i);
8891c8fce27SZhang Rui else if (i == num_gpes + ACPI_EVENT_PMTIMER)
8901c8fce27SZhang Rui sprintf(buffer, "ff_pmtimer");
8911c8fce27SZhang Rui else if (i == num_gpes + ACPI_EVENT_GLOBAL)
8921c8fce27SZhang Rui sprintf(buffer, "ff_gbl_lock");
8931c8fce27SZhang Rui else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)
8941c8fce27SZhang Rui sprintf(buffer, "ff_pwr_btn");
8951c8fce27SZhang Rui else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)
8961c8fce27SZhang Rui sprintf(buffer, "ff_slp_btn");
8971c8fce27SZhang Rui else if (i == num_gpes + ACPI_EVENT_RTC)
8981c8fce27SZhang Rui sprintf(buffer, "ff_rt_clk");
8991c8fce27SZhang Rui else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)
9001c8fce27SZhang Rui sprintf(buffer, "gpe_all");
9011c8fce27SZhang Rui else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)
9021c8fce27SZhang Rui sprintf(buffer, "sci");
9031c8fce27SZhang Rui else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT)
9041c8fce27SZhang Rui sprintf(buffer, "sci_not");
9051c8fce27SZhang Rui else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)
9061c8fce27SZhang Rui sprintf(buffer, "error");
9071c8fce27SZhang Rui else
9081c8fce27SZhang Rui sprintf(buffer, "bug%02X", i);
9091c8fce27SZhang Rui
910096a8aacSKees Cook name = kstrdup(buffer, GFP_KERNEL);
9111c8fce27SZhang Rui if (name == NULL)
9121c8fce27SZhang Rui goto fail;
9131c8fce27SZhang Rui
9141c8fce27SZhang Rui sysfs_attr_init(&counter_attrs[i].attr);
9151c8fce27SZhang Rui counter_attrs[i].attr.name = name;
9161c8fce27SZhang Rui counter_attrs[i].attr.mode = 0644;
9171c8fce27SZhang Rui counter_attrs[i].show = counter_show;
9181c8fce27SZhang Rui counter_attrs[i].store = counter_set;
9191c8fce27SZhang Rui
9201c8fce27SZhang Rui all_attrs[i] = &counter_attrs[i].attr;
9211c8fce27SZhang Rui }
9221c8fce27SZhang Rui
9231c8fce27SZhang Rui interrupt_stats_attr_group.attrs = all_attrs;
9241c8fce27SZhang Rui if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group))
9251c8fce27SZhang Rui return;
9261c8fce27SZhang Rui
9271c8fce27SZhang Rui fail:
9281c8fce27SZhang Rui delete_gpe_attr_array();
9291c8fce27SZhang Rui }
9301c8fce27SZhang Rui
interrupt_stats_exit(void)9311c8fce27SZhang Rui static void __exit interrupt_stats_exit(void)
9321c8fce27SZhang Rui {
9331c8fce27SZhang Rui sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);
9341c8fce27SZhang Rui
9351c8fce27SZhang Rui delete_gpe_attr_array();
9361c8fce27SZhang Rui }
9371c8fce27SZhang Rui
pm_profile_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)938a9c4cf29SAndy Shevchenko static ssize_t pm_profile_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
939362b6460SThomas Renninger {
940362b6460SThomas Renninger return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile);
941362b6460SThomas Renninger }
942362b6460SThomas Renninger
943a9c4cf29SAndy Shevchenko static const struct kobj_attribute pm_profile_attr = __ATTR_RO(pm_profile);
944362b6460SThomas Renninger
enabled_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)945a9c4cf29SAndy Shevchenko static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
9463f8055c3SRafael J. Wysocki {
9473f8055c3SRafael J. Wysocki struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
9483f8055c3SRafael J. Wysocki
9493f8055c3SRafael J. Wysocki return sprintf(buf, "%d\n", hotplug->enabled);
9503f8055c3SRafael J. Wysocki }
9513f8055c3SRafael J. Wysocki
enabled_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t size)952a9c4cf29SAndy Shevchenko static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
9533f8055c3SRafael J. Wysocki const char *buf, size_t size)
9543f8055c3SRafael J. Wysocki {
9553f8055c3SRafael J. Wysocki struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);
9563f8055c3SRafael J. Wysocki unsigned int val;
9573f8055c3SRafael J. Wysocki
9583f8055c3SRafael J. Wysocki if (kstrtouint(buf, 10, &val) || val > 1)
9593f8055c3SRafael J. Wysocki return -EINVAL;
9603f8055c3SRafael J. Wysocki
9613f8055c3SRafael J. Wysocki acpi_scan_hotplug_enabled(hotplug, val);
9623f8055c3SRafael J. Wysocki return size;
9633f8055c3SRafael J. Wysocki }
9643f8055c3SRafael J. Wysocki
965a9c4cf29SAndy Shevchenko static struct kobj_attribute hotplug_enabled_attr = __ATTR_RW(enabled);
9663f8055c3SRafael J. Wysocki
9673f8055c3SRafael J. Wysocki static struct attribute *hotplug_profile_attrs[] = {
9683f8055c3SRafael J. Wysocki &hotplug_enabled_attr.attr,
9693f8055c3SRafael J. Wysocki NULL
9703f8055c3SRafael J. Wysocki };
97117f18417SGreg Kroah-Hartman ATTRIBUTE_GROUPS(hotplug_profile);
9723f8055c3SRafael J. Wysocki
973a527b011SThomas Weißschuh static const struct kobj_type acpi_hotplug_profile_ktype = {
9743f8055c3SRafael J. Wysocki .sysfs_ops = &kobj_sysfs_ops,
97517f18417SGreg Kroah-Hartman .default_groups = hotplug_profile_groups,
9763f8055c3SRafael J. Wysocki };
9773f8055c3SRafael J. Wysocki
acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile * hotplug,const char * name)9783f8055c3SRafael J. Wysocki void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
9793f8055c3SRafael J. Wysocki const char *name)
9803f8055c3SRafael J. Wysocki {
9813f8055c3SRafael J. Wysocki int error;
9823f8055c3SRafael J. Wysocki
9833f8055c3SRafael J. Wysocki if (!hotplug_kobj)
9843f8055c3SRafael J. Wysocki goto err_out;
9853f8055c3SRafael J. Wysocki
986acd3e2c9SBjorn Helgaas error = kobject_init_and_add(&hotplug->kobj,
987acd3e2c9SBjorn Helgaas &acpi_hotplug_profile_ktype, hotplug_kobj, "%s", name);
9886e6c2528SQiushi Wu if (error) {
9896e6c2528SQiushi Wu kobject_put(&hotplug->kobj);
9903f8055c3SRafael J. Wysocki goto err_out;
9916e6c2528SQiushi Wu }
9923f8055c3SRafael J. Wysocki
9933f8055c3SRafael J. Wysocki kobject_uevent(&hotplug->kobj, KOBJ_ADD);
9943f8055c3SRafael J. Wysocki return;
9953f8055c3SRafael J. Wysocki
9963f8055c3SRafael J. Wysocki err_out:
99786ca3b0aSHanjun Guo pr_err("Unable to add hotplug profile '%s'\n", name);
9983f8055c3SRafael J. Wysocki }
9993f8055c3SRafael J. Wysocki
force_remove_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1000683058e3SRafael J. Wysocki static ssize_t force_remove_show(struct kobject *kobj,
1001683058e3SRafael J. Wysocki struct kobj_attribute *attr, char *buf)
1002683058e3SRafael J. Wysocki {
1003ffc10d82SMichal Hocko return sprintf(buf, "%d\n", 0);
1004683058e3SRafael J. Wysocki }
1005683058e3SRafael J. Wysocki
force_remove_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t size)1006683058e3SRafael J. Wysocki static ssize_t force_remove_store(struct kobject *kobj,
1007683058e3SRafael J. Wysocki struct kobj_attribute *attr,
1008683058e3SRafael J. Wysocki const char *buf, size_t size)
1009683058e3SRafael J. Wysocki {
1010683058e3SRafael J. Wysocki bool val;
1011683058e3SRafael J. Wysocki int ret;
1012683058e3SRafael J. Wysocki
10134612c7f1SChristophe JAILLET ret = kstrtobool(buf, &val);
1014683058e3SRafael J. Wysocki if (ret < 0)
1015683058e3SRafael J. Wysocki return ret;
1016683058e3SRafael J. Wysocki
1017ffc10d82SMichal Hocko if (val) {
1018ffc10d82SMichal Hocko pr_err("Enabling force_remove is not supported anymore. Please report to linux-acpi@vger.kernel.org if you depend on this functionality\n");
1019ffc10d82SMichal Hocko return -EINVAL;
1020ffc10d82SMichal Hocko }
1021683058e3SRafael J. Wysocki return size;
1022683058e3SRafael J. Wysocki }
1023683058e3SRafael J. Wysocki
1024a9c4cf29SAndy Shevchenko static const struct kobj_attribute force_remove_attr = __ATTR_RW(force_remove);
1025683058e3SRafael J. Wysocki
acpi_sysfs_init(void)10261c8fce27SZhang Rui int __init acpi_sysfs_init(void)
10271c8fce27SZhang Rui {
10281c8fce27SZhang Rui int result;
10291c8fce27SZhang Rui
10301c8fce27SZhang Rui result = acpi_tables_sysfs_init();
1031362b6460SThomas Renninger if (result)
1032362b6460SThomas Renninger return result;
10333f8055c3SRafael J. Wysocki
10343f8055c3SRafael J. Wysocki hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);
1035bc1e49dfSInsu Yun if (!hotplug_kobj)
1036bc1e49dfSInsu Yun return -ENOMEM;
1037bc1e49dfSInsu Yun
1038683058e3SRafael J. Wysocki result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr);
1039683058e3SRafael J. Wysocki if (result)
1040683058e3SRafael J. Wysocki return result;
1041683058e3SRafael J. Wysocki
1042362b6460SThomas Renninger result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
10431c8fce27SZhang Rui return result;
10441c8fce27SZhang Rui }
1045