at v2.6.29 15 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 void delete_gpe_attr_array(void) 196{ 197 struct event_counter *tmp = all_counters; 198 199 all_counters = NULL; 200 kfree(tmp); 201 202 if (counter_attrs) { 203 int i; 204 205 for (i = 0; i < num_gpes; i++) 206 kfree(counter_attrs[i].attr.name); 207 208 kfree(counter_attrs); 209 } 210 kfree(all_attrs); 211 212 return; 213} 214 215void acpi_os_gpe_count(u32 gpe_number) 216{ 217 acpi_gpe_count++; 218 219 if (!all_counters) 220 return; 221 222 if (gpe_number < num_gpes) 223 all_counters[gpe_number].count++; 224 else 225 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 226 count++; 227 228 return; 229} 230 231void acpi_os_fixed_event_count(u32 event_number) 232{ 233 if (!all_counters) 234 return; 235 236 if (event_number < ACPI_NUM_FIXED_EVENTS) 237 all_counters[num_gpes + event_number].count++; 238 else 239 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR]. 240 count++; 241 242 return; 243} 244 245static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) 246{ 247 int result = 0; 248 249 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 250 goto end; 251 252 if (index < num_gpes) { 253 result = acpi_get_gpe_device(index, handle); 254 if (result) { 255 ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, 256 "Invalid GPE 0x%x\n", index)); 257 goto end; 258 } 259 result = acpi_get_gpe_status(*handle, index, 260 ACPI_NOT_ISR, status); 261 } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) 262 result = acpi_get_event_status(index - num_gpes, status); 263 264end: 265 return result; 266} 267 268static ssize_t counter_show(struct kobject *kobj, 269 struct kobj_attribute *attr, char *buf) 270{ 271 int index = attr - counter_attrs; 272 int size; 273 acpi_handle handle; 274 acpi_event_status status; 275 int result = 0; 276 277 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count = 278 acpi_irq_handled; 279 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = 280 acpi_gpe_count; 281 282 size = sprintf(buf, "%8d", all_counters[index].count); 283 284 /* "gpe_all" or "sci" */ 285 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) 286 goto end; 287 288 result = get_status(index, &status, &handle); 289 if (result) 290 goto end; 291 292 if (!(status & ACPI_EVENT_FLAG_HANDLE)) 293 size += sprintf(buf + size, " invalid"); 294 else if (status & ACPI_EVENT_FLAG_ENABLED) 295 size += sprintf(buf + size, " enabled"); 296 else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) 297 size += sprintf(buf + size, " wake_enabled"); 298 else 299 size += sprintf(buf + size, " disabled"); 300 301end: 302 size += sprintf(buf + size, "\n"); 303 return result ? result : size; 304} 305 306/* 307 * counter_set() sets the specified counter. 308 * setting the total "sci" file to any value clears all counters. 309 * enable/disable/clear a gpe/fixed event in user space. 310 */ 311static ssize_t counter_set(struct kobject *kobj, 312 struct kobj_attribute *attr, const char *buf, size_t size) 313{ 314 int index = attr - counter_attrs; 315 acpi_event_status status; 316 acpi_handle handle; 317 int result = 0; 318 319 if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) { 320 int i; 321 for (i = 0; i < num_counters; ++i) 322 all_counters[i].count = 0; 323 acpi_gpe_count = 0; 324 acpi_irq_handled = 0; 325 goto end; 326 } 327 328 /* show the event status for both GPEs and Fixed Events */ 329 result = get_status(index, &status, &handle); 330 if (result) 331 goto end; 332 333 if (!(status & ACPI_EVENT_FLAG_HANDLE)) { 334 printk(KERN_WARNING PREFIX 335 "Can not change Invalid GPE/Fixed Event status\n"); 336 return -EINVAL; 337 } 338 339 if (index < num_gpes) { 340 if (!strcmp(buf, "disable\n") && 341 (status & ACPI_EVENT_FLAG_ENABLED)) 342 result = acpi_disable_gpe(handle, index); 343 else if (!strcmp(buf, "enable\n") && 344 !(status & ACPI_EVENT_FLAG_ENABLED)) 345 result = acpi_enable_gpe(handle, index); 346 else if (!strcmp(buf, "clear\n") && 347 (status & ACPI_EVENT_FLAG_SET)) 348 result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); 349 else 350 all_counters[index].count = strtoul(buf, NULL, 0); 351 } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { 352 int event = index - num_gpes; 353 if (!strcmp(buf, "disable\n") && 354 (status & ACPI_EVENT_FLAG_ENABLED)) 355 result = acpi_disable_event(event, ACPI_NOT_ISR); 356 else if (!strcmp(buf, "enable\n") && 357 !(status & ACPI_EVENT_FLAG_ENABLED)) 358 result = acpi_enable_event(event, ACPI_NOT_ISR); 359 else if (!strcmp(buf, "clear\n") && 360 (status & ACPI_EVENT_FLAG_SET)) 361 result = acpi_clear_event(event); 362 else 363 all_counters[index].count = strtoul(buf, NULL, 0); 364 } else 365 all_counters[index].count = strtoul(buf, NULL, 0); 366 367 if (ACPI_FAILURE(result)) 368 result = -EINVAL; 369end: 370 return result ? result : size; 371} 372 373void acpi_irq_stats_init(void) 374{ 375 int i; 376 377 if (all_counters) 378 return; 379 380 num_gpes = acpi_current_gpe_count; 381 num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; 382 383 all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), 384 GFP_KERNEL); 385 if (all_attrs == NULL) 386 return; 387 388 all_counters = kzalloc(sizeof(struct event_counter) * (num_counters), 389 GFP_KERNEL); 390 if (all_counters == NULL) 391 goto fail; 392 393 counter_attrs = kzalloc(sizeof(struct kobj_attribute) * (num_counters), 394 GFP_KERNEL); 395 if (counter_attrs == NULL) 396 goto fail; 397 398 for (i = 0; i < num_counters; ++i) { 399 char buffer[12]; 400 char *name; 401 402 if (i < num_gpes) 403 sprintf(buffer, "gpe%02X", i); 404 else if (i == num_gpes + ACPI_EVENT_PMTIMER) 405 sprintf(buffer, "ff_pmtimer"); 406 else if (i == num_gpes + ACPI_EVENT_GLOBAL) 407 sprintf(buffer, "ff_gbl_lock"); 408 else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON) 409 sprintf(buffer, "ff_pwr_btn"); 410 else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON) 411 sprintf(buffer, "ff_slp_btn"); 412 else if (i == num_gpes + ACPI_EVENT_RTC) 413 sprintf(buffer, "ff_rt_clk"); 414 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE) 415 sprintf(buffer, "gpe_all"); 416 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) 417 sprintf(buffer, "sci"); 418 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR) 419 sprintf(buffer, "error"); 420 else 421 sprintf(buffer, "bug%02X", i); 422 423 name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); 424 if (name == NULL) 425 goto fail; 426 strncpy(name, buffer, strlen(buffer) + 1); 427 428 counter_attrs[i].attr.name = name; 429 counter_attrs[i].attr.mode = 0644; 430 counter_attrs[i].show = counter_show; 431 counter_attrs[i].store = counter_set; 432 433 all_attrs[i] = &counter_attrs[i].attr; 434 } 435 436 interrupt_stats_attr_group.attrs = all_attrs; 437 if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group)) 438 return; 439 440fail: 441 delete_gpe_attr_array(); 442 return; 443} 444 445static void __exit interrupt_stats_exit(void) 446{ 447 sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group); 448 449 delete_gpe_attr_array(); 450 451 return; 452} 453 454/* -------------------------------------------------------------------------- 455 FS Interface (/proc) 456 -------------------------------------------------------------------------- */ 457#ifdef CONFIG_ACPI_PROCFS 458#define ACPI_SYSTEM_FILE_INFO "info" 459#define ACPI_SYSTEM_FILE_EVENT "event" 460#define ACPI_SYSTEM_FILE_DSDT "dsdt" 461#define ACPI_SYSTEM_FILE_FADT "fadt" 462 463static int acpi_system_read_info(struct seq_file *seq, void *offset) 464{ 465 466 seq_printf(seq, "version: %x\n", ACPI_CA_VERSION); 467 return 0; 468} 469 470static int acpi_system_info_open_fs(struct inode *inode, struct file *file) 471{ 472 return single_open(file, acpi_system_read_info, PDE(inode)->data); 473} 474 475static const struct file_operations acpi_system_info_ops = { 476 .owner = THIS_MODULE, 477 .open = acpi_system_info_open_fs, 478 .read = seq_read, 479 .llseek = seq_lseek, 480 .release = single_release, 481}; 482 483static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, 484 loff_t *); 485 486static const struct file_operations acpi_system_dsdt_ops = { 487 .owner = THIS_MODULE, 488 .read = acpi_system_read_dsdt, 489}; 490 491static ssize_t 492acpi_system_read_dsdt(struct file *file, 493 char __user * buffer, size_t count, loff_t * ppos) 494{ 495 acpi_status status = AE_OK; 496 struct acpi_table_header *dsdt = NULL; 497 ssize_t res; 498 499 status = acpi_get_table(ACPI_SIG_DSDT, 1, &dsdt); 500 if (ACPI_FAILURE(status)) 501 return -ENODEV; 502 503 res = simple_read_from_buffer(buffer, count, ppos, dsdt, dsdt->length); 504 505 return res; 506} 507 508static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t, 509 loff_t *); 510 511static const struct file_operations acpi_system_fadt_ops = { 512 .owner = THIS_MODULE, 513 .read = acpi_system_read_fadt, 514}; 515 516static ssize_t 517acpi_system_read_fadt(struct file *file, 518 char __user * buffer, size_t count, loff_t * ppos) 519{ 520 acpi_status status = AE_OK; 521 struct acpi_table_header *fadt = NULL; 522 ssize_t res; 523 524 status = acpi_get_table(ACPI_SIG_FADT, 1, &fadt); 525 if (ACPI_FAILURE(status)) 526 return -ENODEV; 527 528 res = simple_read_from_buffer(buffer, count, ppos, fadt, fadt->length); 529 530 return res; 531} 532 533static int acpi_system_procfs_init(void) 534{ 535 struct proc_dir_entry *entry; 536 int error = 0; 537 538 /* 'info' [R] */ 539 entry = proc_create(ACPI_SYSTEM_FILE_INFO, S_IRUGO, acpi_root_dir, 540 &acpi_system_info_ops); 541 if (!entry) 542 goto Error; 543 544 /* 'dsdt' [R] */ 545 entry = proc_create(ACPI_SYSTEM_FILE_DSDT, S_IRUSR, acpi_root_dir, 546 &acpi_system_dsdt_ops); 547 if (!entry) 548 goto Error; 549 550 /* 'fadt' [R] */ 551 entry = proc_create(ACPI_SYSTEM_FILE_FADT, S_IRUSR, acpi_root_dir, 552 &acpi_system_fadt_ops); 553 if (!entry) 554 goto Error; 555 556 Done: 557 return error; 558 559 Error: 560 remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); 561 remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); 562 remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); 563 564 error = -EFAULT; 565 goto Done; 566} 567#else 568static int acpi_system_procfs_init(void) 569{ 570 return 0; 571} 572#endif 573 574static int __init acpi_system_init(void) 575{ 576 int result = 0; 577 578 if (acpi_disabled) 579 return 0; 580 581 result = acpi_system_procfs_init(); 582 if (result) 583 return result; 584 585 result = acpi_system_sysfs_init(); 586 587 return result; 588} 589 590subsys_initcall(acpi_system_init);