diff options
| -rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/misc/eeepc-laptop.c | 147 |
2 files changed, 148 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 01b7deba91e8..7a4869a9db86 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
| @@ -356,6 +356,7 @@ config EEEPC_LAPTOP | |||
| 356 | depends on X86 | 356 | depends on X86 |
| 357 | depends on ACPI | 357 | depends on ACPI |
| 358 | depends on BACKLIGHT_CLASS_DEVICE | 358 | depends on BACKLIGHT_CLASS_DEVICE |
| 359 | depends on HWMON | ||
| 359 | depends on EXPERIMENTAL | 360 | depends on EXPERIMENTAL |
| 360 | ---help--- | 361 | ---help--- |
| 361 | This driver supports the Fn-Fx keys on Eee PC laptops. | 362 | This driver supports the Fn-Fx keys on Eee PC laptops. |
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 2b59b7ed4b14..6d727609097f 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
| 24 | #include <linux/backlight.h> | 24 | #include <linux/backlight.h> |
| 25 | #include <linux/fb.h> | 25 | #include <linux/fb.h> |
| 26 | #include <linux/hwmon.h> | ||
| 27 | #include <linux/hwmon-sysfs.h> | ||
| 26 | #include <acpi/acpi_drivers.h> | 28 | #include <acpi/acpi_drivers.h> |
| 27 | #include <acpi/acpi_bus.h> | 29 | #include <acpi/acpi_bus.h> |
| 28 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
| @@ -103,6 +105,15 @@ const char *cm_setv[] = { | |||
| 103 | "CRDS", NULL | 105 | "CRDS", NULL |
| 104 | }; | 106 | }; |
| 105 | 107 | ||
| 108 | #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." | ||
| 109 | |||
| 110 | #define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */ | ||
| 111 | #define EEEPC_EC_SC02 0x63 | ||
| 112 | #define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */ | ||
| 113 | #define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */ | ||
| 114 | #define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */ | ||
| 115 | #define EEEPC_EC_SFB3 0xD3 | ||
| 116 | |||
| 106 | /* | 117 | /* |
| 107 | * This is the main structure, we can use it to store useful information | 118 | * This is the main structure, we can use it to store useful information |
| 108 | * about the hotk device | 119 | * about the hotk device |
| @@ -154,6 +165,9 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
| 154 | /* The backlight device /sys/class/backlight */ | 165 | /* The backlight device /sys/class/backlight */ |
| 155 | static struct backlight_device *eeepc_backlight_device; | 166 | static struct backlight_device *eeepc_backlight_device; |
| 156 | 167 | ||
| 168 | /* The hwmon device */ | ||
| 169 | static struct device *eeepc_hwmon_device; | ||
| 170 | |||
| 157 | /* | 171 | /* |
| 158 | * The backlight class declaration | 172 | * The backlight class declaration |
| 159 | */ | 173 | */ |
| @@ -429,6 +443,100 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) | |||
| 429 | } | 443 | } |
| 430 | 444 | ||
| 431 | /* | 445 | /* |
| 446 | * Hwmon | ||
| 447 | */ | ||
| 448 | static int eeepc_get_fan_pwm(void) | ||
| 449 | { | ||
| 450 | int value = 0; | ||
| 451 | |||
| 452 | read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value); | ||
| 453 | return (value); | ||
| 454 | } | ||
| 455 | |||
| 456 | static void eeepc_set_fan_pwm(int value) | ||
| 457 | { | ||
| 458 | value = SENSORS_LIMIT(value, 0, 100); | ||
| 459 | ec_write(EEEPC_EC_SC02, value); | ||
| 460 | } | ||
| 461 | |||
| 462 | static int eeepc_get_fan_rpm(void) | ||
| 463 | { | ||
| 464 | int high = 0; | ||
| 465 | int low = 0; | ||
| 466 | |||
| 467 | read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high); | ||
| 468 | read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low); | ||
| 469 | return (high << 8 | low); | ||
| 470 | } | ||
| 471 | |||
| 472 | static int eeepc_get_fan_ctrl(void) | ||
| 473 | { | ||
| 474 | int value = 0; | ||
| 475 | |||
| 476 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | ||
| 477 | return ((value & 0x02 ? 1 : 0)); | ||
| 478 | } | ||
| 479 | |||
| 480 | static void eeepc_set_fan_ctrl(int manual) | ||
| 481 | { | ||
| 482 | int value = 0; | ||
| 483 | |||
| 484 | read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); | ||
| 485 | if (manual) | ||
| 486 | value |= 0x02; | ||
| 487 | else | ||
| 488 | value &= ~0x02; | ||
| 489 | ec_write(EEEPC_EC_SFB3, value); | ||
| 490 | } | ||
| 491 | |||
| 492 | static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) | ||
| 493 | { | ||
| 494 | int rv, value; | ||
| 495 | |||
| 496 | rv = parse_arg(buf, count, &value); | ||
| 497 | if (rv > 0) | ||
| 498 | set(value); | ||
| 499 | return rv; | ||
| 500 | } | ||
| 501 | |||
| 502 | static ssize_t show_sys_hwmon(int (*get)(void), char *buf) | ||
| 503 | { | ||
| 504 | return sprintf(buf, "%d\n", get()); | ||
| 505 | } | ||
| 506 | |||
| 507 | #define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \ | ||
| 508 | static ssize_t show_##_name(struct device *dev, \ | ||
| 509 | struct device_attribute *attr, \ | ||
| 510 | char *buf) \ | ||
| 511 | { \ | ||
| 512 | return show_sys_hwmon(_set, buf); \ | ||
| 513 | } \ | ||
| 514 | static ssize_t store_##_name(struct device *dev, \ | ||
| 515 | struct device_attribute *attr, \ | ||
| 516 | const char *buf, size_t count) \ | ||
| 517 | { \ | ||
| 518 | return store_sys_hwmon(_get, buf, count); \ | ||
| 519 | } \ | ||
| 520 | static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0); | ||
| 521 | |||
| 522 | EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL); | ||
| 523 | EEEPC_CREATE_SENSOR_ATTR(fan1_pwm, S_IRUGO | S_IWUSR, | ||
| 524 | eeepc_get_fan_pwm, eeepc_set_fan_pwm); | ||
| 525 | EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | ||
| 526 | eeepc_get_fan_ctrl, eeepc_set_fan_ctrl); | ||
| 527 | |||
| 528 | static struct attribute *hwmon_attributes[] = { | ||
| 529 | &sensor_dev_attr_fan1_pwm.dev_attr.attr, | ||
| 530 | &sensor_dev_attr_fan1_input.dev_attr.attr, | ||
| 531 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
| 532 | NULL | ||
| 533 | }; | ||
| 534 | |||
| 535 | static struct attribute_group hwmon_attribute_group = { | ||
| 536 | .attrs = hwmon_attributes | ||
| 537 | }; | ||
| 538 | |||
| 539 | /* | ||
| 432 | * exit/init | 540 | * exit/init |
| 433 | */ | 541 | */ |
| 434 | static void eeepc_backlight_exit(void) | 542 | static void eeepc_backlight_exit(void) |
| @@ -438,9 +546,23 @@ static void eeepc_backlight_exit(void) | |||
| 438 | eeepc_backlight_device = NULL; | 546 | eeepc_backlight_device = NULL; |
| 439 | } | 547 | } |
| 440 | 548 | ||
| 549 | static void eeepc_hwmon_exit(void) | ||
| 550 | { | ||
| 551 | struct device *hwmon; | ||
| 552 | |||
| 553 | hwmon = eeepc_hwmon_device; | ||
| 554 | if (!hwmon) | ||
| 555 | return ; | ||
| 556 | hwmon_device_unregister(hwmon); | ||
| 557 | sysfs_remove_group(&hwmon->kobj, | ||
| 558 | &hwmon_attribute_group); | ||
| 559 | eeepc_hwmon_device = NULL; | ||
| 560 | } | ||
| 561 | |||
| 441 | static void __exit eeepc_laptop_exit(void) | 562 | static void __exit eeepc_laptop_exit(void) |
| 442 | { | 563 | { |
| 443 | eeepc_backlight_exit(); | 564 | eeepc_backlight_exit(); |
| 565 | eeepc_hwmon_exit(); | ||
| 444 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | 566 | acpi_bus_unregister_driver(&eeepc_hotk_driver); |
| 445 | sysfs_remove_group(&platform_device->dev.kobj, | 567 | sysfs_remove_group(&platform_device->dev.kobj, |
| 446 | &platform_attribute_group); | 568 | &platform_attribute_group); |
| @@ -468,6 +590,26 @@ static int eeepc_backlight_init(struct device *dev) | |||
| 468 | return 0; | 590 | return 0; |
| 469 | } | 591 | } |
| 470 | 592 | ||
| 593 | static int eeepc_hwmon_init(struct device *dev) | ||
| 594 | { | ||
| 595 | struct device *hwmon; | ||
| 596 | int result; | ||
| 597 | |||
| 598 | hwmon = hwmon_device_register(dev); | ||
| 599 | if (IS_ERR(hwmon)) { | ||
| 600 | printk(EEEPC_ERR | ||
| 601 | "Could not register eeepc hwmon device\n"); | ||
| 602 | eeepc_hwmon_device = NULL; | ||
| 603 | return PTR_ERR(hwmon); | ||
| 604 | } | ||
| 605 | eeepc_hwmon_device = hwmon; | ||
| 606 | result = sysfs_create_group(&hwmon->kobj, | ||
| 607 | &hwmon_attribute_group); | ||
| 608 | if (result) | ||
| 609 | eeepc_hwmon_exit(); | ||
| 610 | return result; | ||
| 611 | } | ||
| 612 | |||
| 471 | static int __init eeepc_laptop_init(void) | 613 | static int __init eeepc_laptop_init(void) |
| 472 | { | 614 | { |
| 473 | struct device *dev; | 615 | struct device *dev; |
| @@ -486,6 +628,9 @@ static int __init eeepc_laptop_init(void) | |||
| 486 | result = eeepc_backlight_init(dev); | 628 | result = eeepc_backlight_init(dev); |
| 487 | if (result) | 629 | if (result) |
| 488 | goto fail_backlight; | 630 | goto fail_backlight; |
| 631 | result = eeepc_hwmon_init(dev); | ||
| 632 | if (result) | ||
| 633 | goto fail_hwmon; | ||
| 489 | /* Register platform stuff */ | 634 | /* Register platform stuff */ |
| 490 | result = platform_driver_register(&platform_driver); | 635 | result = platform_driver_register(&platform_driver); |
| 491 | if (result) | 636 | if (result) |
| @@ -510,6 +655,8 @@ fail_platform_device2: | |||
| 510 | fail_platform_device1: | 655 | fail_platform_device1: |
| 511 | platform_driver_unregister(&platform_driver); | 656 | platform_driver_unregister(&platform_driver); |
| 512 | fail_platform_driver: | 657 | fail_platform_driver: |
| 658 | eeepc_hwmon_exit(); | ||
| 659 | fail_hwmon: | ||
| 513 | eeepc_backlight_exit(); | 660 | eeepc_backlight_exit(); |
| 514 | fail_backlight: | 661 | fail_backlight: |
| 515 | return result; | 662 | return result; |
