Add GPE count under /sys/firmware/acpi/gpe. signed-off-by: Luming Yu <Luming.yu@xxxxxxxxx> -- drivers/acpi/Makefile | 1 drivers/acpi/bus.c | 44 ++++++++++++- drivers/acpi/events/evgpe.c | 1 drivers/acpi/gpe_stats.c | 104 +++++++++++++++++++++++++++++++ drivers/acpi/osl.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ include/acpi/aclocal.h | 1 include/linux/acpi.h | 7 ++ 7 files changed, 303 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5956e9f..066eeeb 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_ac obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o +obj-m += gpe_stats.o diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index dd49ea0..b1a8b24 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -735,6 +735,47 @@ #endif decl_subsys(acpi, NULL, NULL); +struct acpi_subsys_attr { + struct attribute attr; + int type; + int value; + ssize_t(*show) (struct attribute *, char *); + ssize_t(*store) (struct attribute *, const char *, size_t count); +}; +#define to_acpi_attr(k) container_of(k,struct acpi_subsys_attr,attr) +static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct acpi_subsys_attr *aattr = to_acpi_attr(attr); + if (aattr->show) + return aattr->show(attr, buf); + else + return -EIO; +} + +static ssize_t store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct acpi_subsys_attr *aattr = to_acpi_attr(attr); + if (aattr->store) + return aattr->store(attr, buf, count); + else + return -EIO; +} +static struct sysfs_ops sysfs_ops = { + .show = show, + .store = store, +}; + +static struct kobj_type ktype_acpi = { + .sysfs_ops = &sysfs_ops, +}; + +void acpi_subsys_set_sysfs_ops(void) +{ + acpi_subsys.kset.kobj.ktype = &ktype_acpi; + acpi_subsys.kset.ktype = &ktype_acpi; +} + static int __init acpi_init(void) { int result = 0; @@ -749,7 +790,7 @@ static int __init acpi_init(void) if (result < 0) printk(KERN_WARNING "%s: firmware_register error: %d\n", __FUNCTION__, result); - + acpi_subsys_set_sysfs_ops(); result = acpi_bus_init(); if (!result) { @@ -769,4 +810,5 @@ #endif return result; } +EXPORT_SYMBOL(acpi_subsys); subsys_initcall(acpi_init); diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index 635ba44..9b3face 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -621,6 +621,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_eve acpi_gpe_count++; + gpe_event_info->count++; /* * If edge-triggered, clear the GPE status bit now. Note that * level-triggered events are cleared after the GPE is serviced. diff --git a/drivers/acpi/gpe_stats.c b/drivers/acpi/gpe_stats.c new file mode 100644 index 0000000..7be639d --- /dev/null +++ b/drivers/acpi/gpe_stats.c @@ -0,0 +1,104 @@ +/* + * gpe_stats.c + * + * Copyright (C) 2007 Luming Yu <luming.yu@xxxxxxxxx> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; either version 2 of the License, or (at + * your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sysdev.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/spinlock.h> +#include <linux/acpi.h> + +typedef ssize_t(*show_func) (struct attribute * attr, char *buf); +extern struct subsystem acpi_subsys; +#define to_gpe_attr(k) container_of(k,struct gpe_attr, attr) +#define GPE_STATS_ATTR(_name,_mode,_show)\ +static struct gpe_attr _attr_##_name = {\ + .attr = {.name = __stringify(_name), .owner = THIS_MODULE, \ + .mode = _mode,},\ + .type = 0,\ + .value = -1, \ + .show = _show,\ +}; +extern int acpi_show_gpe_count(char *buf); +extern int acpi_show_one_gpe_count(int gpe_number, char *buf); +static ssize_t show_gpe_states(struct attribute *attr, char *buf) +{ + struct gpe_attr *gattr = to_gpe_attr(attr); + if (gattr->type == 0) { + if (gattr->value == -1) + return acpi_show_gpe_count(buf); + else + return acpi_show_one_gpe_count(gattr->value, buf); + } else + return 0; +} + +GPE_STATS_ATTR(stats, 0444, show_gpe_states); + +static struct attribute **default_attrs; +static struct attribute_group gpe_stats_attr_group = { + .name = "gpe", +}; +static struct gpe_attr *gpe_attrs; +extern int create_gpe_attr_array(struct module *module, + struct attribute ***attrs, + struct gpe_attr **gpe_attrs, + struct gpe_attr *attr, show_func func); +static int __init gpe_stats_init(void) +{ + int gpe_count; + int ret = 0; + + gpe_count = create_gpe_attr_array(THIS_MODULE, &default_attrs, + &gpe_attrs, &_attr_stats, + &show_gpe_states); + default_attrs[gpe_count] = &_attr_stats.attr; + gpe_stats_attr_group.attrs = default_attrs; + ret = sysfs_create_group(&acpi_subsys.kset.kobj, &gpe_stats_attr_group); + return ret; +} + +static void __exit gpe_stats_exit(void) +{ + struct attribute **p; + sysfs_remove_group(&acpi_subsys.kset.kobj, &gpe_stats_attr_group); + + p = default_attrs; + while (*p) { + if (*p != &_attr_stats.attr) + kfree((*p++)->name); + else + p++; + } + kfree(default_attrs); + kfree(gpe_attrs); + return; +} + +MODULE_AUTHOR("Yu Luming <luming.yu@xxxxxxxxx>"); +MODULE_DESCRIPTION("gpe_stats"); +MODULE_LICENSE("GPL"); + +module_init(gpe_stats_init); +module_exit(gpe_stats_exit); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 971eca4..ab04591 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1019,6 +1019,152 @@ void acpi_os_release_lock(acpi_spinlock spin_unlock_irqrestore(lockp, flags); } +typedef ssize_t(*show_func) (struct attribute * attr, char *buf); +int create_gpe_attr_array(struct module *module, + struct attribute ***attrs, + struct gpe_attr **gpe_attrs, + struct gpe_attr *attr, show_func * func) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_register_info *gpe_register; + acpi_cpu_flags flags; + int i, j, gpe_count, count; + + gpe_count = 0; + 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) { + gpe_count += gpe_block->register_count * + ACPI_GPE_REGISTER_WIDTH; + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + *attrs = + kzalloc(sizeof(struct attribute *) * (gpe_count + 2), GFP_KERNEL); + *gpe_attrs = + kzalloc(sizeof(struct gpe_attr) * (gpe_count + 1), GFP_KERNEL); + count = 0; + 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) { + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register = &gpe_block->register_info[i]; + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + char *name; + int base = + gpe_register->base_gpe_number; + int gpe; + gpe = base + j; + name = kzalloc(10, GFP_KERNEL); + sprintf(name, "%x", gpe); + memcpy(&(*gpe_attrs)[count], + attr, sizeof(struct gpe_attr)); + (*gpe_attrs)[count].attr.name = name; + (*gpe_attrs)[count].value = gpe; + (*attrs)[count] = (struct attribute *) + &(*gpe_attrs)[count].attr; + count++; + } + } + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + + return gpe_count; +} + +EXPORT_SYMBOL(create_gpe_attr_array); + +int acpi_show_gpe_count(char *buf) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_register_info *gpe_register_info; + acpi_cpu_flags flags; + u32 i, j, count; + + count = 0; + 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) { + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = + &gpe_block->register_info[i]; + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) + count += sprintf(buf + count, + "GPE[%d]: %d\n", + gpe_register_info-> + base_gpe_number + j, + gpe_block-> + event_info[(i * + ACPI_GPE_REGISTER_WIDTH) + + j].count); + } + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return count; +} + +EXPORT_SYMBOL(acpi_show_gpe_count); + +int acpi_show_one_gpe_count(int gpe_number, char *buf) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_register_info *gpe_register_info; + acpi_cpu_flags flags; + u32 i, j, count; + + count = 0; + 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) { + for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = + &gpe_block->register_info[i]; + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) + if (gpe_number == + (gpe_register_info-> + base_gpe_number + j)) { + count += + sprintf(buf + count, + "GPE[%d]: %d\n", + gpe_register_info-> + base_gpe_number + j, + gpe_block-> + event_info[(i * + ACPI_GPE_REGISTER_WIDTH) + + + j]. + count); + acpi_os_release_lock + (acpi_gbl_gpe_lock, flags); + return count; + } + } + gpe_block = gpe_block->next; + } + gpe_xrupt_info = gpe_xrupt_info->next; + } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return count; +} + +EXPORT_SYMBOL(acpi_show_one_gpe_count); #ifndef ACPI_USE_LOCAL_CACHE /******************************************************************************* diff --git a/include/acpi/aclocal.h b/include/acpi/aclocal.h index 6f83ddb..380a8a8 100644 --- a/include/acpi/aclocal.h +++ b/include/acpi/aclocal.h @@ -368,6 +368,7 @@ struct acpi_gpe_event_info { struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ u8 flags; /* Misc info about this GPE */ u8 gpe_number; /* This GPE */ + u32 count; }; /* Information about a GPE register pair, one per each status/enable pair in an array */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8bcfaa4..35fab01 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -158,6 +158,13 @@ struct acpi_prt_list { struct list_head entries; }; +struct gpe_attr { + struct attribute attr; + int type; + int value; + ssize_t(*show) (struct attribute *, char *); + ssize_t(*store) (struct attribute *, const char *, size_t count); +}; struct pci_dev; int acpi_pci_irq_enable (struct pci_dev *dev);
Attachment:
gpe_count.patch
Description: Binary data