Hi,
So, here's the hwmon patch for ASUS G Series. (Tested on a G1S,
can someone please try it on a G1 , G2 , G2S ?)
But before
- WARNING -
=> DO NOT SWITCH OFF/THROTTLE THE FAN, unless you know what you're doing!
- WARNING -
new interfaces:
see /sys/devices/platform/asus_laptop.0/
temp1_input => CPU temp in °C (note: acpi -t does the same job!)
fan1_input => fanspeed in RPM
pwm1_enable
0 - QFAN (AUTO)
1 - QFAN (MAX, basiclly the same as above)
2 - QFAN Manual Control (DANGEROUS)
3 - Direct Fan Control! (DANGEROUS)
pwm1 - fanspeed (0 - 255) for QFAN Manual Control & Direct Fan Control
e.g:
# direct fan
echo 3 > /sys/devices/platform/asus_laptop.0/pwm1_enable
# ~ 5300 RPM
echo 250 > /sys/devices/platform/asus_laptop.0/pwm1
... (listen to your fan) ...
# back to QFAN
echo 1 > /sys/devices/platform/asus_laptop.0/pwm1_enable
--- a/drivers/misc/asus-laptop.c 2007-07-18 19:48:15.923405210 +0200
+++ b/drivers/misc/asus-laptop.c 2007-07-18 19:41:23.635910285 +0200
@@ -4,6 +4,7 @@
*
* Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
* Copyright (C) 2006-2007 Corentin Chary
+ * Copyright (C) 2007 Christian Lamparter
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,6 +44,9 @@
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/leds.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/string.h>
#include <linux/platform_device.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
@@ -54,7 +58,7 @@
#define ASUS_HOTK_CLASS "hotkey"
#define ASUS_HOTK_DEVICE_NAME "Hotkey"
#define ASUS_HOTK_HID "ATK0100"
-#define ASUS_HOTK_FILE "asus-laptop"
+#define ASUS_HOTK_FILE "asus_laptop"
#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
/*
@@ -169,6 +173,19 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SD
ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
+/* hwmon */
+ASUS_HANDLE(hwmon_cpu, "\\_TZ.RTMP");
+ASUS_HANDLE(hwmon_mfan, "\\_SB.PCI0.SBRG.EC0.WFOV");
+ASUS_HANDLE(hwmon_sfan, "\\_SB.PCI0.SBRG.EC0.SFNV");
+ASUS_HANDLE(hwmon_qfan, ASUS_HOTK_PREFIX "ECRW");
+/*
+ * Alternative
+ * ASUS_HANDLE(hwmon_fan, "\\_SB.PCI0.SBRG.EC0.TACH");
+ */
+
+ASUS_HANDLE(hwmon_fan, "\\_TZ.RFAN");
+
+
/*
* This is the main structure, we can use it to store anything interesting
* about the hotk device
@@ -245,6 +262,8 @@ ASUS_LED(rled, "record");
ASUS_LED(pled, "phone");
ASUS_LED(gled, "gaming");
+typedef enum { SHOW_NAME, SHOW_LABEL, SHOW_INPUT, SHOW_FAN, SHOW_FAN_MODE, STORE_FAN_SPEED } SHOW;
+
/*
* This function evaluates an ACPI method, given an int as parameter, the
* method is searched within the scope of the handle, can be NULL. The output
@@ -709,6 +728,220 @@ static ssize_t store_gps(struct device *
return store_status(buf, count, NULL, GPS_ON);
}
+static int read_temperature(void)
+{
+ ulong value = 0;
+ acpi_status rv = AE_OK;
+
+ if (!hwmon_cpu_handle)
+ return 0;
+
+ rv = acpi_evaluate_integer(hwmon_cpu_handle, NULL,
+ NULL, &value);
+ if (ACPI_FAILURE(rv))
+ printk(ASUS_WARNING "Error reading temperature\n");
+
+ return value;
+}
+
+enum { MODE_QFAN_AUTO, MODE_QFAN_MAX, MODE_QFAN_MANUAL, MODE_DFAN } fan_modes;
+static int fan_mode = MODE_QFAN_AUTO;
+static int manual_fan_speed = 140;
+
+static int get_fan_speed(int fan_nr)
+{
+ struct acpi_object_list params; //list of input parameters (an int here)
+ union acpi_object in_obj; //the only param we use
+ ulong value = 0;
+ acpi_status rv = AE_OK;
+
+ if (!hwmon_fan_handle)
+ return 0;
+
+ /*
+ * Fan speed monitoring is disabled by the
+ * Embedded Controller in Manual Mode!
+ */
+ if (fan_mode == MODE_DFAN)
+ return manual_fan_speed * 22 + 224;
+
+ params.count = 1;
+ params.pointer = &in_obj;
+ in_obj.type = ACPI_TYPE_INTEGER;
+ in_obj.integer.value = fan_nr;
+
+ rv = acpi_evaluate_integer(hwmon_fan_handle, NULL, ¶ms, &value);
+ if (ACPI_FAILURE(rv))
+ printk(ASUS_WARNING "Error reading fan speed\n");
+
+
+ return value * 100;
+}
+
+static int set_fan_mode(int new_mode)
+{
+ struct acpi_object_list params; //list of input parameters (an int here)
+ union acpi_object in_obj[2]; //the only param we use
+ acpi_status rv = AE_OK;
+
+ params.count = 2;
+ in_obj[0].type = ACPI_TYPE_INTEGER;
+ in_obj[1].type = ACPI_TYPE_INTEGER;
+ params.pointer = &in_obj[0];
+
+ if (!hwmon_sfan_handle)
+ return -1;
+
+ switch(new_mode) {
+ case MODE_QFAN_AUTO:
+ case MODE_QFAN_MAX:
+ case MODE_QFAN_MANUAL:
+ in_obj[0].integer.value = 0;
+ in_obj[1].integer.value = 0;
+ rv = acpi_evaluate_object(hwmon_sfan_handle, NULL,
+ ¶ms, NULL);
+ break;
+
+ case MODE_DFAN:
+ in_obj[0].integer.value = 1;
+ in_obj[1].integer.value = 130;
+ rv = acpi_evaluate_object(hwmon_sfan_handle, NULL,
+ ¶ms, NULL);
+ msleep(50);
+
+ in_obj[0].integer.value = 2;
+ rv |= acpi_evaluate_object(hwmon_sfan_handle, NULL,
+ ¶ms, NULL);
+ break;
+ }
+
+ if (ACPI_FAILURE(rv)) {
+ printk(ASUS_WARNING "Error setting fanmode\n");
+ return -1;
+ }
+
+ fan_mode = new_mode;
+
+ return 0;
+}
+
+static int set_fan_speed(int fan, int speed)
+{
+ struct acpi_object_list params;
+ union acpi_object in_obj[2];
+ acpi_status rv = AE_OK;
+
+ in_obj[0].type = ACPI_TYPE_INTEGER;
+ in_obj[1].type = ACPI_TYPE_INTEGER;
+ params.count = 1;
+ params.pointer = &in_obj[0];
+
+ switch (fan_mode) {
+ case MODE_QFAN_AUTO:
+ case MODE_QFAN_MAX:
+ if (!hwmon_qfan_handle)
+ break;
+ in_obj[0].integer.value = fan_mode;
+ rv = acpi_evaluate_object(hwmon_qfan_handle,
+ ASUS_HOTK_PREFIX "QMOD", ¶ms, NULL);
+ break;
+
+ case MODE_QFAN_MANUAL:
+ if (!hwmon_qfan_handle)
+ break;
+ in_obj[0].integer.value = 0x98b6 | (speed << 0x10);
+ rv = acpi_evaluate_object(hwmon_qfan_handle,
+ NULL, ¶ms, NULL);
+
+ break;
+
+ case MODE_DFAN:
+ if ((!hwmon_mfan_handle) || (!hwmon_sfan_handle))
+ break;
+ params.count = 2;
+
+ /*
+ * The current G1S DSDT has a bug,
+ * we have to reset the mode each time we want
+ * to change it
+ */
+ if (set_fan_mode(MODE_QFAN_AUTO))
+ return -1;
+
+ in_obj[0].integer.value = fan;
+ in_obj[1].integer.value = speed;
+ rv |= acpi_evaluate_object(hwmon_mfan_handle,
+ NULL, ¶ms, NULL);
+
+ // wait for the EC to catch up.
+ msleep(150);
+
+ if (set_fan_mode(MODE_DFAN))
+ return -1;
+
+ manual_fan_speed = speed;
+ break;
+ }
+
+ if (ACPI_FAILURE(rv))
+ printk(ASUS_WARNING "Error setting Fanspeed\n");
+ return 0;
+}
+
+static ssize_t show_cpu_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", read_temperature() * 1000);
+}
+static ssize_t store_fan_speed(struct device *dev, struct device_attribute *devattr,
+ const char* buf, size_t count)
+{
+ unsigned int value;
+
+ if (parse_arg(buf, count, &value) < 0)
+ return -EINVAL;
+
+ set_fan_speed(0, value);
+
+ return count;
+}
+
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", get_fan_speed(0));
+}
+
+static ssize_t show_fan_mode(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", fan_mode);
+}
+
+static ssize_t store_fan_mode(struct device *dev, struct device_attribute *devattr,
+ const char* buf, size_t count)
+{
+ unsigned int value;
+
+ if (parse_arg(buf, count, &value) < 0)
+ return -EINVAL;
+
+ set_fan_mode(value);
+
+ return count;
+}
+
+
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ if (attr->index == SHOW_LABEL)
+ return sprintf(buf, "CPU-Temp\n");
+
+ return sprintf(buf, "%s\n", ASUS_HOTK_FILE);
+}
+
static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
{
/* TODO Find a better way to handle events count. */
@@ -737,7 +970,8 @@ static void asus_hotk_notify(acpi_handle
struct device_attribute dev_attr_##_name = { \
.attr = { \
.name = __stringify(_name), \
- .mode = 0 }, \
+ .mode = 0, \
+ .owner = THIS_MODULE }, \
.show = NULL, \
.store = NULL, \
}
@@ -749,6 +983,13 @@ static void asus_hotk_notify(acpi_handle
dev_attr_##_name.store = _store; \
} while(0)
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_cpu_temp, NULL, SHOW_INPUT);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_speed, NULL, SHOW_FAN);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_fan_mode, store_fan_mode, SHOW_FAN_MODE);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR, NULL, store_fan_speed, STORE_FAN_SPEED);
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
+
static ASUS_CREATE_DEVICE_ATTR(infos);
static ASUS_CREATE_DEVICE_ATTR(wlan);
static ASUS_CREATE_DEVICE_ATTR(bluetooth);
@@ -767,6 +1008,12 @@ static struct attribute *asuspf_attribut
&dev_attr_ls_switch.attr,
&dev_attr_ls_level.attr,
&dev_attr_gps.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
NULL
};
@@ -782,6 +1029,7 @@ static struct platform_driver asuspf_dri
};
static struct platform_device *asuspf_device;
+static struct class_device *asushw_device;
static void asus_hotk_add_fs(void)
{
@@ -943,6 +1191,12 @@ static int asus_hotk_get_info(void)
ASUS_HANDLE_INIT(gps_off);
ASUS_HANDLE_INIT(gps_status);
+ ASUS_HANDLE_INIT(hwmon_cpu);
+ ASUS_HANDLE_INIT(hwmon_fan);
+ ASUS_HANDLE_INIT(hwmon_qfan);
+ ASUS_HANDLE_INIT(hwmon_mfan);
+ ASUS_HANDLE_INIT(hwmon_sfan);
+
kfree(model);
return AE_OK;
@@ -1085,6 +1339,10 @@ static void asus_led_exit(void)
static void __exit asus_laptop_exit(void)
{
+ set_fan_mode(MODE_QFAN_MAX);
+ if (asushw_device)
+ hwmon_device_unregister(asushw_device);
+
asus_backlight_exit();
asus_led_exit();
@@ -1200,7 +1458,7 @@ static int __init asus_laptop_init(void)
if (result)
goto fail_platform_driver;
- asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
+ asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, 0);
if (!asuspf_device) {
result = -ENOMEM;
goto fail_platform_device1;
@@ -1215,6 +1473,18 @@ static int __init asus_laptop_init(void)
if (result)
goto fail_sysfs;
+ // Check if it's a G Serie
+ if (hotk->name[0] == 'G') {
+ printk(ASUS_INFO "register hwmon interface\n");
+ asushw_device = hwmon_device_register(&asuspf_device->dev);
+ if (IS_ERR(asushw_device)) {
+ result = PTR_ERR(asushw_device);
+ asushw_device = NULL;
+ printk(ASUS_ERR "Unable to register hwmon device\n");
+ }
+ set_fan_mode(MODE_QFAN_MAX);
+ }
+
return 0;
fail_sysfs:
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Acpi4asus-user mailing list
Acpi4asus-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/acpi4asus-user