···11+What: /sys/firmware/acpi/interrupts/22+Date: February 200833+Contact: Len Brown <lenb@kernel.org>44+Description:55+ All ACPI interrupts are handled via a single IRQ,66+ the System Control Interrupt (SCI), which appears77+ as "acpi" in /proc/interrupts.88+99+ However, one of the main functions of ACPI is to make1010+ the platform understand random hardware without1111+ special driver support. So while the SCI handles a few1212+ well known (fixed feature) interrupts sources, such1313+ as the power button, it can also handle a variable1414+ number of a "General Purpose Events" (GPE).1515+1616+ A GPE vectors to a specified handler in AML, which1717+ can do a anything the BIOS writer wants from1818+ OS context. GPE 0x12, for example, would vector1919+ to a level or edge handler called _L12 or _E12.2020+ The handler may do its business and return.2121+ Or the handler may send send a Notify event2222+ to a Linux device driver registered on an ACPI device,2323+ such as a battery, or a processor.2424+2525+ To figure out where all the SCI's are coming from,2626+ /sys/firmware/acpi/interrupts contains a file listing2727+ every possible source, and the count of how many2828+ times it has triggered.2929+3030+ $ cd /sys/firmware/acpi/interrupts3131+ $ grep . *3232+ error:03333+ ff_gbl_lock:03434+ ff_pmtimer:03535+ ff_pwr_btn:03636+ ff_rt_clk:03737+ ff_slp_btn:03838+ gpe00:03939+ gpe01:04040+ gpe02:04141+ gpe03:04242+ gpe04:04343+ gpe05:04444+ gpe06:04545+ gpe07:04646+ gpe08:04747+ gpe09:1744848+ gpe0A:04949+ gpe0B:05050+ gpe0C:05151+ gpe0D:05252+ gpe0E:05353+ gpe0F:05454+ gpe10:05555+ gpe11:605656+ gpe12:05757+ gpe13:05858+ gpe14:05959+ gpe15:06060+ gpe16:06161+ gpe17:06262+ gpe18:06363+ gpe19:76464+ gpe1A:06565+ gpe1B:06666+ gpe1C:06767+ gpe1D:06868+ gpe1E:06969+ gpe1F:07070+ gpe_all:2417171+ sci:2417272+7373+ sci - The total number of times the ACPI SCI7474+ has claimed an interrupt.7575+7676+ gpe_all - count of SCI caused by GPEs.7777+7878+ gpeXX - count for individual GPE source7979+8080+ ff_gbl_lock - Global Lock8181+8282+ ff_pmtimer - PM Timer8383+8484+ ff_pwr_btn - Power Button8585+8686+ ff_rt_clk - Real Time Clock8787+8888+ ff_slp_btn - Sleep Button8989+9090+ error - an interrupt that can't be accounted for above.9191+9292+ Root has permission to clear any of these counters. Eg.9393+ # echo 0 > gpe119494+9595+ All counters can be cleared by clearing the total "sci":9696+ # echo 0 > sci9797+9898+ None of these counters has an effect on the function9999+ of the system, they are simply statistics.
+1-1
drivers/acpi/events/evevent.c
···259259 enable_bit_mask)) {260260261261 /* Found an active (signalled) event */262262-262262+ acpi_os_fixed_event_count(i);263263 int_status |= acpi_ev_fixed_event_dispatch((u32) i);264264 }265265 }
+1-1
drivers/acpi/events/evgpe.c
···627627628628 ACPI_FUNCTION_TRACE(ev_gpe_dispatch);629629630630- acpi_gpe_count++;630630+ acpi_os_gpe_count(gpe_number);631631632632 /*633633 * If edge-triggered, clear the GPE status bit now. Note that
+11-1
drivers/acpi/osl.c
···337337338338static irqreturn_t acpi_irq(int irq, void *dev_id)339339{340340- return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE;340340+ u32 handled;341341+342342+ handled = (*acpi_irq_handler) (acpi_irq_context);343343+344344+ if (handled) {345345+ acpi_irq_handled++;346346+ return IRQ_HANDLED;347347+ } else348348+ return IRQ_NONE;341349}342350343351acpi_status···353345 void *context)354346{355347 unsigned int irq;348348+349349+ acpi_irq_stats_init();356350357351 /*358352 * Ignore the GSI from the core, and use the value in our copy of the
+208
drivers/acpi/system.c
···4040#define ACPI_SYSTEM_CLASS "system"4141#define ACPI_SYSTEM_DEVICE_NAME "System"42424343+u32 acpi_irq_handled;4444+4345/*4446 * Make ACPICA version work as module param4547 */···166164 kobject_uevent(tables_kobj, KOBJ_ADD);167165168166 return 0;167167+}168168+169169+/*170170+ * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/171171+ * See Documentation/ABI/testing/sysfs-firmware-acpi172172+ */173173+174174+#define COUNT_GPE 0175175+#define COUNT_SCI 1 /* acpi_irq_handled */176176+#define COUNT_ERROR 2 /* other */177177+#define NUM_COUNTERS_EXTRA 3178178+179179+static u32 *all_counters;180180+static u32 num_gpes;181181+static u32 num_counters;182182+static struct attribute **all_attrs;183183+static u32 acpi_gpe_count;184184+185185+static struct attribute_group interrupt_stats_attr_group = {186186+ .name = "interrupts",187187+};188188+static struct kobj_attribute *counter_attrs;189189+190190+static int count_num_gpes(void)191191+{192192+ int count = 0;193193+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;194194+ struct acpi_gpe_block_info *gpe_block;195195+ acpi_cpu_flags flags;196196+197197+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);198198+199199+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;200200+ while (gpe_xrupt_info) {201201+ gpe_block = gpe_xrupt_info->gpe_block_list_head;202202+ while (gpe_block) {203203+ count += gpe_block->register_count *204204+ ACPI_GPE_REGISTER_WIDTH;205205+ gpe_block = gpe_block->next;206206+ }207207+ gpe_xrupt_info = gpe_xrupt_info->next;208208+ }209209+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);210210+211211+ return count;212212+}213213+214214+static void delete_gpe_attr_array(void)215215+{216216+ u32 *tmp = all_counters;217217+218218+ all_counters = NULL;219219+ kfree(tmp);220220+221221+ if (counter_attrs) {222222+ int i;223223+224224+ for (i = 0; i < num_gpes; i++)225225+ kfree(counter_attrs[i].attr.name);226226+227227+ kfree(counter_attrs);228228+ }229229+ kfree(all_attrs);230230+231231+ return;232232+}233233+234234+void acpi_os_gpe_count(u32 gpe_number)235235+{236236+ acpi_gpe_count++;237237+238238+ if (!all_counters)239239+ return;240240+241241+ if (gpe_number < num_gpes)242242+ all_counters[gpe_number]++;243243+ else244244+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++;245245+246246+ return;247247+}248248+249249+void acpi_os_fixed_event_count(u32 event_number)250250+{251251+ if (!all_counters)252252+ return;253253+254254+ if (event_number < ACPI_NUM_FIXED_EVENTS)255255+ all_counters[num_gpes + event_number]++;256256+ else257257+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++;258258+259259+ return;260260+}261261+262262+static ssize_t counter_show(struct kobject *kobj,263263+ struct kobj_attribute *attr, char *buf)264264+{265265+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI] =266266+ acpi_irq_handled;267267+ all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE] =268268+ acpi_gpe_count;269269+270270+ return sprintf(buf, "%d\n", all_counters[attr - counter_attrs]);271271+}272272+273273+/*274274+ * counter_set() sets the specified counter.275275+ * setting the total "sci" file to any value clears all counters.276276+ */277277+static ssize_t counter_set(struct kobject *kobj,278278+ struct kobj_attribute *attr, const char *buf, size_t size)279279+{280280+ int index = attr - counter_attrs;281281+282282+ if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {283283+ int i;284284+ for (i = 0; i < num_counters; ++i)285285+ all_counters[i] = 0;286286+ acpi_gpe_count = 0;287287+ acpi_irq_handled = 0;288288+289289+ } else290290+ all_counters[index] = strtoul(buf, NULL, 0);291291+292292+ return size;293293+}294294+295295+void acpi_irq_stats_init(void)296296+{297297+ int i;298298+299299+ if (all_counters)300300+ return;301301+302302+ num_gpes = count_num_gpes();303303+ num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;304304+305305+ all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1),306306+ GFP_KERNEL);307307+ if (all_attrs == NULL)308308+ return;309309+310310+ all_counters = kzalloc(sizeof(u32) * (num_counters), GFP_KERNEL);311311+ if (all_counters == NULL)312312+ goto fail;313313+314314+ counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters),315315+ GFP_KERNEL);316316+ if (counter_attrs == NULL)317317+ goto fail;318318+319319+ for (i = 0; i < num_counters; ++i) {320320+ char buffer[10];321321+ char *name;322322+323323+ if (i < num_gpes)324324+ sprintf(buffer, "gpe%02X", i);325325+ else if (i == num_gpes + ACPI_EVENT_PMTIMER)326326+ sprintf(buffer, "ff_pmtimer");327327+ else if (i == num_gpes + ACPI_EVENT_GLOBAL)328328+ sprintf(buffer, "ff_gbl_lock");329329+ else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)330330+ sprintf(buffer, "ff_pwr_btn");331331+ else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)332332+ sprintf(buffer, "ff_slp_btn");333333+ else if (i == num_gpes + ACPI_EVENT_RTC)334334+ sprintf(buffer, "ff_rt_clk");335335+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)336336+ sprintf(buffer, "gpe_all");337337+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)338338+ sprintf(buffer, "sci");339339+ else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)340340+ sprintf(buffer, "error");341341+ else342342+ sprintf(buffer, "bug%02X", i);343343+344344+ name = kzalloc(strlen(buffer) + 1, GFP_KERNEL);345345+ if (name == NULL)346346+ goto fail;347347+ strncpy(name, buffer, strlen(buffer) + 1);348348+349349+ counter_attrs[i].attr.name = name;350350+ counter_attrs[i].attr.mode = 0644;351351+ counter_attrs[i].show = counter_show;352352+ counter_attrs[i].store = counter_set;353353+354354+ all_attrs[i] = &counter_attrs[i].attr;355355+ }356356+357357+ interrupt_stats_attr_group.attrs = all_attrs;358358+ sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group);359359+ return;360360+361361+fail:362362+ delete_gpe_attr_array();363363+ return;364364+}365365+366366+static void __exit interrupt_stats_exit(void)367367+{368368+ sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);369369+370370+ delete_gpe_attr_array();371371+372372+ return;169373}170374171375/* --------------------------------------------------------------------------