diff options
author | Corentin Chary <corentincj@iksaif.net> | 2008-03-13 07:57:18 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-04-29 10:08:07 -0400 |
commit | e1faa9da284d14487ed4280b4e87cfde8e1539af (patch) | |
tree | 7bcc0e3d7f44eed7c62103305c1b5bd1d665d731 | |
parent | a5fa429b4b19cccd3f91a98af891c7ba2706cc1d (diff) |
eeepc-laptop: add hwmon fan control
Adds an hwmon interface to control the fan.
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
-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; |