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