at v2.6.27 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 table_attr->attr.attr.owner = THIS_MODULE; 119 120 return; 121} 122 123static int acpi_system_sysfs_init(void) 124{ 125 struct acpi_table_attr *table_attr; 126 struct acpi_table_header *table_header = NULL; 127 int table_index = 0; 128 int result; 129 130 tables_kobj = kobject_create_and_add("tables", acpi_kobj); 131 if (!tables_kobj) 132 return -ENOMEM; 133 134 do { 135 result = acpi_get_table_by_index(table_index, &table_header); 136 if (!result) { 137 table_index++; 138 table_attr = NULL; 139 table_attr = 140 kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); 141 if (!table_attr) 142 return -ENOMEM; 143 144 acpi_table_attr_init(table_attr, table_header); 145 result = 146 sysfs_create_bin_file(tables_kobj, 147 &table_attr->attr); 148 if (result) { 149 kfree(table_attr); 150 return result; 151 } else 152 list_add_tail(&table_attr->node, 153 &acpi_table_attr_list); 154 } 155 } while (!result); 156 kobject_uevent(tables_kobj, KOBJ_ADD); 157 158 return 0; 159} 160 161/* 162 * Detailed ACPI IRQ counters in /sys/firmware/acpi/interrupts/ 163 * See Documentation/ABI/testing/sysfs-firmware-acpi 164 */ 165 166#define COUNT_GPE 0 167#define COUNT_SCI 1 /* acpi_irq_handled */ 168#define COUNT_ERROR 2 /* other */ 169#define NUM_COUNTERS_EXTRA 3 170 171#define ACPI_EVENT_VALID 0x01 172struct event_counter { 173 u32 count; 174 u32 flags; 175}; 176 177static struct event_counter *all_counters; 178static u32 num_gpes; 179static u32 num_counters; 180static struct attribute **all_attrs; 181static u32 acpi_gpe_count; 182 183static struct attribute_group interrupt_stats_attr_group = { 184 .name = "interrupts", 185}; 186static struct kobj_attribute *counter_attrs; 187 188static int count_num_gpes(void) 189{ 190 int count = 0; 191 struct acpi_gpe_xrupt_info *gpe_xrupt_info; 192 struct acpi_gpe_block_info *gpe_block; 193 acpi_cpu_flags flags; 194 195 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 196 197 gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; 198 while (gpe_xrupt_info) { 199 gpe_block = gpe_xrupt_info->gpe_block_list_head; 200 while (gpe_block) { 201 count += gpe_block->register_count * 202 ACPI_GPE_REGISTER_WIDTH; 203 gpe_block = gpe_block->next; 204 } 205 gpe_xrupt_info = gpe_xrupt_info->next; 206 } 207 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 208 209 return count; 210} 211 212static int get_gpe_device(int index, acpi_handle *handle) 213{ 214 struct acpi_gpe_xrupt_info *gpe_xrupt_info; 215 struct acpi_gpe_block_info *gpe_block; 216 acpi_cpu_flags flags; 217 struct acpi_namespace_node *node; 218 219 flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); 220 221 gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; 222 while (gpe_xrupt_info) { 223 gpe_block = gpe_xrupt_info->gpe_block_list_head; 224 node = gpe_block->node; 225 while (gpe_block) { 226 index -= gpe_block->register_count * 227 ACPI_GPE_REGISTER_WIDTH; 228 if (index < 0) { 229 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 230 /* return NULL if it's FADT GPE */ 231 if (node->type != ACPI_TYPE_DEVICE) 232 *handle = NULL; 233 else 234 *handle = node; 235 return 0; 236 } 237 node = gpe_block->node; 238 gpe_block = gpe_block->next; 239 } 240 gpe_xrupt_info = gpe_xrupt_info->next; 241 } 242 acpi_os_release_lock(acpi_gbl_gpe_lock, flags); 243 244 return -ENODEV; 245} 246 247static void delete_gpe_attr_array(void) 248{ 249 struct event_counter *tmp = all_counters; 250 251 all_counters = NULL; 252 kfree(tmp); 253 254 if (counter_attrs) { 255 int i; 256 257 for (i = 0; i < num_gpes; i++) 258 kfree(counter_attrs[i].attr.name); 259 260 kfree(counter_attrs); 261 } 262 kfree(all_attrs); 263 264 return; 265} 266 267void acpi_os_gpe_count(u32 gpe_number) 268{ 269 acpi_gpe_count++; 270 271 if (!all_counters) 272 return; 273 274 if (gpe_number < num_gpes) 275 all_counters[gpe_number].count++; 276 else 277 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 278 count++; 279 280 return; 281} 282 283void acpi_os_fixed_event_count(u32 event_number) 284{ 285 if (!all_counters) 286 return; 287 288 if (event_number < ACPI_NUM_FIXED_EVENTS) 289 all_counters[num_gpes + event_number].count++; 290 else 291 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 292 count++; 293 294 return; 295} 296 297static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) 298{ 299 int result = 0; 300 301 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 302 goto end; 303 304 if (index < num_gpes) { 305 result = get_gpe_device(index, handle); 306 if (result) { 307 ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, 308 "Invalid GPE 0x%x\n", index)); 309 goto end; 310 } 311 result = acpi_get_gpe_status(*handle, index, 312 ACPI_NOT_ISR, status); 313 } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) 314 result = acpi_get_event_status(index - num_gpes, status); 315 316 /* 317 * sleep/power button GPE/Fixed Event is enabled after acpi_system_init, 318 * check the status at runtime and mark it as valid once it's enabled 319 */ 320 if (!result && (*status & ACPI_EVENT_FLAG_ENABLED)) 321 all_counters[index].flags |= ACPI_EVENT_VALID; 322end: 323 return result; 324} 325 326static ssize_t counter_show(struct kobject *kobj, 327 struct kobj_attribute *attr, char *buf) 328{ 329 int index = attr - counter_attrs; 330 int size; 331 acpi_handle handle; 332 acpi_event_status status; 333 int result = 0; 334 335 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count = 336 acpi_irq_handled; 337 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = 338 acpi_gpe_count; 339 340 size = sprintf(buf, "%8d", all_counters[index].count); 341 342 /* "gpe_all" or "sci" */ 343 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 344 goto end; 345 346 result = get_status(index, &status, &handle); 347 if (result) 348 goto end; 349 350 if (!(all_counters[index].flags & ACPI_EVENT_VALID)) 351 size += sprintf(buf + size, " invalid"); 352 else if (status & ACPI_EVENT_FLAG_ENABLED) 353 size += sprintf(buf + size, " enable"); 354 else 355 size += sprintf(buf + size, " disable"); 356 357end: 358 size += sprintf(buf + size, "\n"); 359 return result ? result : size; 360} 361 362/* 363 * counter_set() sets the specified counter. 364 * setting the total "sci" file to any value clears all counters. 365 * enable/disable/clear a gpe/fixed event in user space. 366 */ 367static ssize_t counter_set(struct kobject *kobj, 368 struct kobj_attribute *attr, const char *buf, size_t size) 369{ 370 int index = attr - counter_attrs; 371 acpi_event_status status; 372 acpi_handle handle; 373 int result = 0; 374 375 if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { 376 int i; 377 for (i = 0; i < num_counters; ++i) 378 all_counters[i].count = 0; 379 acpi_gpe_count = 0; 380 acpi_irq_handled = 0; 381 goto end; 382 } 383 384 /* show the event status for both GPEs and Fixed Events */ 385 result = get_status(index, &status, &handle); 386 if (result) 387 goto end; 388 389 if (!(all_counters[index].flags & ACPI_EVENT_VALID)) { 390 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 391 "Can not change Invalid GPE/Fixed Event status\n")); 392 return -EINVAL; 393 } 394 395 if (index < num_gpes) { 396 if (!strcmp(buf, "disable\n") && 397 (status & ACPI_EVENT_FLAG_ENABLED)) 398 result = acpi_disable_gpe(handle, index, ACPI_NOT_ISR); 399 else if (!strcmp(buf, "enable\n") && 400 !(status & ACPI_EVENT_FLAG_ENABLED)) 401 result = acpi_enable_gpe(handle, index, ACPI_NOT_ISR); 402 else if (!strcmp(buf, "clear\n") && 403 (status & ACPI_EVENT_FLAG_SET)) 404 result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); 405 else 406 all_counters[index].count = strtoul(buf, NULL, 0); 407 } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { 408 int event = index - num_gpes; 409 if (!strcmp(buf, "disable\n") && 410 (status & ACPI_EVENT_FLAG_ENABLED)) 411 result = acpi_disable_event(event, ACPI_NOT_ISR); 412 else if (!strcmp(buf, "enable\n") && 413 !(status & ACPI_EVENT_FLAG_ENABLED)) 414 result = acpi_enable_event(event, ACPI_NOT_ISR); 415 else if (!strcmp(buf, "clear\n") && 416 (status & ACPI_EVENT_FLAG_SET)) 417 result = acpi_clear_event(event); 418 else 419 all_counters[index].count = strtoul(buf, NULL, 0); 420 } else 421 all_counters[index].count = strtoul(buf, NULL, 0); 422 423 if (ACPI_FAILURE(result)) 424 result = -EINVAL; 425end: 426 return result ? result : size; 427} 428 429void acpi_irq_stats_init(void) 430{ 431 int i; 432 433 if (all_counters) 434 return; 435 436 num_gpes = count_num_gpes(); 437 num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; 438 439 all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), 440 GFP_KERNEL); 441 if (all_attrs == NULL) 442 return; 443 444 all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), 445 GFP_KERNEL); 446 if (all_counters == NULL) 447 goto fail; 448 449 counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), 450 GFP_KERNEL); 451 if (counter_attrs == NULL) 452 goto fail; 453 454 for (i = 0; i < num_counters; ++i) { 455 char buffer[12]; 456 char *name; 457 458 if (i < num_gpes) 459 sprintf(buffer, "gpe%02X", i); 460 else if (i == num_gpes + ACPI_EVENT_PMTIMER) 461 sprintf(buffer, "ff_pmtimer"); 462 else if (i == num_gpes + ACPI_EVENT_GLOBAL) 463 sprintf(buffer, "ff_gbl_lock"); 464 else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) 465 sprintf(buffer, "ff_pwr_btn"); 466 else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) 467 sprintf(buffer, "ff_slp_btn"); 468 else if (i == num_gpes + ACPI_EVENT_RTC) 469 sprintf(buffer, "ff_rt_clk"); 470 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) 471 sprintf(buffer, "gpe_all"); 472 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) 473 sprintf(buffer, "sci"); 474 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) 475 sprintf(buffer, "error"); 476 else 477 sprintf(buffer, "bug%02X", i); 478 479 name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); 480 if (name == NULL) 481 goto fail; 482 strncpy(name, buffer, strlen(buffer) + 1); 483 484 counter_attrs[i].attr.name = name; 485 counter_attrs[i].attr.mode = 0644; 486 counter_attrs[i].show = counter_show; 487 counter_attrs[i].store = counter_set; 488 489 all_attrs[i] = &counter_attrs[i].attr; 490 } 491 492 interrupt_stats_attr_group.attrs = all_attrs; 493 if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group)) 494 return; 495 496fail: 497 delete_gpe_attr_array(); 498 return; 499} 500 501static void __exit interrupt_stats_exit(void) 502{ 503 sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); 504 505 delete_gpe_attr_array(); 506 507 return; 508} 509 510/* -------------------------------------------------------------------------- 511 FS Interface (/proc) 512 -------------------------------------------------------------------------- */ 513#ifdef CONFIG_ACPI_PROCFS 514#define ACPI_SYSTEM_FILE_INFO "info" 515#define ACPI_SYSTEM_FILE_EVENT "event" 516#define ACPI_SYSTEM_FILE_DSDT "dsdt" 517#define ACPI_SYSTEM_FILE_FADT "fadt" 518 519static int acpi_system_read_info(struct seq_file *seq, void *offset) 520{ 521 522 seq_printf(seq, "version: %x\n", ACPI_CA_VERSION); 523 return 0; 524} 525 526static int acpi_system_info_open_fs(struct inode *inode, struct file *file) 527{ 528 return single_open(file, acpi_system_read_info, PDE(inode)->data); 529} 530 531static const struct file_operations acpi_system_info_ops = { 532 .owner = THIS_MODULE, 533 .open = acpi_system_info_open_fs, 534 .read = seq_read, 535 .llseek = seq_lseek, 536 .release = single_release, 537}; 538 539static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, 540 loff_t *); 541 542static const struct file_operations acpi_system_dsdt_ops = { 543 .owner = THIS_MODULE, 544 .read = acpi_system_read_dsdt, 545}; 546 547static ssize_t 548acpi_system_read_dsdt(struct file *file, 549 char __user * buffer, size_t count, loff_t * ppos) 550{ 551 acpi_status status = AE_OK; 552 struct acpi_table_header *dsdt = NULL; 553 ssize_t res; 554 555 status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt); 556 if (ACPI_FAILURE(status)) 557 return -ENODEV; 558 559 res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length); 560 561 return res; 562} 563 564static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t, 565 loff_t *); 566 567static const struct file_operations acpi_system_fadt_ops = { 568 .owner = THIS_MODULE, 569 .read = acpi_system_read_fadt, 570}; 571 572static ssize_t 573acpi_system_read_fadt(struct file *file, 574 char __user * buffer, size_t count, loff_t * ppos) 575{ 576 acpi_status status = AE_OK; 577 struct acpi_table_header *fadt = NULL; 578 ssize_t res; 579 580 status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt); 581 if (ACPI_FAILURE(status)) 582 return -ENODEV; 583 584 res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length); 585 586 return res; 587} 588 589static int acpi_system_procfs_init(void) 590{ 591 struct proc_dir_entry *entry; 592 int error = 0; 593 594 /* 'info' [R] */ 595 entry = proc_create(ACPI_SYSTEM_FILE_INFO, S_IRUGO, acpi_root_dir, 596 &acpi_system_info_ops); 597 if (!entry) 598 goto Error; 599 600 /* 'dsdt' [R] */ 601 entry = proc_create(ACPI_SYSTEM_FILE_DSDT, S_IRUSR, acpi_root_dir, 602 &acpi_system_dsdt_ops); 603 if (!entry) 604 goto Error; 605 606 /* 'fadt' [R] */ 607 entry = proc_create(ACPI_SYSTEM_FILE_FADT, S_IRUSR, acpi_root_dir, 608 &acpi_system_fadt_ops); 609 if (!entry) 610 goto Error; 611 612 Done: 613 return error; 614 615 Error: 616 remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); 617 remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); 618 remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); 619 620 error = -EFAULT; 621 goto Done; 622} 623#else 624static int acpi_system_procfs_init(void) 625{ 626 return 0; 627} 628#endif 629 630static int __init acpi_system_init(void) 631{ 632 int result = 0; 633 634 if (acpi_disabled) 635 return 0; 636 637 result = acpi_system_procfs_init(); 638 if (result) 639 return result; 640 641 result = acpi_system_sysfs_init(); 642 643 return result; 644} 645 646subsys_initcall(acpi_system_init);