Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.28-rc2 645 lines 16 kB view raw
1/* 2 * acpi_system.c - ACPI System Driver ($Revision: 63 $) 3 * 4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 6 * 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 22 * 23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 */ 25 26#include <linux/proc_fs.h> 27#include <linux/seq_file.h> 28#include <linux/init.h> 29#include <linux/string.h> 30#include <asm/uaccess.h> 31 32#include <acpi/acpi_drivers.h> 33 34#define _COMPONENT ACPI_SYSTEM_COMPONENT 35ACPI_MODULE_NAME("system"); 36#ifdef MODULE_PARAM_PREFIX 37#undef MODULE_PARAM_PREFIX 38#endif 39#define MODULE_PARAM_PREFIX "acpi." 40 41#define ACPI_SYSTEM_CLASS "system" 42#define ACPI_SYSTEM_DEVICE_NAME "System" 43 44u32 acpi_irq_handled; 45 46/* 47 * Make ACPICA version work as module param 48 */ 49static int param_get_acpica_version(char *buffer, struct kernel_param *kp) 50{ 51 int result; 52 53 result = sprintf(buffer, "%x", ACPI_CA_VERSION); 54 55 return result; 56} 57 58module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); 59 60/* -------------------------------------------------------------------------- 61 FS Interface (/sys) 62 -------------------------------------------------------------------------- */ 63static LIST_HEAD(acpi_table_attr_list); 64static struct kobject *tables_kobj; 65 66struct acpi_table_attr { 67 struct bin_attribute attr; 68 char name[8]; 69 int instance; 70 struct list_head node; 71}; 72 73static ssize_t acpi_table_show(struct kobject *kobj, 74 struct bin_attribute *bin_attr, char *buf, 75 loff_t offset, size_t count) 76{ 77 struct acpi_table_attr *table_attr = 78 container_of(bin_attr, struct acpi_table_attr, attr); 79 struct acpi_table_header *table_header = NULL; 80 acpi_status status; 81 82 status = 83 acpi_get_table(table_attr->name, table_attr->instance, 84 &table_header); 85 if (ACPI_FAILURE(status)) 86 return -ENODEV; 87 88 return memory_read_from_buffer(buf, count, &offset, 89 table_header, table_header->length); 90} 91 92static void acpi_table_attr_init(struct acpi_table_attr *table_attr, 93 struct acpi_table_header *table_header) 94{ 95 struct acpi_table_header *header = NULL; 96 struct acpi_table_attr *attr = NULL; 97 98 memcpy(table_attr->name, table_header->signature, ACPI_NAME_SIZE); 99 100 list_for_each_entry(attr, &acpi_table_attr_list, node) { 101 if (!memcmp(table_header->signature, attr->name, 102 ACPI_NAME_SIZE)) 103 if (table_attr->instance < attr->instance) 104 table_attr->instance = attr->instance; 105 } 106 table_attr->instance++; 107 108 if (table_attr->instance > 1 || (table_attr->instance == 1 && 109 !acpi_get_table(table_header-> 110 signature, 2, 111 &header))) 112 sprintf(table_attr->name + 4, "%d", table_attr->instance); 113 114 table_attr->attr.size = 0; 115 table_attr->attr.read = acpi_table_show; 116 table_attr->attr.attr.name = table_attr->name; 117 table_attr->attr.attr.mode = 0444; 118 119 return; 120} 121 122static int acpi_system_sysfs_init(void) 123{ 124 struct acpi_table_attr *table_attr; 125 struct acpi_table_header *table_header = NULL; 126 int table_index = 0; 127 int result; 128 129 tables_kobj = kobject_create_and_add("tables", acpi_kobj); 130 if (!tables_kobj) 131 return -ENOMEM; 132 133 do { 134 result = acpi_get_table_by_index(table_index, &table_header); 135 if (!result) { 136 table_index++; 137 table_attr = NULL; 138 table_attr = 139 kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); 140 if (!table_attr) 141 return -ENOMEM; 142 143 acpi_table_attr_init(table_attr, table_header); 144 result = 145 sysfs_create_bin_file(tables_kobj, 146 &table_attr->attr); 147 if (result) { 148 kfree(table_attr); 149 return result; 150 } else 151 list_add_tail(&table_attr->node, 152 &acpi_table_attr_list); 153 } 154 } while (!result); 155 kobject_uevent(tables_kobj, KOBJ_ADD); 156 157 return 0; 158} 159 160/* 161 * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ 162 * See Documentation/ABI/testing/sysfs-firmware-acpi 163 */ 164 165#define COUNT_GPE 0 166#define COUNT_SCI 1 /* acpi_irq_handled */ 167#define COUNT_ERROR 2 /* other */ 168#define NUM_COUNTERS_EXTRA 3 169 170#define ACPI_EVENT_VALID 0x01 171struct event_counter { 172 u32 count; 173 u32 flags; 174}; 175 176static struct event_counter *all_counters; 177static u32 num_gpes; 178static u32 num_counters; 179static struct attribute **all_attrs; 180static u32 acpi_gpe_count; 181 182static struct attribute_group interrupt_stats_attr_group = { 183 .name = "interrupts", 184}; 185static struct kobj_attribute *counter_attrs; 186 187static int count_num_gpes(void) 188{ 189 int count = 0; 190 struct acpi_gpe_xrupt_info *gpe_xrupt_info; 191 struct acpi_gpe_block_info *gpe_block; 192 acpi_cpu_flags flags; 193 194 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 195 196 gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; 197 while (gpe_xrupt_info) { 198 gpe_block = gpe_xrupt_info->gpe_block_list_head; 199 while (gpe_block) { 200 count += gpe_block->register_count * 201 ACPI_GPE_REGISTER_WIDTH; 202 gpe_block = gpe_block->next; 203 } 204 gpe_xrupt_info = gpe_xrupt_info->next; 205 } 206 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 207 208 return count; 209} 210 211static int get_gpe_device(int index, acpi_handle *handle) 212{ 213 struct acpi_gpe_xrupt_info *gpe_xrupt_info; 214 struct acpi_gpe_block_info *gpe_block; 215 acpi_cpu_flags flags; 216 struct acpi_namespace_node *node; 217 218 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 219 220 gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; 221 while (gpe_xrupt_info) { 222 gpe_block = gpe_xrupt_info->gpe_block_list_head; 223 node = gpe_block->node; 224 while (gpe_block) { 225 index -= gpe_block->register_count * 226 ACPI_GPE_REGISTER_WIDTH; 227 if (index < 0) { 228 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 229 /* return NULL if it's FADT GPE */ 230 if (node->type != ACPI_TYPE_DEVICE) 231 *handle = NULL; 232 else 233 *handle = node; 234 return 0; 235 } 236 node = gpe_block->node; 237 gpe_block = gpe_block->next; 238 } 239 gpe_xrupt_info = gpe_xrupt_info->next; 240 } 241 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 242 243 return -ENODEV; 244} 245 246static void delete_gpe_attr_array(void) 247{ 248 struct event_counter *tmp = all_counters; 249 250 all_counters = NULL; 251 kfree(tmp); 252 253 if (counter_attrs) { 254 int i; 255 256 for (i = 0; i < num_gpes; i++) 257 kfree(counter_attrs[i].attr.name); 258 259 kfree(counter_attrs); 260 } 261 kfree(all_attrs); 262 263 return; 264} 265 266void acpi_os_gpe_count(u32 gpe_number) 267{ 268 acpi_gpe_count++; 269 270 if (!all_counters) 271 return; 272 273 if (gpe_number < num_gpes) 274 all_counters[gpe_number].count++; 275 else 276 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 277 count++; 278 279 return; 280} 281 282void acpi_os_fixed_event_count(u32 event_number) 283{ 284 if (!all_counters) 285 return; 286 287 if (event_number < ACPI_NUM_FIXED_EVENTS) 288 all_counters[num_gpes + event_number].count++; 289 else 290 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 291 count++; 292 293 return; 294} 295 296static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) 297{ 298 int result = 0; 299 300 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 301 goto end; 302 303 if (index < num_gpes) { 304 result = get_gpe_device(index, handle); 305 if (result) { 306 ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, 307 "Invalid GPE 0x%x\n", index)); 308 goto end; 309 } 310 result = acpi_get_gpe_status(*handle, index, 311 ACPI_NOT_ISR, status); 312 } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) 313 result = acpi_get_event_status(index - num_gpes, status); 314 315 /* 316 * sleep/power button GPE/Fixed Event is enabled after acpi_system_init, 317 * check the status at runtime and mark it as valid once it's enabled 318 */ 319 if (!result && (*status & ACPI_EVENT_FLAG_ENABLED)) 320 all_counters[index].flags |= ACPI_EVENT_VALID; 321end: 322 return result; 323} 324 325static ssize_t counter_show(struct kobject *kobj, 326 struct kobj_attribute *attr, char *buf) 327{ 328 int index = attr - counter_attrs; 329 int size; 330 acpi_handle handle; 331 acpi_event_status status; 332 int result = 0; 333 334 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count = 335 acpi_irq_handled; 336 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = 337 acpi_gpe_count; 338 339 size = sprintf(buf, "%8d", all_counters[index].count); 340 341 /* "gpe_all" or "sci" */ 342 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 343 goto end; 344 345 result = get_status(index, &status, &handle); 346 if (result) 347 goto end; 348 349 if (!(all_counters[index].flags & ACPI_EVENT_VALID)) 350 size += sprintf(buf + size, " invalid"); 351 else if (status & ACPI_EVENT_FLAG_ENABLED) 352 size += sprintf(buf + size, " enable"); 353 else 354 size += sprintf(buf + size, " disable"); 355 356end: 357 size += sprintf(buf + size, "\n"); 358 return result ? result : size; 359} 360 361/* 362 * counter_set() sets the specified counter. 363 * setting the total "sci" file to any value clears all counters. 364 * enable/disable/clear a gpe/fixed event in user space. 365 */ 366static ssize_t counter_set(struct kobject *kobj, 367 struct kobj_attribute *attr, const char *buf, size_t size) 368{ 369 int index = attr - counter_attrs; 370 acpi_event_status status; 371 acpi_handle handle; 372 int result = 0; 373 374 if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { 375 int i; 376 for (i = 0; i < num_counters; ++i) 377 all_counters[i].count = 0; 378 acpi_gpe_count = 0; 379 acpi_irq_handled = 0; 380 goto end; 381 } 382 383 /* show the event status for both GPEs and Fixed Events */ 384 result = get_status(index, &status, &handle); 385 if (result) 386 goto end; 387 388 if (!(all_counters[index].flags & ACPI_EVENT_VALID)) { 389 printk(KERN_WARNING PREFIX 390 "Can not change Invalid GPE/Fixed Event status\n"); 391 return -EINVAL; 392 } 393 394 if (index < num_gpes) { 395 if (!strcmp(buf, "disable\n") && 396 (status & ACPI_EVENT_FLAG_ENABLED)) 397 result = acpi_disable_gpe(handle, index, ACPI_NOT_ISR); 398 else if (!strcmp(buf, "enable\n") && 399 !(status & ACPI_EVENT_FLAG_ENABLED)) 400 result = acpi_enable_gpe(handle, index, ACPI_NOT_ISR); 401 else if (!strcmp(buf, "clear\n") && 402 (status & ACPI_EVENT_FLAG_SET)) 403 result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); 404 else 405 all_counters[index].count = strtoul(buf, NULL, 0); 406 } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { 407 int event = index - num_gpes; 408 if (!strcmp(buf, "disable\n") && 409 (status & ACPI_EVENT_FLAG_ENABLED)) 410 result = acpi_disable_event(event, ACPI_NOT_ISR); 411 else if (!strcmp(buf, "enable\n") && 412 !(status & ACPI_EVENT_FLAG_ENABLED)) 413 result = acpi_enable_event(event, ACPI_NOT_ISR); 414 else if (!strcmp(buf, "clear\n") && 415 (status & ACPI_EVENT_FLAG_SET)) 416 result = acpi_clear_event(event); 417 else 418 all_counters[index].count = strtoul(buf, NULL, 0); 419 } else 420 all_counters[index].count = strtoul(buf, NULL, 0); 421 422 if (ACPI_FAILURE(result)) 423 result = -EINVAL; 424end: 425 return result ? result : size; 426} 427 428void acpi_irq_stats_init(void) 429{ 430 int i; 431 432 if (all_counters) 433 return; 434 435 num_gpes = count_num_gpes(); 436 num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; 437 438 all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), 439 GFP_KERNEL); 440 if (all_attrs == NULL) 441 return; 442 443 all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), 444 GFP_KERNEL); 445 if (all_counters == NULL) 446 goto fail; 447 448 counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), 449 GFP_KERNEL); 450 if (counter_attrs == NULL) 451 goto fail; 452 453 for (i = 0; i < num_counters; ++i) { 454 char buffer[12]; 455 char *name; 456 457 if (i < num_gpes) 458 sprintf(buffer, "gpe%02X", i); 459 else if (i == num_gpes + ACPI_EVENT_PMTIMER) 460 sprintf(buffer, "ff_pmtimer"); 461 else if (i == num_gpes + ACPI_EVENT_GLOBAL) 462 sprintf(buffer, "ff_gbl_lock"); 463 else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) 464 sprintf(buffer, "ff_pwr_btn"); 465 else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) 466 sprintf(buffer, "ff_slp_btn"); 467 else if (i == num_gpes + ACPI_EVENT_RTC) 468 sprintf(buffer, "ff_rt_clk"); 469 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) 470 sprintf(buffer, "gpe_all"); 471 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) 472 sprintf(buffer, "sci"); 473 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) 474 sprintf(buffer, "error"); 475 else 476 sprintf(buffer, "bug%02X", i); 477 478 name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); 479 if (name == NULL) 480 goto fail; 481 strncpy(name, buffer, strlen(buffer) + 1); 482 483 counter_attrs[i].attr.name = name; 484 counter_attrs[i].attr.mode = 0644; 485 counter_attrs[i].show = counter_show; 486 counter_attrs[i].store = counter_set; 487 488 all_attrs[i] = &counter_attrs[i].attr; 489 } 490 491 interrupt_stats_attr_group.attrs = all_attrs; 492 if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group)) 493 return; 494 495fail: 496 delete_gpe_attr_array(); 497 return; 498} 499 500static void __exit interrupt_stats_exit(void) 501{ 502 sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); 503 504 delete_gpe_attr_array(); 505 506 return; 507} 508 509/* -------------------------------------------------------------------------- 510 FS Interface (/proc) 511 -------------------------------------------------------------------------- */ 512#ifdef CONFIG_ACPI_PROCFS 513#define ACPI_SYSTEM_FILE_INFO "info" 514#define ACPI_SYSTEM_FILE_EVENT "event" 515#define ACPI_SYSTEM_FILE_DSDT "dsdt" 516#define ACPI_SYSTEM_FILE_FADT "fadt" 517 518static int acpi_system_read_info(struct seq_file *seq, void *offset) 519{ 520 521 seq_printf(seq, "version: %x\n", ACPI_CA_VERSION); 522 return 0; 523} 524 525static int acpi_system_info_open_fs(struct inode *inode, struct file *file) 526{ 527 return single_open(file, acpi_system_read_info, PDE(inode)->data); 528} 529 530static const struct file_operations acpi_system_info_ops = { 531 .owner = THIS_MODULE, 532 .open = acpi_system_info_open_fs, 533 .read = seq_read, 534 .llseek = seq_lseek, 535 .release = single_release, 536}; 537 538static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, 539 loff_t *); 540 541static const struct file_operations acpi_system_dsdt_ops = { 542 .owner = THIS_MODULE, 543 .read = acpi_system_read_dsdt, 544}; 545 546static ssize_t 547acpi_system_read_dsdt(struct file *file, 548 char __user * buffer, size_t count, loff_t * ppos) 549{ 550 acpi_status status = AE_OK; 551 struct acpi_table_header *dsdt = NULL; 552 ssize_t res; 553 554 status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt); 555 if (ACPI_FAILURE(status)) 556 return -ENODEV; 557 558 res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length); 559 560 return res; 561} 562 563static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t, 564 loff_t *); 565 566static const struct file_operations acpi_system_fadt_ops = { 567 .owner = THIS_MODULE, 568 .read = acpi_system_read_fadt, 569}; 570 571static ssize_t 572acpi_system_read_fadt(struct file *file, 573 char __user * buffer, size_t count, loff_t * ppos) 574{ 575 acpi_status status = AE_OK; 576 struct acpi_table_header *fadt = NULL; 577 ssize_t res; 578 579 status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt); 580 if (ACPI_FAILURE(status)) 581 return -ENODEV; 582 583 res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length); 584 585 return res; 586} 587 588static int acpi_system_procfs_init(void) 589{ 590 struct proc_dir_entry *entry; 591 int error = 0; 592 593 /* 'info' [R] */ 594 entry = proc_create(ACPI_SYSTEM_FILE_INFO, S_IRUGO, acpi_root_dir, 595 &acpi_system_info_ops); 596 if (!entry) 597 goto Error; 598 599 /* 'dsdt' [R] */ 600 entry = proc_create(ACPI_SYSTEM_FILE_DSDT, S_IRUSR, acpi_root_dir, 601 &acpi_system_dsdt_ops); 602 if (!entry) 603 goto Error; 604 605 /* 'fadt' [R] */ 606 entry = proc_create(ACPI_SYSTEM_FILE_FADT, S_IRUSR, acpi_root_dir, 607 &acpi_system_fadt_ops); 608 if (!entry) 609 goto Error; 610 611 Done: 612 return error; 613 614 Error: 615 remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); 616 remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); 617 remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); 618 619 error = -EFAULT; 620 goto Done; 621} 622#else 623static int acpi_system_procfs_init(void) 624{ 625 return 0; 626} 627#endif 628 629static int __init acpi_system_init(void) 630{ 631 int result = 0; 632 633 if (acpi_disabled) 634 return 0; 635 636 result = acpi_system_procfs_init(); 637 if (result) 638 return result; 639 640 result = acpi_system_sysfs_init(); 641 642 return result; 643} 644 645subsys_initcall(acpi_system_init);