diff options
author | Len Brown <len.brown@intel.com> | 2008-02-06 01:26:55 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-02-06 22:27:06 -0500 |
commit | 5229e87d59cef33539322948bd8e3b5a537f7c97 (patch) | |
tree | 711a5708474fc79dbe599fae35f3e50d0a037ca0 | |
parent | 21511abd0a248a3f225d3b611cfabb93124605a7 (diff) |
ACPI: create /sys/firmware/acpi/interrupts
See Documentation/ABI/testing/sysfs-firmware-acpi
Based-on-original-patch-by: Luming Yu <luming.yu@intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/ABI/testing/sysfs-firmware-acpi | 99 | ||||
-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 | ||||
-rw-r--r-- | include/acpi/acglobal.h | 4 | ||||
-rw-r--r-- | include/acpi/acpiosxf.h | 3 | ||||
-rw-r--r-- | include/linux/acpi.h | 2 |
9 files changed, 325 insertions, 9 deletions
diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi new file mode 100644 index 000000000000..9470ed9afcc0 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-acpi | |||
@@ -0,0 +1,99 @@ | |||
1 | What: /sys/firmware/acpi/interrupts/ | ||
2 | Date: February 2008 | ||
3 | Contact: Len Brown <lenb@kernel.org> | ||
4 | Description: | ||
5 | All ACPI interrupts are handled via a single IRQ, | ||
6 | the System Control Interrupt (SCI), which appears | ||
7 | as "acpi" in /proc/interrupts. | ||
8 | |||
9 | However, one of the main functions of ACPI is to make | ||
10 | the platform understand random hardware without | ||
11 | special driver support. So while the SCI handles a few | ||
12 | well known (fixed feature) interrupts sources, such | ||
13 | as the power button, it can also handle a variable | ||
14 | number of a "General Purpose Events" (GPE). | ||
15 | |||
16 | A GPE vectors to a specified handler in AML, which | ||
17 | can do a anything the BIOS writer wants from | ||
18 | OS context. GPE 0x12, for example, would vector | ||
19 | to a level or edge handler called _L12 or _E12. | ||
20 | The handler may do its business and return. | ||
21 | Or the handler may send send a Notify event | ||
22 | to a Linux device driver registered on an ACPI device, | ||
23 | such as a battery, or a processor. | ||
24 | |||
25 | To figure out where all the SCI's are coming from, | ||
26 | /sys/firmware/acpi/interrupts contains a file listing | ||
27 | every possible source, and the count of how many | ||
28 | times it has triggered. | ||
29 | |||
30 | $ cd /sys/firmware/acpi/interrupts | ||
31 | $ grep . * | ||
32 | error:0 | ||
33 | ff_gbl_lock:0 | ||
34 | ff_pmtimer:0 | ||
35 | ff_pwr_btn:0 | ||
36 | ff_rt_clk:0 | ||
37 | ff_slp_btn:0 | ||
38 | gpe00:0 | ||
39 | gpe01:0 | ||
40 | gpe02:0 | ||
41 | gpe03:0 | ||
42 | gpe04:0 | ||
43 | gpe05:0 | ||
44 | gpe06:0 | ||
45 | gpe07:0 | ||
46 | gpe08:0 | ||
47 | gpe09:174 | ||
48 | gpe0A:0 | ||
49 | gpe0B:0 | ||
50 | gpe0C:0 | ||
51 | gpe0D:0 | ||
52 | gpe0E:0 | ||
53 | gpe0F:0 | ||
54 | gpe10:0 | ||
55 | gpe11:60 | ||
56 | gpe12:0 | ||
57 | gpe13:0 | ||
58 | gpe14:0 | ||
59 | gpe15:0 | ||
60 | gpe16:0 | ||
61 | gpe17:0 | ||
62 | gpe18:0 | ||
63 | gpe19:7 | ||
64 | gpe1A:0 | ||
65 | gpe1B:0 | ||
66 | gpe1C:0 | ||
67 | gpe1D:0 | ||
68 | gpe1E:0 | ||
69 | gpe1F:0 | ||
70 | gpe_all:241 | ||
71 | sci:241 | ||
72 | |||
73 | sci - The total number of times the ACPI SCI | ||
74 | has claimed an interrupt. | ||
75 | |||
76 | gpe_all - count of SCI caused by GPEs. | ||
77 | |||
78 | gpeXX - count for individual GPE source | ||
79 | |||
80 | ff_gbl_lock - Global Lock | ||
81 | |||
82 | ff_pmtimer - PM Timer | ||
83 | |||
84 | ff_pwr_btn - Power Button | ||
85 | |||
86 | ff_rt_clk - Real Time Clock | ||
87 | |||
88 | ff_slp_btn - Sleep Button | ||
89 | |||
90 | error - an interrupt that can't be accounted for above. | ||
91 | |||
92 | Root has permission to clear any of these counters. Eg. | ||
93 | # echo 0 > gpe11 | ||
94 | |||
95 | All counters can be cleared by clearing the total "sci": | ||
96 | # echo 0 > sci | ||
97 | |||
98 | None of these counters has an effect on the function | ||
99 | of the system, they are simply statistics. | ||
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) | |||
259 | enable_bit_mask)) { | 259 | enable_bit_mask)) { |
260 | 260 | ||
261 | /* Found an active (signalled) event */ | 261 | /* Found an active (signalled) event */ |
262 | 262 | acpi_os_fixed_event_count(i); | |
263 | int_status |= acpi_ev_fixed_event_dispatch((u32) i); | 263 | int_status |= acpi_ev_fixed_event_dispatch((u32) i); |
264 | } | 264 | } |
265 | } | 265 | } |
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) | |||
618 | 618 | ||
619 | ACPI_FUNCTION_TRACE(ev_gpe_dispatch); | 619 | ACPI_FUNCTION_TRACE(ev_gpe_dispatch); |
620 | 620 | ||
621 | acpi_gpe_count++; | 621 | acpi_os_gpe_count(gpe_number); |
622 | 622 | ||
623 | /* | 623 | /* |
624 | * If edge-triggered, clear the GPE status bit now. Note that | 624 | * 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, | |||
332 | 332 | ||
333 | static irqreturn_t acpi_irq(int irq, void *dev_id) | 333 | static irqreturn_t acpi_irq(int irq, void *dev_id) |
334 | { | 334 | { |
335 | return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; | 335 | u32 handled; |
336 | |||
337 | handled = (*acpi_irq_handler) (acpi_irq_context); | ||
338 | |||
339 | if (handled) { | ||
340 | acpi_irq_handled++; | ||
341 | return IRQ_HANDLED; | ||
342 | } else | ||
343 | return IRQ_NONE; | ||
336 | } | 344 | } |
337 | 345 | ||
338 | acpi_status | 346 | acpi_status |
@@ -341,6 +349,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, | |||
341 | { | 349 | { |
342 | unsigned int irq; | 350 | unsigned int irq; |
343 | 351 | ||
352 | acpi_irq_stats_init(); | ||
353 | |||
344 | /* | 354 | /* |
345 | * Ignore the GSI from the core, and use the value in our copy of the | 355 | * Ignore the GSI from the core, and use the value in our copy of the |
346 | * FADT. It may not be the same if an interrupt source override exists | 356 | * 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"); | |||
40 | #define ACPI_SYSTEM_CLASS "system" | 40 | #define ACPI_SYSTEM_CLASS "system" |
41 | #define ACPI_SYSTEM_DEVICE_NAME "System" | 41 | #define ACPI_SYSTEM_DEVICE_NAME "System" |
42 | 42 | ||
43 | u32 acpi_irq_handled; | ||
44 | |||
43 | /* | 45 | /* |
44 | * Make ACPICA version work as module param | 46 | * Make ACPICA version work as module param |
45 | */ | 47 | */ |
@@ -166,6 +168,212 @@ static int acpi_system_sysfs_init(void) | |||
166 | return 0; | 168 | return 0; |
167 | } | 169 | } |
168 | 170 | ||
171 | /* | ||
172 | * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ | ||
173 | * See Documentation/ABI/testing/sysfs-firmware-acpi | ||
174 | */ | ||
175 | |||
176 | #define COUNT_GPE 0 | ||
177 | #define COUNT_SCI 1 /* acpi_irq_handled */ | ||
178 | #define COUNT_ERROR 2 /* other */ | ||
179 | #define NUM_COUNTERS_EXTRA 3 | ||
180 | |||
181 | static u32 *all_counters; | ||
182 | static u32 num_gpes; | ||
183 | static u32 num_counters; | ||
184 | static struct attribute **all_attrs; | ||
185 | static u32 acpi_gpe_count; | ||
186 | |||
187 | static struct attribute_group interrupt_stats_attr_group = { | ||
188 | .name = "interrupts", | ||
189 | }; | ||
190 | static struct kobj_attribute *counter_attrs; | ||
191 | |||
192 | static int count_num_gpes(void) | ||
193 | { | ||
194 | int count = 0; | ||
195 | struct acpi_gpe_xrupt_info *gpe_xrupt_info; | ||
196 | struct acpi_gpe_block_info *gpe_block; | ||
197 | acpi_cpu_flags flags; | ||
198 | |||
199 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); | ||
200 | |||
201 | gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; | ||
202 | while (gpe_xrupt_info) { | ||
203 | gpe_block = gpe_xrupt_info->gpe_block_list_head; | ||
204 | while (gpe_block) { | ||
205 | count += gpe_block->register_count * | ||
206 | ACPI_GPE_REGISTER_WIDTH; | ||
207 | gpe_block = gpe_block->next; | ||
208 | } | ||
209 | gpe_xrupt_info = gpe_xrupt_info->next; | ||
210 | } | ||
211 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | ||
212 | |||
213 | return count; | ||
214 | } | ||
215 | |||
216 | static void delete_gpe_attr_array(void) | ||
217 | { | ||
218 | u32 *tmp = all_counters; | ||
219 | |||
220 | all_counters = NULL; | ||
221 | kfree(tmp); | ||
222 | |||
223 | if (counter_attrs) { | ||
224 | int i; | ||
225 | |||
226 | for (i = 0; i < num_gpes; i++) | ||
227 | kfree(counter_attrs[i].attr.name); | ||
228 | |||
229 | kfree(counter_attrs); | ||
230 | } | ||
231 | kfree(all_attrs); | ||
232 | |||
233 | return; | ||
234 | } | ||
235 | |||
236 | void acpi_os_gpe_count(u32 gpe_number) | ||
237 | { | ||
238 | acpi_gpe_count++; | ||
239 | |||
240 | if (!all_counters) | ||
241 | return; | ||
242 | |||
243 | if (gpe_number < num_gpes) | ||
244 | all_counters[gpe_number]++; | ||
245 | else | ||
246 | all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; | ||
247 | |||
248 | return; | ||
249 | } | ||
250 | |||
251 | void acpi_os_fixed_event_count(u32 event_number) | ||
252 | { | ||
253 | if (!all_counters) | ||
254 | return; | ||
255 | |||
256 | if (event_number < ACPI_NUM_FIXED_EVENTS) | ||
257 | all_counters[num_gpes + event_number]++; | ||
258 | else | ||
259 | all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]++; | ||
260 | |||
261 | return; | ||
262 | } | ||
263 | |||
264 | static ssize_t counter_show(struct kobject *kobj, | ||
265 | struct kobj_attribute *attr, char *buf) | ||
266 | { | ||
267 | all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI] = | ||
268 | acpi_irq_handled; | ||
269 | all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE] = | ||
270 | acpi_gpe_count; | ||
271 | |||
272 | return sprintf(buf, "%d\n", all_counters[attr - counter_attrs]); | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * counter_set() sets the specified counter. | ||
277 | * setting the total "sci" file to any value clears all counters. | ||
278 | */ | ||
279 | static ssize_t counter_set(struct kobject *kobj, | ||
280 | struct kobj_attribute *attr, const char *buf, size_t size) | ||
281 | { | ||
282 | int index = attr - counter_attrs; | ||
283 | |||
284 | if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { | ||
285 | int i; | ||
286 | for (i = 0; i < num_counters; ++i) | ||
287 | all_counters[i] = 0; | ||
288 | acpi_gpe_count = 0; | ||
289 | acpi_irq_handled = 0; | ||
290 | |||
291 | } else | ||
292 | all_counters[index] = strtoul(buf, NULL, 0); | ||
293 | |||
294 | return size; | ||
295 | } | ||
296 | |||
297 | void acpi_irq_stats_init(void) | ||
298 | { | ||
299 | int i; | ||
300 | |||
301 | if (all_counters) | ||
302 | return; | ||
303 | |||
304 | num_gpes = count_num_gpes(); | ||
305 | num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; | ||
306 | |||
307 | all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), | ||
308 | GFP_KERNEL); | ||
309 | if (all_attrs == NULL) | ||
310 | return; | ||
311 | |||
312 | all_counters = kzalloc(sizeof(u32) * (num_counters), GFP_KERNEL); | ||
313 | if (all_counters == NULL) | ||
314 | goto fail; | ||
315 | |||
316 | counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), | ||
317 | GFP_KERNEL); | ||
318 | if (counter_attrs == NULL) | ||
319 | goto fail; | ||
320 | |||
321 | for (i = 0; i < num_counters; ++i) { | ||
322 | char buffer[10]; | ||
323 | char *name; | ||
324 | |||
325 | if (i < num_gpes) | ||
326 | sprintf(buffer, "gpe%02X", i); | ||
327 | else if (i == num_gpes + ACPI_EVENT_PMTIMER) | ||
328 | sprintf(buffer, "ff_pmtimer"); | ||
329 | else if (i == num_gpes + ACPI_EVENT_GLOBAL) | ||
330 | sprintf(buffer, "ff_gbl_lock"); | ||
331 | else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) | ||
332 | sprintf(buffer, "ff_pwr_btn"); | ||
333 | else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) | ||
334 | sprintf(buffer, "ff_slp_btn"); | ||
335 | else if (i == num_gpes + ACPI_EVENT_RTC) | ||
336 | sprintf(buffer, "ff_rt_clk"); | ||
337 | else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) | ||
338 | sprintf(buffer, "gpe_all"); | ||
339 | else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) | ||
340 | sprintf(buffer, "sci"); | ||
341 | else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) | ||
342 | sprintf(buffer, "error"); | ||
343 | else | ||
344 | sprintf(buffer, "bug%02X", i); | ||
345 | |||
346 | name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); | ||
347 | if (name == NULL) | ||
348 | goto fail; | ||
349 | strncpy(name, buffer, strlen(buffer) + 1); | ||
350 | |||
351 | counter_attrs[i].attr.name = name; | ||
352 | counter_attrs[i].attr.mode = 0644; | ||
353 | counter_attrs[i].show = counter_show; | ||
354 | counter_attrs[i].store = counter_set; | ||
355 | |||
356 | all_attrs[i] = &counter_attrs[i].attr; | ||
357 | } | ||
358 | |||
359 | interrupt_stats_attr_group.attrs = all_attrs; | ||
360 | sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group); | ||
361 | return; | ||
362 | |||
363 | fail: | ||
364 | delete_gpe_attr_array(); | ||
365 | return; | ||
366 | } | ||
367 | |||
368 | static void __exit interrupt_stats_exit(void) | ||
369 | { | ||
370 | sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); | ||
371 | |||
372 | delete_gpe_attr_array(); | ||
373 | |||
374 | return; | ||
375 | } | ||
376 | |||
169 | /* -------------------------------------------------------------------------- | 377 | /* -------------------------------------------------------------------------- |
170 | FS Interface (/proc) | 378 | FS Interface (/proc) |
171 | -------------------------------------------------------------------------- */ | 379 | -------------------------------------------------------------------------- */ |
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) | |||
671 | 671 | ||
672 | /* GPE support */ | 672 | /* GPE support */ |
673 | 673 | ||
674 | acpi_gpe_count = 0; | ||
675 | acpi_gbl_gpe_xrupt_list_head = NULL; | 674 | acpi_gbl_gpe_xrupt_list_head = NULL; |
676 | acpi_gbl_gpe_fadt_blocks[0] = NULL; | 675 | acpi_gbl_gpe_fadt_blocks[0] = NULL; |
677 | acpi_gbl_gpe_fadt_blocks[1] = NULL; | 676 | acpi_gbl_gpe_fadt_blocks[1] = NULL; |
@@ -735,4 +734,3 @@ void acpi_ut_init_globals(void) | |||
735 | 734 | ||
736 | ACPI_EXPORT_SYMBOL(acpi_dbg_level) | 735 | ACPI_EXPORT_SYMBOL(acpi_dbg_level) |
737 | ACPI_EXPORT_SYMBOL(acpi_dbg_layer) | 736 | ACPI_EXPORT_SYMBOL(acpi_dbg_layer) |
738 | ACPI_EXPORT_SYMBOL(acpi_gpe_count) | ||
diff --git a/include/acpi/acglobal.h b/include/acpi/acglobal.h index 347a911d8237..47a1fd8f2d8a 100644 --- a/include/acpi/acglobal.h +++ b/include/acpi/acglobal.h | |||
@@ -117,10 +117,6 @@ extern u32 acpi_dbg_layer; | |||
117 | 117 | ||
118 | extern u32 acpi_gbl_nesting_level; | 118 | extern u32 acpi_gbl_nesting_level; |
119 | 119 | ||
120 | /* Event counters */ | ||
121 | |||
122 | ACPI_EXTERN u32 acpi_gpe_count; | ||
123 | |||
124 | /* Support for dynamic control method tracing mechanism */ | 120 | /* Support for dynamic control method tracing mechanism */ |
125 | 121 | ||
126 | ACPI_EXTERN u32 acpi_gbl_original_dbg_level; | 122 | ACPI_EXTERN u32 acpi_gbl_original_dbg_level; |
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index ca882b8e7d10..1a16cfbe9e0d 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h | |||
@@ -181,6 +181,9 @@ acpi_os_install_interrupt_handler(u32 gsi, | |||
181 | acpi_status | 181 | acpi_status |
182 | acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler service_routine); | 182 | acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler service_routine); |
183 | 183 | ||
184 | void acpi_os_gpe_count(u32 gpe_number); | ||
185 | void acpi_os_fixed_event_count(u32 fixed_event_number); | ||
186 | |||
184 | /* | 187 | /* |
185 | * Threads and Scheduling | 188 | * Threads and Scheduling |
186 | */ | 189 | */ |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 63f2e6ed698f..cb911f3e40f5 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -115,7 +115,9 @@ int acpi_unmap_lsapic(int cpu); | |||
115 | 115 | ||
116 | int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base); | 116 | int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base); |
117 | int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base); | 117 | int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base); |
118 | void acpi_irq_stats_init(void); | ||
118 | 119 | ||
120 | extern u32 acpi_irq_handled; | ||
119 | extern int acpi_mp_config; | 121 | extern int acpi_mp_config; |
120 | 122 | ||
121 | extern struct acpi_mcfg_allocation *pci_mmcfg_config; | 123 | extern struct acpi_mcfg_allocation *pci_mmcfg_config; |