diff options
Diffstat (limited to 'drivers/acpi/ec.c')
| -rw-r--r-- | drivers/acpi/ec.c | 495 |
1 files changed, 177 insertions, 318 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a802962ff2b4..e08cf98f504f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) | 2 | * ec.c - ACPI Embedded Controller Driver (v2.0) |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> | ||
| 5 | * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> | ||
| 4 | * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> | 6 | * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> |
| 5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | 7 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
| 6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | 8 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
| @@ -91,9 +93,9 @@ static struct acpi_driver acpi_ec_driver = { | |||
| 91 | }; | 93 | }; |
| 92 | 94 | ||
| 93 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ | 95 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ |
| 96 | /* External interfaces use first EC only, so remember */ | ||
| 94 | static struct acpi_ec { | 97 | static struct acpi_ec { |
| 95 | acpi_handle handle; | 98 | acpi_handle handle; |
| 96 | unsigned long uid; | ||
| 97 | unsigned long gpe; | 99 | unsigned long gpe; |
| 98 | unsigned long command_addr; | 100 | unsigned long command_addr; |
| 99 | unsigned long data_addr; | 101 | unsigned long data_addr; |
| @@ -101,12 +103,8 @@ static struct acpi_ec { | |||
| 101 | struct mutex lock; | 103 | struct mutex lock; |
| 102 | atomic_t query_pending; | 104 | atomic_t query_pending; |
| 103 | atomic_t event_count; | 105 | atomic_t event_count; |
| 104 | atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ | ||
| 105 | wait_queue_head_t wait; | 106 | wait_queue_head_t wait; |
| 106 | } *ec_ecdt; | 107 | } *boot_ec, *first_ec; |
| 107 | |||
| 108 | /* External interfaces use first EC only, so remember */ | ||
| 109 | static struct acpi_device *first_ec; | ||
| 110 | 108 | ||
| 111 | /* -------------------------------------------------------------------------- | 109 | /* -------------------------------------------------------------------------- |
| 112 | Transaction Management | 110 | Transaction Management |
| @@ -173,56 +171,6 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) | |||
| 173 | return -ETIME; | 171 | return -ETIME; |
| 174 | } | 172 | } |
| 175 | 173 | ||
| 176 | #ifdef ACPI_FUTURE_USAGE | ||
| 177 | /* | ||
| 178 | * Note: samsung nv5000 doesn't work with ec burst mode. | ||
| 179 | * http://bugzilla.kernel.org/show_bug.cgi?id=4980 | ||
| 180 | */ | ||
| 181 | int acpi_ec_enter_burst_mode(struct acpi_ec *ec) | ||
| 182 | { | ||
| 183 | u8 tmp = 0; | ||
| 184 | u8 status = 0; | ||
| 185 | |||
| 186 | status = acpi_ec_read_status(ec); | ||
| 187 | if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { | ||
| 188 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
| 189 | if (status) | ||
| 190 | goto end; | ||
| 191 | acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); | ||
| 192 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); | ||
| 193 | tmp = acpi_ec_read_data(ec); | ||
| 194 | if (tmp != 0x90) { /* Burst ACK byte */ | ||
| 195 | return -EINVAL; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | atomic_set(&ec->leaving_burst, 0); | ||
| 200 | return 0; | ||
| 201 | end: | ||
| 202 | ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); | ||
| 203 | return -1; | ||
| 204 | } | ||
| 205 | |||
| 206 | int acpi_ec_leave_burst_mode(struct acpi_ec *ec) | ||
| 207 | { | ||
| 208 | u8 status = 0; | ||
| 209 | |||
| 210 | status = acpi_ec_read_status(ec); | ||
| 211 | if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { | ||
| 212 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
| 213 | if (status) | ||
| 214 | goto end; | ||
| 215 | acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); | ||
| 216 | acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); | ||
| 217 | } | ||
| 218 | atomic_set(&ec->leaving_burst, 1); | ||
| 219 | return 0; | ||
| 220 | end: | ||
| 221 | ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); | ||
| 222 | return -1; | ||
| 223 | } | ||
| 224 | #endif /* ACPI_FUTURE_USAGE */ | ||
| 225 | |||
| 226 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | 174 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, |
| 227 | const u8 * wdata, unsigned wdata_len, | 175 | const u8 * wdata, unsigned wdata_len, |
| 228 | u8 * rdata, unsigned rdata_len) | 176 | u8 * rdata, unsigned rdata_len) |
| @@ -312,6 +260,21 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
| 312 | return status; | 260 | return status; |
| 313 | } | 261 | } |
| 314 | 262 | ||
| 263 | /* | ||
| 264 | * Note: samsung nv5000 doesn't work with ec burst mode. | ||
| 265 | * http://bugzilla.kernel.org/show_bug.cgi?id=4980 | ||
| 266 | */ | ||
| 267 | int acpi_ec_burst_enable(struct acpi_ec *ec) | ||
| 268 | { | ||
| 269 | u8 d; | ||
| 270 | return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); | ||
| 271 | } | ||
| 272 | |||
| 273 | int acpi_ec_burst_disable(struct acpi_ec *ec) | ||
| 274 | { | ||
| 275 | return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); | ||
| 276 | } | ||
| 277 | |||
| 315 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | 278 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) |
| 316 | { | 279 | { |
| 317 | int result; | 280 | int result; |
| @@ -333,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) | |||
| 333 | /* | 296 | /* |
| 334 | * Externally callable EC access functions. For now, assume 1 EC only | 297 | * Externally callable EC access functions. For now, assume 1 EC only |
| 335 | */ | 298 | */ |
| 299 | int ec_burst_enable(void) | ||
| 300 | { | ||
| 301 | if (!first_ec) | ||
| 302 | return -ENODEV; | ||
| 303 | return acpi_ec_burst_enable(first_ec); | ||
| 304 | } | ||
| 305 | |||
| 306 | EXPORT_SYMBOL(ec_burst_enable); | ||
| 307 | |||
| 308 | int ec_burst_disable(void) | ||
| 309 | { | ||
| 310 | if (!first_ec) | ||
| 311 | return -ENODEV; | ||
| 312 | return acpi_ec_burst_disable(first_ec); | ||
| 313 | } | ||
| 314 | |||
| 315 | EXPORT_SYMBOL(ec_burst_disable); | ||
| 316 | |||
| 336 | int ec_read(u8 addr, u8 * val) | 317 | int ec_read(u8 addr, u8 * val) |
| 337 | { | 318 | { |
| 338 | struct acpi_ec *ec; | ||
| 339 | int err; | 319 | int err; |
| 340 | u8 temp_data; | 320 | u8 temp_data; |
| 341 | 321 | ||
| 342 | if (!first_ec) | 322 | if (!first_ec) |
| 343 | return -ENODEV; | 323 | return -ENODEV; |
| 344 | 324 | ||
| 345 | ec = acpi_driver_data(first_ec); | 325 | err = acpi_ec_read(first_ec, addr, &temp_data); |
| 346 | |||
| 347 | err = acpi_ec_read(ec, addr, &temp_data); | ||
| 348 | 326 | ||
| 349 | if (!err) { | 327 | if (!err) { |
| 350 | *val = temp_data; | 328 | *val = temp_data; |
| @@ -357,15 +335,12 @@ EXPORT_SYMBOL(ec_read); | |||
| 357 | 335 | ||
| 358 | int ec_write(u8 addr, u8 val) | 336 | int ec_write(u8 addr, u8 val) |
| 359 | { | 337 | { |
| 360 | struct acpi_ec *ec; | ||
| 361 | int err; | 338 | int err; |
| 362 | 339 | ||
| 363 | if (!first_ec) | 340 | if (!first_ec) |
| 364 | return -ENODEV; | 341 | return -ENODEV; |
| 365 | 342 | ||
| 366 | ec = acpi_driver_data(first_ec); | 343 | err = acpi_ec_write(first_ec, addr, val); |
| 367 | |||
| 368 | err = acpi_ec_write(ec, addr, val); | ||
| 369 | 344 | ||
| 370 | return err; | 345 | return err; |
| 371 | } | 346 | } |
| @@ -376,14 +351,10 @@ int ec_transaction(u8 command, | |||
| 376 | const u8 * wdata, unsigned wdata_len, | 351 | const u8 * wdata, unsigned wdata_len, |
| 377 | u8 * rdata, unsigned rdata_len) | 352 | u8 * rdata, unsigned rdata_len) |
| 378 | { | 353 | { |
| 379 | struct acpi_ec *ec; | ||
| 380 | |||
| 381 | if (!first_ec) | 354 | if (!first_ec) |
| 382 | return -ENODEV; | 355 | return -ENODEV; |
| 383 | 356 | ||
| 384 | ec = acpi_driver_data(first_ec); | 357 | return acpi_ec_transaction(first_ec, command, wdata, |
| 385 | |||
| 386 | return acpi_ec_transaction(ec, command, wdata, | ||
| 387 | wdata_len, rdata, rdata_len); | 358 | wdata_len, rdata, rdata_len); |
| 388 | } | 359 | } |
| 389 | 360 | ||
| @@ -420,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
| 420 | 391 | ||
| 421 | static void acpi_ec_gpe_query(void *ec_cxt) | 392 | static void acpi_ec_gpe_query(void *ec_cxt) |
| 422 | { | 393 | { |
| 423 | struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; | 394 | struct acpi_ec *ec = ec_cxt; |
| 424 | u8 value = 0; | 395 | u8 value = 0; |
| 425 | char object_name[8]; | 396 | char object_name[8]; |
| 426 | 397 | ||
| @@ -438,8 +409,9 @@ static u32 acpi_ec_gpe_handler(void *data) | |||
| 438 | { | 409 | { |
| 439 | acpi_status status = AE_OK; | 410 | acpi_status status = AE_OK; |
| 440 | u8 value; | 411 | u8 value; |
| 441 | struct acpi_ec *ec = (struct acpi_ec *)data; | 412 | struct acpi_ec *ec = data; |
| 442 | atomic_inc(&ec->event_count); | 413 | atomic_inc(&ec->event_count); |
| 414 | |||
| 443 | if (acpi_ec_mode == EC_INTR) { | 415 | if (acpi_ec_mode == EC_INTR) { |
| 444 | wake_up(&ec->wait); | 416 | wake_up(&ec->wait); |
| 445 | } | 417 | } |
| @@ -482,7 +454,7 @@ acpi_ec_space_handler(u32 function, | |||
| 482 | void *handler_context, void *region_context) | 454 | void *handler_context, void *region_context) |
| 483 | { | 455 | { |
| 484 | int result = 0; | 456 | int result = 0; |
| 485 | struct acpi_ec *ec = NULL; | 457 | struct acpi_ec *ec = handler_context; |
| 486 | u64 temp = *value; | 458 | u64 temp = *value; |
| 487 | acpi_integer f_v = 0; | 459 | acpi_integer f_v = 0; |
| 488 | int i = 0; | 460 | int i = 0; |
| @@ -494,8 +466,6 @@ acpi_ec_space_handler(u32 function, | |||
| 494 | return AE_BAD_PARAMETER; | 466 | return AE_BAD_PARAMETER; |
| 495 | } | 467 | } |
| 496 | 468 | ||
| 497 | ec = (struct acpi_ec *)handler_context; | ||
| 498 | |||
| 499 | next_byte: | 469 | next_byte: |
| 500 | switch (function) { | 470 | switch (function) { |
| 501 | case ACPI_READ: | 471 | case ACPI_READ: |
| @@ -551,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_dir; | |||
| 551 | 521 | ||
| 552 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) | 522 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) |
| 553 | { | 523 | { |
| 554 | struct acpi_ec *ec = (struct acpi_ec *)seq->private; | 524 | struct acpi_ec *ec = seq->private; |
| 555 | 525 | ||
| 556 | if (!ec) | 526 | if (!ec) |
| 557 | goto end; | 527 | goto end; |
| 558 | 528 | ||
| 559 | seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe); | 529 | seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); |
| 560 | seq_printf(seq, "ports: 0x%02x, 0x%02x\n", | 530 | seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", |
| 561 | (u32) ec->command_addr, (u32) ec->data_addr); | 531 | (unsigned)ec->command_addr, (unsigned)ec->data_addr); |
| 562 | seq_printf(seq, "use global lock: %s\n", | 532 | seq_printf(seq, "use global lock:\t%s\n", |
| 563 | ec->global_lock ? "yes" : "no"); | 533 | ec->global_lock ? "yes" : "no"); |
| 564 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | ||
| 565 | |||
| 566 | end: | 534 | end: |
| 567 | return 0; | 535 | return 0; |
| 568 | } | 536 | } |
| @@ -619,154 +587,122 @@ static int acpi_ec_remove_fs(struct acpi_device *device) | |||
| 619 | /* -------------------------------------------------------------------------- | 587 | /* -------------------------------------------------------------------------- |
| 620 | Driver Interface | 588 | Driver Interface |
| 621 | -------------------------------------------------------------------------- */ | 589 | -------------------------------------------------------------------------- */ |
| 590 | static acpi_status | ||
| 591 | ec_parse_io_ports(struct acpi_resource *resource, void *context); | ||
| 592 | |||
| 593 | static acpi_status | ||
| 594 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); | ||
| 595 | |||
| 596 | static struct acpi_ec *make_acpi_ec(void) | ||
| 597 | { | ||
| 598 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
| 599 | if (!ec) | ||
| 600 | return NULL; | ||
| 601 | |||
| 602 | atomic_set(&ec->query_pending, 1); | ||
| 603 | atomic_set(&ec->event_count, 1); | ||
| 604 | mutex_init(&ec->lock); | ||
| 605 | init_waitqueue_head(&ec->wait); | ||
| 606 | |||
| 607 | return ec; | ||
| 608 | } | ||
| 622 | 609 | ||
| 623 | static int acpi_ec_add(struct acpi_device *device) | 610 | static int acpi_ec_add(struct acpi_device *device) |
| 624 | { | 611 | { |
| 625 | int result = 0; | ||
| 626 | acpi_status status = AE_OK; | 612 | acpi_status status = AE_OK; |
| 627 | struct acpi_ec *ec = NULL; | 613 | struct acpi_ec *ec = NULL; |
| 628 | 614 | ||
| 629 | if (!device) | 615 | if (!device) |
| 630 | return -EINVAL; | 616 | return -EINVAL; |
| 631 | 617 | ||
| 632 | ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
| 633 | if (!ec) | ||
| 634 | return -ENOMEM; | ||
| 635 | |||
| 636 | ec->handle = device->handle; | ||
| 637 | ec->uid = -1; | ||
| 638 | mutex_init(&ec->lock); | ||
| 639 | atomic_set(&ec->query_pending, 0); | ||
| 640 | atomic_set(&ec->event_count, 1); | ||
| 641 | if (acpi_ec_mode == EC_INTR) { | ||
| 642 | atomic_set(&ec->leaving_burst, 1); | ||
| 643 | init_waitqueue_head(&ec->wait); | ||
| 644 | } | ||
| 645 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | 618 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
| 646 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | 619 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); |
| 647 | acpi_driver_data(device) = ec; | ||
| 648 | |||
| 649 | /* Use the global lock for all EC transactions? */ | ||
| 650 | acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); | ||
| 651 | |||
| 652 | /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: | ||
| 653 | http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ | ||
| 654 | if (ec_ecdt) { | ||
| 655 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, | ||
| 656 | ACPI_ADR_SPACE_EC, | ||
| 657 | &acpi_ec_space_handler); | ||
| 658 | 620 | ||
| 659 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, | 621 | ec = make_acpi_ec(); |
| 660 | &acpi_ec_gpe_handler); | 622 | if (!ec) |
| 623 | return -ENOMEM; | ||
| 661 | 624 | ||
| 662 | kfree(ec_ecdt); | 625 | status = ec_parse_device(device->handle, 0, ec, NULL); |
| 626 | if (status != AE_CTRL_TERMINATE) { | ||
| 627 | kfree(ec); | ||
| 628 | return -EINVAL; | ||
| 663 | } | 629 | } |
| 664 | 630 | ||
| 665 | /* Get GPE bit assignment (EC events). */ | 631 | /* Check if we found the boot EC */ |
| 666 | /* TODO: Add support for _GPE returning a package */ | 632 | if (boot_ec) { |
| 667 | status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); | 633 | if (boot_ec->gpe == ec->gpe) { |
| 668 | if (ACPI_FAILURE(status)) { | 634 | /* We might have incorrect info for GL at boot time */ |
| 669 | ACPI_EXCEPTION((AE_INFO, status, | 635 | mutex_lock(&boot_ec->lock); |
| 670 | "Obtaining GPE bit assignment")); | 636 | boot_ec->global_lock = ec->global_lock; |
| 671 | result = -ENODEV; | 637 | mutex_unlock(&boot_ec->lock); |
| 672 | goto end; | 638 | kfree(ec); |
| 673 | } | 639 | ec = boot_ec; |
| 640 | } | ||
| 641 | } else | ||
| 642 | first_ec = ec; | ||
| 643 | ec->handle = device->handle; | ||
| 644 | acpi_driver_data(device) = ec; | ||
| 674 | 645 | ||
| 675 | result = acpi_ec_add_fs(device); | 646 | acpi_ec_add_fs(device); |
| 676 | if (result) | ||
| 677 | goto end; | ||
| 678 | 647 | ||
| 679 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", | 648 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", |
| 680 | acpi_device_name(device), acpi_device_bid(device), | 649 | acpi_device_name(device), acpi_device_bid(device), |
| 681 | (u32) ec->gpe)); | 650 | (u32) ec->gpe)); |
| 682 | 651 | ||
| 683 | if (!first_ec) | 652 | return 0; |
| 684 | first_ec = device; | ||
| 685 | |||
| 686 | end: | ||
| 687 | if (result) | ||
| 688 | kfree(ec); | ||
| 689 | |||
| 690 | return result; | ||
| 691 | } | 653 | } |
| 692 | 654 | ||
| 693 | static int acpi_ec_remove(struct acpi_device *device, int type) | 655 | static int acpi_ec_remove(struct acpi_device *device, int type) |
| 694 | { | 656 | { |
| 695 | struct acpi_ec *ec = NULL; | 657 | struct acpi_ec *ec; |
| 696 | 658 | ||
| 697 | if (!device) | 659 | if (!device) |
| 698 | return -EINVAL; | 660 | return -EINVAL; |
| 699 | 661 | ||
| 700 | ec = acpi_driver_data(device); | 662 | ec = acpi_driver_data(device); |
| 701 | |||
| 702 | acpi_ec_remove_fs(device); | 663 | acpi_ec_remove_fs(device); |
| 664 | acpi_driver_data(device) = NULL; | ||
| 665 | if (ec == first_ec) | ||
| 666 | first_ec = NULL; | ||
| 703 | 667 | ||
| 704 | kfree(ec); | 668 | /* Don't touch boot EC */ |
| 705 | 669 | if (boot_ec != ec) | |
| 670 | kfree(ec); | ||
| 706 | return 0; | 671 | return 0; |
| 707 | } | 672 | } |
| 708 | 673 | ||
| 709 | static acpi_status | 674 | static acpi_status |
| 710 | acpi_ec_io_ports(struct acpi_resource *resource, void *context) | 675 | ec_parse_io_ports(struct acpi_resource *resource, void *context) |
| 711 | { | 676 | { |
| 712 | struct acpi_ec *ec = (struct acpi_ec *)context; | 677 | struct acpi_ec *ec = context; |
| 713 | 678 | ||
| 714 | if (resource->type != ACPI_RESOURCE_TYPE_IO) { | 679 | if (resource->type != ACPI_RESOURCE_TYPE_IO) |
| 715 | return AE_OK; | 680 | return AE_OK; |
| 716 | } | ||
| 717 | 681 | ||
| 718 | /* | 682 | /* |
| 719 | * The first address region returned is the data port, and | 683 | * The first address region returned is the data port, and |
| 720 | * the second address region returned is the status/command | 684 | * the second address region returned is the status/command |
| 721 | * port. | 685 | * port. |
| 722 | */ | 686 | */ |
| 723 | if (ec->data_addr == 0) { | 687 | if (ec->data_addr == 0) |
| 724 | ec->data_addr = resource->data.io.minimum; | 688 | ec->data_addr = resource->data.io.minimum; |
| 725 | } else if (ec->command_addr == 0) { | 689 | else if (ec->command_addr == 0) |
| 726 | ec->command_addr = resource->data.io.minimum; | 690 | ec->command_addr = resource->data.io.minimum; |
| 727 | } else { | 691 | else |
| 728 | return AE_CTRL_TERMINATE; | 692 | return AE_CTRL_TERMINATE; |
| 729 | } | ||
| 730 | 693 | ||
| 731 | return AE_OK; | 694 | return AE_OK; |
| 732 | } | 695 | } |
| 733 | 696 | ||
| 734 | static int acpi_ec_start(struct acpi_device *device) | 697 | static int ec_install_handlers(struct acpi_ec *ec) |
| 735 | { | 698 | { |
| 736 | acpi_status status = AE_OK; | 699 | acpi_status status; |
| 737 | struct acpi_ec *ec = NULL; | ||
| 738 | |||
| 739 | if (!device) | ||
| 740 | return -EINVAL; | ||
| 741 | |||
| 742 | ec = acpi_driver_data(device); | ||
| 743 | |||
| 744 | if (!ec) | ||
| 745 | return -EINVAL; | ||
| 746 | |||
| 747 | /* | ||
| 748 | * Get I/O port addresses. Convert to GAS format. | ||
| 749 | */ | ||
| 750 | status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, | ||
| 751 | acpi_ec_io_ports, ec); | ||
| 752 | if (ACPI_FAILURE(status) || ec->command_addr == 0) { | ||
| 753 | ACPI_EXCEPTION((AE_INFO, status, | ||
| 754 | "Error getting I/O port addresses")); | ||
| 755 | return -ENODEV; | ||
| 756 | } | ||
| 757 | |||
| 758 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", | ||
| 759 | ec->gpe, ec->command_addr, ec->data_addr)); | ||
| 760 | |||
| 761 | /* | ||
| 762 | * Install GPE handler | ||
| 763 | */ | ||
| 764 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 700 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
| 765 | ACPI_GPE_EDGE_TRIGGERED, | 701 | ACPI_GPE_EDGE_TRIGGERED, |
| 766 | &acpi_ec_gpe_handler, ec); | 702 | &acpi_ec_gpe_handler, ec); |
| 767 | if (ACPI_FAILURE(status)) { | 703 | if (ACPI_FAILURE(status)) |
| 768 | return -ENODEV; | 704 | return -ENODEV; |
| 769 | } | 705 | |
| 770 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); | 706 | acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); |
| 771 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | 707 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); |
| 772 | 708 | ||
| @@ -779,18 +715,49 @@ static int acpi_ec_start(struct acpi_device *device) | |||
| 779 | return -ENODEV; | 715 | return -ENODEV; |
| 780 | } | 716 | } |
| 781 | 717 | ||
| 782 | return AE_OK; | 718 | /* EC is fully operational, allow queries */ |
| 719 | atomic_set(&ec->query_pending, 0); | ||
| 720 | |||
| 721 | return 0; | ||
| 722 | } | ||
| 723 | |||
| 724 | static int acpi_ec_start(struct acpi_device *device) | ||
| 725 | { | ||
| 726 | struct acpi_ec *ec; | ||
| 727 | |||
| 728 | if (!device) | ||
| 729 | return -EINVAL; | ||
| 730 | |||
| 731 | ec = acpi_driver_data(device); | ||
| 732 | |||
| 733 | if (!ec) | ||
| 734 | return -EINVAL; | ||
| 735 | |||
| 736 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", | ||
| 737 | ec->gpe, ec->command_addr, ec->data_addr)); | ||
| 738 | |||
| 739 | /* Boot EC is already working */ | ||
| 740 | if (ec == boot_ec) | ||
| 741 | return 0; | ||
| 742 | |||
| 743 | return ec_install_handlers(ec); | ||
| 783 | } | 744 | } |
| 784 | 745 | ||
| 785 | static int acpi_ec_stop(struct acpi_device *device, int type) | 746 | static int acpi_ec_stop(struct acpi_device *device, int type) |
| 786 | { | 747 | { |
| 787 | acpi_status status = AE_OK; | 748 | acpi_status status; |
| 788 | struct acpi_ec *ec = NULL; | 749 | struct acpi_ec *ec; |
| 789 | 750 | ||
| 790 | if (!device) | 751 | if (!device) |
| 791 | return -EINVAL; | 752 | return -EINVAL; |
| 792 | 753 | ||
| 793 | ec = acpi_driver_data(device); | 754 | ec = acpi_driver_data(device); |
| 755 | if (!ec) | ||
| 756 | return -EINVAL; | ||
| 757 | |||
| 758 | /* Don't touch boot EC */ | ||
| 759 | if (ec == boot_ec) | ||
| 760 | return 0; | ||
| 794 | 761 | ||
| 795 | status = acpi_remove_address_space_handler(ec->handle, | 762 | status = acpi_remove_address_space_handler(ec->handle, |
| 796 | ACPI_ADR_SPACE_EC, | 763 | ACPI_ADR_SPACE_EC, |
| @@ -805,164 +772,67 @@ static int acpi_ec_stop(struct acpi_device *device, int type) | |||
| 805 | return 0; | 772 | return 0; |
| 806 | } | 773 | } |
| 807 | 774 | ||
| 808 | static acpi_status __init | 775 | static acpi_status |
| 809 | acpi_fake_ecdt_callback(acpi_handle handle, | 776 | ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) |
| 810 | u32 Level, void *context, void **retval) | ||
| 811 | { | 777 | { |
| 812 | acpi_status status; | 778 | acpi_status status; |
| 813 | 779 | ||
| 814 | mutex_init(&ec_ecdt->lock); | 780 | struct acpi_ec *ec = context; |
| 815 | atomic_set(&ec_ecdt->event_count, 1); | ||
| 816 | if (acpi_ec_mode == EC_INTR) { | ||
| 817 | init_waitqueue_head(&ec_ecdt->wait); | ||
| 818 | } | ||
| 819 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | 781 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, |
| 820 | acpi_ec_io_ports, ec_ecdt); | 782 | ec_parse_io_ports, ec); |
| 821 | if (ACPI_FAILURE(status)) | 783 | if (ACPI_FAILURE(status)) |
| 822 | return status; | 784 | return status; |
| 823 | 785 | ||
| 824 | ec_ecdt->uid = -1; | 786 | /* Get GPE bit assignment (EC events). */ |
| 825 | acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); | 787 | /* TODO: Add support for _GPE returning a package */ |
| 826 | 788 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); | |
| 827 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe); | ||
| 828 | if (ACPI_FAILURE(status)) | 789 | if (ACPI_FAILURE(status)) |
| 829 | return status; | 790 | return status; |
| 830 | ec_ecdt->global_lock = TRUE; | ||
| 831 | ec_ecdt->handle = handle; | ||
| 832 | |||
| 833 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", | ||
| 834 | ec_ecdt->gpe, ec_ecdt->command_addr, | ||
| 835 | ec_ecdt->data_addr)); | ||
| 836 | 791 | ||
| 837 | return AE_CTRL_TERMINATE; | 792 | /* Use the global lock for all EC transactions? */ |
| 838 | } | 793 | acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); |
| 839 | |||
| 840 | /* | ||
| 841 | * Some BIOS (such as some from Gateway laptops) access EC region very early | ||
| 842 | * such as in BAT0._INI or EC._INI before an EC device is found and | ||
| 843 | * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily | ||
| 844 | * required, but if EC regison is accessed early, it is required. | ||
| 845 | * The routine tries to workaround the BIOS bug by pre-scan EC device | ||
| 846 | * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any | ||
| 847 | * op region (since _REG isn't invoked yet). The assumption is true for | ||
| 848 | * all systems found. | ||
| 849 | */ | ||
| 850 | static int __init acpi_ec_fake_ecdt(void) | ||
| 851 | { | ||
| 852 | acpi_status status; | ||
| 853 | int ret = 0; | ||
| 854 | 794 | ||
| 855 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT")); | 795 | ec->handle = handle; |
| 856 | 796 | ||
| 857 | ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | 797 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", |
| 858 | if (!ec_ecdt) { | 798 | ec->gpe, ec->command_addr, ec->data_addr)); |
| 859 | ret = -ENOMEM; | ||
| 860 | goto error; | ||
| 861 | } | ||
| 862 | 799 | ||
| 863 | status = acpi_get_devices(ACPI_EC_HID, | 800 | return AE_CTRL_TERMINATE; |
| 864 | acpi_fake_ecdt_callback, NULL, NULL); | ||
| 865 | if (ACPI_FAILURE(status)) { | ||
| 866 | kfree(ec_ecdt); | ||
| 867 | ec_ecdt = NULL; | ||
| 868 | ret = -ENODEV; | ||
| 869 | ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT")); | ||
| 870 | goto error; | ||
| 871 | } | ||
| 872 | return 0; | ||
| 873 | error: | ||
| 874 | return ret; | ||
| 875 | } | 801 | } |
| 876 | 802 | ||
| 877 | static int __init acpi_ec_get_real_ecdt(void) | 803 | int __init acpi_ec_ecdt_probe(void) |
| 878 | { | 804 | { |
| 805 | int ret; | ||
| 879 | acpi_status status; | 806 | acpi_status status; |
| 880 | struct acpi_table_ecdt *ecdt_ptr; | 807 | struct acpi_table_ecdt *ecdt_ptr; |
| 881 | 808 | ||
| 882 | status = acpi_get_table(ACPI_SIG_ECDT, 1, | 809 | boot_ec = make_acpi_ec(); |
| 883 | (struct acpi_table_header **)&ecdt_ptr); | 810 | if (!boot_ec) |
| 884 | if (ACPI_FAILURE(status)) | 811 | return -ENOMEM; |
| 885 | return -ENODEV; | ||
| 886 | |||
| 887 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); | ||
| 888 | |||
| 889 | /* | 812 | /* |
| 890 | * Generate a temporary ec context to use until the namespace is scanned | 813 | * Generate a boot ec context |
| 891 | */ | 814 | */ |
| 892 | ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | ||
| 893 | if (!ec_ecdt) | ||
| 894 | return -ENOMEM; | ||
| 895 | 815 | ||
| 896 | mutex_init(&ec_ecdt->lock); | 816 | status = acpi_get_table(ACPI_SIG_ECDT, 1, |
| 897 | atomic_set(&ec_ecdt->event_count, 1); | 817 | (struct acpi_table_header **)&ecdt_ptr); |
| 898 | if (acpi_ec_mode == EC_INTR) { | 818 | if (ACPI_FAILURE(status)) |
| 899 | init_waitqueue_head(&ec_ecdt->wait); | ||
| 900 | } | ||
| 901 | ec_ecdt->command_addr = ecdt_ptr->control.address; | ||
| 902 | ec_ecdt->data_addr = ecdt_ptr->data.address; | ||
| 903 | ec_ecdt->gpe = ecdt_ptr->gpe; | ||
| 904 | /* use the GL just to be safe */ | ||
| 905 | ec_ecdt->global_lock = TRUE; | ||
| 906 | ec_ecdt->uid = ecdt_ptr->uid; | ||
| 907 | |||
| 908 | status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle); | ||
| 909 | if (ACPI_FAILURE(status)) { | ||
| 910 | goto error; | 819 | goto error; |
| 911 | } | ||
| 912 | |||
| 913 | return 0; | ||
| 914 | error: | ||
| 915 | ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); | ||
| 916 | kfree(ec_ecdt); | ||
| 917 | ec_ecdt = NULL; | ||
| 918 | 820 | ||
| 919 | return -ENODEV; | 821 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); |
| 920 | } | ||
| 921 | |||
| 922 | static int __initdata acpi_fake_ecdt_enabled; | ||
| 923 | int __init acpi_ec_ecdt_probe(void) | ||
| 924 | { | ||
| 925 | acpi_status status; | ||
| 926 | int ret; | ||
| 927 | 822 | ||
| 928 | ret = acpi_ec_get_real_ecdt(); | 823 | boot_ec->command_addr = ecdt_ptr->control.address; |
| 929 | /* Try to make a fake ECDT */ | 824 | boot_ec->data_addr = ecdt_ptr->data.address; |
| 930 | if (ret && acpi_fake_ecdt_enabled) { | 825 | boot_ec->gpe = ecdt_ptr->gpe; |
| 931 | ret = acpi_ec_fake_ecdt(); | 826 | boot_ec->handle = ACPI_ROOT_OBJECT; |
| 932 | } | ||
| 933 | 827 | ||
| 934 | if (ret) | 828 | ret = ec_install_handlers(boot_ec); |
| 829 | if (!ret) { | ||
| 830 | first_ec = boot_ec; | ||
| 935 | return 0; | 831 | return 0; |
| 936 | |||
| 937 | /* | ||
| 938 | * Install GPE handler | ||
| 939 | */ | ||
| 940 | status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe, | ||
| 941 | ACPI_GPE_EDGE_TRIGGERED, | ||
| 942 | &acpi_ec_gpe_handler, ec_ecdt); | ||
| 943 | if (ACPI_FAILURE(status)) { | ||
| 944 | goto error; | ||
| 945 | } | 832 | } |
| 946 | acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME); | ||
| 947 | acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR); | ||
| 948 | |||
| 949 | status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, | ||
| 950 | ACPI_ADR_SPACE_EC, | ||
| 951 | &acpi_ec_space_handler, | ||
| 952 | &acpi_ec_space_setup, | ||
| 953 | ec_ecdt); | ||
| 954 | if (ACPI_FAILURE(status)) { | ||
| 955 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, | ||
| 956 | &acpi_ec_gpe_handler); | ||
| 957 | goto error; | ||
| 958 | } | ||
| 959 | |||
| 960 | return 0; | ||
| 961 | |||
| 962 | error: | 833 | error: |
| 963 | ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); | 834 | kfree(boot_ec); |
| 964 | kfree(ec_ecdt); | 835 | boot_ec = NULL; |
| 965 | ec_ecdt = NULL; | ||
| 966 | 836 | ||
| 967 | return -ENODEV; | 837 | return -ENODEV; |
| 968 | } | 838 | } |
| @@ -1003,13 +873,6 @@ static void __exit acpi_ec_exit(void) | |||
| 1003 | } | 873 | } |
| 1004 | #endif /* 0 */ | 874 | #endif /* 0 */ |
| 1005 | 875 | ||
| 1006 | static int __init acpi_fake_ecdt_setup(char *str) | ||
| 1007 | { | ||
| 1008 | acpi_fake_ecdt_enabled = 1; | ||
| 1009 | return 1; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | __setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); | ||
| 1013 | static int __init acpi_ec_set_intr_mode(char *str) | 876 | static int __init acpi_ec_set_intr_mode(char *str) |
| 1014 | { | 877 | { |
| 1015 | int intr; | 878 | int intr; |
| @@ -1017,12 +880,8 @@ static int __init acpi_ec_set_intr_mode(char *str) | |||
| 1017 | if (!get_option(&str, &intr)) | 880 | if (!get_option(&str, &intr)) |
| 1018 | return 0; | 881 | return 0; |
| 1019 | 882 | ||
| 1020 | if (intr) { | 883 | acpi_ec_mode = (intr) ? EC_INTR : EC_POLL; |
| 1021 | acpi_ec_mode = EC_INTR; | 884 | |
| 1022 | } else { | ||
| 1023 | acpi_ec_mode = EC_POLL; | ||
| 1024 | } | ||
| 1025 | acpi_ec_driver.ops.add = acpi_ec_add; | ||
| 1026 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); | 885 | printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); |
| 1027 | 886 | ||
| 1028 | return 1; | 887 | return 1; |
