diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/events/evevent.c | 2 | ||||
-rw-r--r-- | drivers/acpi/events/evgpe.c | 2 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 12 | ||||
-rw-r--r-- | drivers/acpi/system.c | 208 | ||||
-rw-r--r-- | drivers/acpi/utilities/utglobal.c | 2 |
5 files changed, 221 insertions, 5 deletions
diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c index e41287815ea1..3048801a37b5 100644 --- a/drivers/acpi/events/evevent.c +++ b/drivers/acpi/events/evevent.c @@ -259,7 +259,7 @@ u32 acpi_ev_fixed_event_detect(void) enable_bit_mask)) { /* Found an active (signalled) event */ - + acpi_os_fixed_event_count(i); int_status |= acpi_ev_fixed_event_dispatch((u32) i); } } diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index e22f4a973c0f..4bd9e2291bd9 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -618,7 +618,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) ACPI_FUNCTION_TRACE(ev_gpe_dispatch); - acpi_gpe_count++; + acpi_os_gpe_count(gpe_number); /* * If edge-triggered, clear the GPE status bit now. Note that diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e53fb516f9d4..1087efeca9b1 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -332,7 +332,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table, static irqreturn_t acpi_irq(int irq, void *dev_id) { - return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; + u32 handled; + + handled = (*acpi_irq_handler) (acpi_irq_context); + + if (handled) { + acpi_irq_handled++; + return IRQ_HANDLED; + } else + return IRQ_NONE; } acpi_status @@ -341,6 +349,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, { unsigned int irq; + acpi_irq_stats_init(); + /* * Ignore the GSI from the core, and use the value in our copy of the * FADT. It may not be the same if an interrupt source override exists diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 5ffe0ea18967..ce881713f7a6 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -40,6 +40,8 @@ ACPI_MODULE_NAME("system"); #define ACPI_SYSTEM_CLASS "system" #define ACPI_SYSTEM_DEVICE_NAME "System" +u32 acpi_irq_handled; + /* * Make ACPICA version work as module param */ @@ -166,6 +168,212 @@ static int acpi_system_sysfs_init(void) return 0; } +/* + * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ + * See Documentation/ABI/testing/sysfs-firmware-acpi + */ + +#define COUNT_GPE 0 +#define COUNT_SCI 1 /* acpi_irq_handled */ +#define COUNT_ERROR 2 /* other */ +#define NUM_COUNTERS_EXTRA 3 + +static u32 *all_counters; +static u32 num_gpes; +static u32 num_counters; +static struct attribute **all_attrs; +static u32 acpi_gpe_count; + +static struct attribute_group interrupt_stats_attr_group = { + .name = "interrupts", +}; +static struct kobj_attribute *counter_attrs; + +static int count_num_gpes(void) +{ + int count = 0; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_block_info *gpe_block; + acpi_cpu_flags flags; + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + count += gpe_block->register_count * + ACPI_GPE_REGISTER_WIDTH; + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + return count; +} + +static void delete_gpe_attr_array(void) +{ + u32 *tmp = all_counters; + + all_counters = NULL; + kfree(tmp); + + if (counter_attrs) { + int i; + + for (i = 0; i < num_gpes; i++) + kfree(counter_attrs[i].attr.name); + + kfree(counter_attrs); + } + kfree(all_attrs); + + return; +} + +void acpi_os_gpe_count(u32 gpe_number) +{ + acpi_gpe_count++; + + if (!all_counters) + return; + + if (gpe_number < num_gpes) + all_counters[gpe_number]++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; + + return; +} + +void acpi_os_fixed_event_count(u32 event_number) +{ + if (!all_counters) + return; + + if (event_number < ACPI_NUM_FIXED_EVENTS) + all_counters[num_gpes + event_number]++; + else + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; + + return; +} + +static ssize_t counter_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI] = + acpi_irq_handled; + all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE] = + acpi_gpe_count; + + return sprintf(buf, "%d\n", all_counters[attr - counter_attrs]); +} + +/* + * counter_set() sets the specified counter. + * setting the total "sci" file to any value clears all counters. + */ +static ssize_t counter_set(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t size) +{ + int index = attr - counter_attrs; + + if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { + int i; + for (i = 0; i < num_counters; ++i) + all_counters[i] = 0; + acpi_gpe_count = 0; + acpi_irq_handled = 0; + + } else + all_counters[index] = strtoul(buf, NULL, 0); + + return size; +} + +void acpi_irq_stats_init(void) +{ + int i; + + if (all_counters) + return; + + num_gpes = count_num_gpes(); + num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; + + all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), + GFP_KERNEL); + if (all_attrs == NULL) + return; + + all_counters = kzalloc(sizeof(u32) * (num_counters), GFP_KERNEL); + if (all_counters == NULL) + goto fail; + + counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), + GFP_KERNEL); + if (counter_attrs == NULL) + goto fail; + + for (i = 0; i < num_counters; ++i) { + char buffer[10]; + char *name; + + if (i < num_gpes) + sprintf(buffer, "gpe%02X", i); + else if (i == num_gpes + ACPI_EVENT_PMTIMER) + sprintf(buffer, "ff_pmtimer"); + else if (i == num_gpes + ACPI_EVENT_GLOBAL) + sprintf(buffer, "ff_gbl_lock"); + else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) + sprintf(buffer, "ff_pwr_btn"); + else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) + sprintf(buffer, "ff_slp_btn"); + else if (i == num_gpes + ACPI_EVENT_RTC) + sprintf(buffer, "ff_rt_clk"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) + sprintf(buffer, "gpe_all"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) + sprintf(buffer, "sci"); + else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) + sprintf(buffer, "error"); + else + sprintf(buffer, "bug%02X", i); + + name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); + if (name == NULL) + goto fail; + strncpy(name, buffer, strlen(buffer) + 1); + + counter_attrs[i].attr.name = name; + counter_attrs[i].attr.mode = 0644; + counter_attrs[i].show = counter_show; + counter_attrs[i].store = counter_set; + + all_attrs[i] = &counter_attrs[i].attr; + } + + interrupt_stats_attr_group.attrs = all_attrs; + sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group); + return; + +fail: + delete_gpe_attr_array(); + return; +} + +static void __exit interrupt_stats_exit(void) +{ + sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); + + delete_gpe_attr_array(); + + return; +} + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c index 93ea8290b4f7..630c9a2c5b7b 100644 --- a/drivers/acpi/utilities/utglobal.c +++ b/drivers/acpi/utilities/utglobal.c @@ -671,7 +671,6 @@ void acpi_ut_init_globals(void) /* GPE support */ - acpi_gpe_count = 0; acpi_gbl_gpe_xrupt_list_head = NULL; acpi_gbl_gpe_fadt_blocks[0] = NULL; acpi_gbl_gpe_fadt_blocks[1] = NULL; @@ -735,4 +734,3 @@ void acpi_ut_init_globals(void) ACPI_EXPORT_SYMBOL(acpi_dbg_level) ACPI_EXPORT_SYMBOL(acpi_dbg_layer) - ACPI_EXPORT_SYMBOL(acpi_gpe_count) |