Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5e54bc8 authored by Len Brown's avatar Len Brown
Browse files

Merge branches 'release' and 'stats' into release

parents 70ec75c5 5229e87d
Loading
Loading
Loading
Loading
+99 −0
Original line number Original line Diff line number Diff line
What:		/sys/firmware/acpi/interrupts/
Date:		February 2008
Contact:	Len Brown <lenb@kernel.org>
Description:
		All ACPI interrupts are handled via a single IRQ,
		the System Control Interrupt (SCI), which appears
		as "acpi" in /proc/interrupts.

		However, one of the main functions of ACPI is to make
		the platform understand random hardware without
		special driver support.  So while the SCI handles a few
		well known (fixed feature) interrupts sources, such
		as the power button, it can also handle a variable
		number of a "General Purpose Events" (GPE).

		A GPE vectors to a specified handler in AML, which
		can do a anything the BIOS writer wants from
		OS context.  GPE 0x12, for example, would vector
		to a level or edge handler called _L12 or _E12.
		The handler may do its business and return.
		Or the handler may send send a Notify event
		to a Linux device driver registered on an ACPI device,
		such as a battery, or a processor.

		To figure out where all the SCI's are coming from,
		/sys/firmware/acpi/interrupts contains a file listing
		every possible source, and the count of how many
		times it has triggered.

		$ cd /sys/firmware/acpi/interrupts
		$ grep . *
		error:0
		ff_gbl_lock:0
		ff_pmtimer:0
		ff_pwr_btn:0
		ff_rt_clk:0
		ff_slp_btn:0
		gpe00:0
		gpe01:0
		gpe02:0
		gpe03:0
		gpe04:0
		gpe05:0
		gpe06:0
		gpe07:0
		gpe08:0
		gpe09:174
		gpe0A:0
		gpe0B:0
		gpe0C:0
		gpe0D:0
		gpe0E:0
		gpe0F:0
		gpe10:0
		gpe11:60
		gpe12:0
		gpe13:0
		gpe14:0
		gpe15:0
		gpe16:0
		gpe17:0
		gpe18:0
		gpe19:7
		gpe1A:0
		gpe1B:0
		gpe1C:0
		gpe1D:0
		gpe1E:0
		gpe1F:0
		gpe_all:241
		sci:241

		sci - The total number of times the ACPI SCI
		has claimed an interrupt.

		gpe_all - count of SCI caused by GPEs.

		gpeXX - count for individual GPE source

		ff_gbl_lock - Global Lock

		ff_pmtimer - PM Timer

		ff_pwr_btn - Power Button

		ff_rt_clk - Real Time Clock

		ff_slp_btn - Sleep Button

		error - an interrupt that can't be accounted for above.

		Root has permission to clear any of these counters.  Eg.
		# echo 0 > gpe11

		All counters can be cleared by clearing the total "sci":
		# echo 0 > sci

		None of these counters has an effect on the function
		of the system, they are simply statistics.
+1 −1
Original line number Original line Diff line number Diff line
@@ -259,7 +259,7 @@ u32 acpi_ev_fixed_event_detect(void)
			enable_bit_mask)) {
			enable_bit_mask)) {


			/* Found an active (signalled) event */
			/* Found an active (signalled) event */

			acpi_os_fixed_event_count(i);
			int_status |= acpi_ev_fixed_event_dispatch((u32) i);
			int_status |= acpi_ev_fixed_event_dispatch((u32) i);
		}
		}
	}
	}
+1 −1
Original line number Original line Diff line number Diff line
@@ -627,7 +627,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)


	ACPI_FUNCTION_TRACE(ev_gpe_dispatch);
	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
	 * If edge-triggered, clear the GPE status bit now.  Note that
+11 −1
Original line number Original line Diff line number Diff line
@@ -337,7 +337,15 @@ acpi_os_table_override(struct acpi_table_header * existing_table,


static irqreturn_t acpi_irq(int irq, void *dev_id)
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
acpi_status
@@ -346,6 +354,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
{
{
	unsigned int irq;
	unsigned int irq;


	acpi_irq_stats_init();

	/*
	/*
	 * Ignore the GSI from the core, and use the value in our copy of the
	 * 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
	 * FADT. It may not be the same if an interrupt source override exists
+208 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,8 @@ ACPI_MODULE_NAME("system");
#define ACPI_SYSTEM_CLASS		"system"
#define ACPI_SYSTEM_CLASS		"system"
#define ACPI_SYSTEM_DEVICE_NAME		"System"
#define ACPI_SYSTEM_DEVICE_NAME		"System"


u32 acpi_irq_handled;

/*
/*
 * Make ACPICA version work as module param
 * Make ACPICA version work as module param
 */
 */
@@ -166,6 +168,212 @@ static int acpi_system_sysfs_init(void)
	return 0;
	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)
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */
   -------------------------------------------------------------------------- */
Loading