linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] ACPI based hwmon driver for ASUS
@ 2007-05-13 22:33 Luca Tettamanti
  2007-05-21 21:29 ` [lm-sensors] " Rudolf Marek
  0 siblings, 1 reply; 10+ messages in thread
From: Luca Tettamanti @ 2007-05-13 22:33 UTC (permalink / raw)
  To: lm-sensors; +Cc: linux-kernel, linux-acpi

Hi,
this is the first attempt to write a hwmon driver for ASUS motherboards
that uses ACPI methods instead of directly touching the sensor chip.
If you haven't followed the discussion[1] we want to avoid concurrent
access to the monitoring hw by ACPI and native driver, since it may
cause a malfunction.

A brief description of the driver:
- I've extended the hwmon userspace interface with *_name attributes;
  since ACPI "knows" the wiring and provides a pretty description of
  the reading I thought it's worth exposing it.
- all the attributes are read only: for some of them (voltage,
  temperature) rw doesn't make sense; for FAN: it's not possible to
  directly control the PWM (the native driver can do that) via ACPI;
  ASUS provides "Q-FAN" which has 3 settings: "silent", "perfomance",
  "auto"; so far I've been unable to make it work. The write path is
  basically "write caller supplied magic number to a magic memory
  location"...
- various ASUS stuff: my motherboard also has other features like
  auto-overclock, PCI-E frequency readings, DRAM voltage and frequency,
  CPU clock / ratio (some of the writable at runtime). I've ignored them
  since I don't know how they work (magic numbers...)
- I exported a few ACPI functions: acpi_ns_map_handle_to_node,
  acpi_ns_convert_entry_to_handle and acpi_ns_get_node for inspecting
  ACPI methods and ensure that all the expected stuff is there;
  acpi_evaluate_object_typed since it's very handy to let it do the
  check on the returned data instead of open-coding it all over the
  driver. Patch against 2.6.21:

diff --git a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/namespace/nsutils.c
index 90fd059..97e1139 100644
--- a/drivers/acpi/namespace/nsutils.c
+++ b/drivers/acpi/namespace/nsutils.c
@@ -700,6 +700,7 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle)
 
 	return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
 }
+EXPORT_SYMBOL(acpi_ns_map_handle_to_node);
 
 /*******************************************************************************
  *
@@ -736,6 +737,7 @@ acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node)
 	return ((acpi_handle) Node);
 ------------------------------------------------------*/
 }
+EXPORT_SYMBOL(acpi_ns_convert_entry_to_handle);
 
 /*******************************************************************************
  *
@@ -875,6 +877,7 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
 	ACPI_FREE(internal_path);
 	return_ACPI_STATUS(status);
 }
+EXPORT_SYMBOL(acpi_ns_get_node);
 
 /*******************************************************************************
  *
diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c
index 8904d0f..1b81758 100644
--- a/drivers/acpi/namespace/nsxfeval.c
+++ b/drivers/acpi/namespace/nsxfeval.c
@@ -49,7 +49,6 @@
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsxfeval")
 
-#ifdef ACPI_FUTURE_USAGE
 /*******************************************************************************
  *
  * FUNCTION:    acpi_evaluate_object_typed
@@ -142,7 +141,6 @@ acpi_evaluate_object_typed(acpi_handle handle,
 }
 
 ACPI_EXPORT_SYMBOL(acpi_evaluate_object_typed)
-#endif				/*  ACPI_FUTURE_USAGE  */
 
 /*******************************************************************************
  *
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index e08f7df..85da346 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -164,14 +164,12 @@ acpi_evaluate_object(acpi_handle object,
 		     struct acpi_object_list *parameter_objects,
 		     struct acpi_buffer *return_object_buffer);
 
-#ifdef ACPI_FUTURE_USAGE
 acpi_status
 acpi_evaluate_object_typed(acpi_handle object,
 			   acpi_string pathname,
 			   struct acpi_object_list *external_params,
 			   struct acpi_buffer *return_buffer,
 			   acpi_object_type return_type);
-#endif
 
 acpi_status
 acpi_get_object_info(acpi_handle handle, struct acpi_buffer *return_buffer);


I've asked for help to local LUG: the driver has been tested by a couple
of people; unfortunately the board were all very similar (a P5B, a P5B
delux and my P5B-E). If you have a recent ASUS mainboard and the driver
doesn't work you can send me a dump of your DSDT.

Now the driver itself:

/*
 * Copyright (C) 2007 Luca Tettamanti <kronos.it@gmail.com>
 * Distribute under GPLv2.
 */

#define DEBUG

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hwmon.h>

#include <acpi/acpi.h>
#include <acpi/acnamesp.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>


#define ATK_HID "ATK0110"
#define ATK_DRV "atk-hwmon"
#define ASOC "_SB.PCI0.SBRG.ASOC"

struct atk_data {
	struct class_device *class_dev;
	acpi_handle atk_handle;
	struct acpi_device *device;

	acpi_handle rtmp_handle;
	acpi_handle rvlt_handle;
	acpi_handle rfan_handle;
} atk_data;


typedef ssize_t (*sysfs_show_func)(struct device *dev,
			struct device_attribute *attr, char *buf);

typedef ssize_t (*sysfs_store_func)(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count);


static void atk_init_attribute(struct device_attribute *attr, char *name,
		mode_t mode, sysfs_show_func show, sysfs_store_func store)
{
	attr->attr.name = name;
	attr->attr.mode = mode;
	attr->show = show;
	attr->store = store;
}

#define ATTR_NAME_SIZE 16 /* Worst case is "tempN_input" */

struct atk_temp {
	struct device_attribute name_attr;
	struct device_attribute input_attr;
	struct device_attribute max_attr;
	struct device_attribute crit_attr;
	char name_attr_name[ATTR_NAME_SIZE];
	char input_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	char crit_attr_name[ATTR_NAME_SIZE];
	/* id is used for the ACPI ID of the temp */
	int id;
	acpi_handle handle;
	char *acpi_name;
};

struct atk_temp_list {
	int count;
	struct atk_temp temp[];
};

#define input_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, input_attr)

static ssize_t atk_temp_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = input_to_atk_temp(attr);
	unsigned long temp;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;
	ssize_t count;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rtmp_handle, NULL, &params, &temp);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	/* ACPI returns centidegree */
	count = sprintf(buf, "%lu\n", temp * 10);

	return count;
}

#define name_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, name_attr)

static ssize_t atk_temp_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = name_to_atk_temp(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

enum atk_temp_pack_id {
	ATK_TEMP_PACK_MAX = 2,
	ATK_TEMP_PACK_CRIT = 3,
};

static int atk_temp_pack_read(acpi_handle handle, int pack_id,
		enum atk_temp_pack_id temp_id, unsigned long *temp)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception: %s\n",
				__func__, acpi_format_exception(status));
		return -EIO;
	}

	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[temp_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, temp_id, obj->type);
		err = -EIO;
		goto out;
	}

	*temp = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define max_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, max_attr)

static ssize_t atk_temp_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = max_to_atk_temp(attr);
	unsigned long temp;

	if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_MAX, &temp))
		return -EIO;

	return sprintf(buf, "%ld\n", temp * 10);
}

#define crit_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, crit_attr)

static ssize_t atk_temp_crit_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = crit_to_atk_temp(attr);
	unsigned long temp;

	if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_CRIT, &temp))
		return -EIO;

	return sprintf(buf, "%ld\n", temp * 10);
}

struct atk_voltage {
	struct device_attribute input_attr;
	struct device_attribute name_attr;
	struct device_attribute min_attr;
	struct device_attribute max_attr;
	char name_attr_name[ATTR_NAME_SIZE];
	char input_attr_name[ATTR_NAME_SIZE];
	char min_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	int id;
	acpi_handle handle;
	char const *acpi_name;
};

struct atk_voltage_list {
	int count;
	struct atk_voltage voltage[];
};

#define name_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, name_attr)

static ssize_t atk_voltage_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = name_to_atk_voltage(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

#define input_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, input_attr)

static ssize_t atk_voltage_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = input_to_atk_voltage(attr);
	unsigned long voltage;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rvlt_handle, NULL, &params, &voltage);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	return sprintf(buf, "%lu\n", voltage);
}

enum atk_voltage_pack_id {
	ATK_VOLTAGE_PACK_MIN = 2,
	ATK_VOLTAGE_PACK_MAX = 3,
};

static int atk_voltage_pack_read(acpi_handle handle, int pack_id,
		enum atk_voltage_pack_id volt_id, unsigned long *voltage)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[volt_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, volt_id, obj->type);
		err = -EIO;
		goto out;
	}

	*voltage = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define max_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, max_attr)

static ssize_t atk_voltage_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = max_to_atk_voltage(attr);
	unsigned long volt;

	if (atk_voltage_pack_read(a->handle, a->id, ATK_VOLTAGE_PACK_MAX, &volt))
		return -EIO;

	return sprintf(buf, "%lu\n", volt);
}

#define min_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, min_attr)

static ssize_t atk_voltage_min_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = min_to_atk_voltage(attr);
	unsigned long volt;

	if (atk_voltage_pack_read(a->handle, a->id, ATK_VOLTAGE_PACK_MIN, &volt))
		return -EIO;

	return sprintf(buf, "%lu\n", volt);
}

struct atk_fan {
	struct device_attribute input_attr;
	struct device_attribute name_attr;
	struct device_attribute min_attr;
	struct device_attribute max_attr;
	char input_attr_name[ATTR_NAME_SIZE];
	char name_attr_name[ATTR_NAME_SIZE];
	char min_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	int id;
	unsigned long long unknown;
	acpi_handle handle;
	char const *acpi_name;
};

struct atk_fan_list {
	int count;
	struct atk_fan fan[];
};

#define input_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, input_attr)

static ssize_t atk_fan_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = input_to_atk_fan(attr);
	unsigned long rotation;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rfan_handle, NULL, &params, &rotation);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	return sprintf(buf, "%lu\n", rotation);
}

#define name_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, name_attr)

static ssize_t atk_fan_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = name_to_atk_fan(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

enum atk_fan_pack_id {
	ATK_FAN_PACK_MIN = 2,
	ATK_FAN_PACK_MAX = 3,
};


static int atk_fan_pack_read(acpi_handle handle, int pack_id,
		enum atk_fan_pack_id fan_id, unsigned long *rot)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception: %s\n",
				__func__, acpi_format_exception(status));
		return -EIO;
	}
	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[fan_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, fan_id, obj->type);
		err = -EIO;
		goto out;
	}

	*rot = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define min_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, min_attr)

static ssize_t atk_fan_min_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = min_to_atk_fan(attr);
	unsigned long rot;

	if (atk_fan_pack_read(a->handle, a->id, ATK_FAN_PACK_MIN, &rot))
		return -EIO;

	return sprintf(buf, "%ld\n", rot);
}

#define max_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, max_attr)

static ssize_t atk_fan_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = max_to_atk_fan(attr);
	unsigned long rot;

	if (atk_fan_pack_read(a->handle, a->id, ATK_FAN_PACK_MAX, &rot))
		return -EIO;

	return sprintf(buf, "%ld\n", rot);;
}

static ssize_t atk_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "atk0110-0\n");
}

static struct device_attribute atk_name_attr = __ATTR(name, 0444, atk_name_show, NULL);

struct atk_temp_list *temp_list;
struct atk_voltage_list *voltage_list;
struct atk_fan_list *fan_list;

static int atk_add(struct acpi_device *device);
static int atk_remove(struct acpi_device *device, int type);

static struct acpi_driver atk_driver = {
	.name	= ATK_HID,
	.class	= "hwmon",
	.ids	= ATK_HID,
	.ops	= {
		.add	= atk_add,
		.remove	= atk_remove,
	},
};

static int atk_create_files(struct device *dev)
{
	int i;
	int ret;

	/* Temperatures */
	for (i = 0; i < temp_list->count; i++) {
		ret = device_create_file(dev, &temp_list->temp[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].name_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].max_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].crit_attr);
		if (ret)
			return ret;
	}

	/* Voltages */
	for (i = 0; i < voltage_list->count; i++) {
		ret = device_create_file(dev, &voltage_list->voltage[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].name_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].min_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].max_attr);
		if (ret)
			return ret;
	}

	/* Fans */
	for (i = 0; i < fan_list->count; i++) {
		ret = device_create_file(dev, &fan_list->fan[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].name_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].min_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].max_attr);
		if (ret)
			return ret;
	}

	ret = device_create_file(dev, &atk_name_attr);

	return ret;
}

static void atk_remove_files(struct device *dev)
{
	int i;

	/* Temperatures */
	if (temp_list) {
		for (i = 0; i < temp_list->count; i++) {
			device_remove_file(dev, &temp_list->temp[i].input_attr);
			device_remove_file(dev, &temp_list->temp[i].name_attr);
			kfree(temp_list->temp[i].acpi_name);
			device_remove_file(dev, &temp_list->temp[i].max_attr);
			device_remove_file(dev, &temp_list->temp[i].crit_attr);
		}
	}
	kfree(temp_list);
	temp_list = NULL;

	/* Voltages */
	if (voltage_list) {
		for (i = 0; i < voltage_list->count; i++) {
			device_remove_file(dev, &voltage_list->voltage[i].input_attr);
			device_remove_file(dev, &voltage_list->voltage[i].name_attr);
			kfree(voltage_list->voltage[i].acpi_name);
			device_remove_file(dev, &voltage_list->voltage[i].min_attr);
			device_remove_file(dev, &voltage_list->voltage[i].max_attr);
		}
	}
	kfree(voltage_list);
	voltage_list = NULL;

	/* Fans */
	if (fan_list) {
		for (i = 0; i < fan_list->count; i++) {
			device_remove_file(dev, &fan_list->fan[i].input_attr);
			device_remove_file(dev, &fan_list->fan[i].name_attr);
			kfree(fan_list->fan[i].acpi_name);
			device_remove_file(dev, &fan_list->fan[i].min_attr);
			device_remove_file(dev, &fan_list->fan[i].max_attr);
		}
	}
	kfree(fan_list);
	voltage_list = NULL;

	device_remove_file(dev, &atk_name_attr);
}

static int atk_enumerate_temp(struct acpi_buffer *temp_buf)
{
	struct atk_temp_list *tmp;
	union acpi_object *pack;
	union acpi_object *obj;
	struct device *dev = &atk_data.device->dev;
	int i, ret;

	/* Result must be a package */
	pack = temp_buf->pointer;

	if (pack->package.count < 1) {
		dev_dbg(dev, "%s: temp package is too small: %d\n", __func__,
				pack->package.count);
		return -EINVAL;
	}

	/* First field is the number of available readings */
	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_dbg(dev, "%s: temp package: invalid type for "
				"element 0: %d\n", __func__, obj->type);
		return -EINVAL;
	}

	/* Sanity check */
	if (pack->package.count != obj->integer.value + 1) {
		dev_dbg(dev, "%s: temperature count (%llu) differs "
				"from package count (%u)\n", __func__,
				obj->integer.value, pack->package.count);
		return -EINVAL;
	}

	tmp = kzalloc(sizeof(*tmp) + sizeof(*tmp->temp) * obj->integer.value, GFP_KERNEL);
	if (!tmp)
		return -ENOMEM;

	tmp->count = obj->integer.value;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buf;
		union acpi_object *temp_pack;
		union acpi_object *name;
		union acpi_object *tmax;
		union acpi_object *tcrit;
		acpi_status status;

		obj = &pack->package.elements[i + 1];

		/* obj is a handle to the temperature package */
		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type for element %d: %d\n",
					__func__, i, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		buf.length = ACPI_ALLOCATE_BUFFER;
		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buf, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_dbg(dev, "%s: ACPI exception on object %u: %s\n",
					__func__, i + 1, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Temperature package:
		 * byte buffer?
		 *   [3]: used by GITM/SITM to locate correct GITx/SITx,
		 *        method, same as the other package (GPID)
		 *   [2]: same as the other package, seems unused in
		 *        GITM/STIM
		 *   [1]: type?
		 *        1: cpu freq?
		 *        2: voltage
		 *        3: temperature
		 *        4: fan
		 *        7: Q-FAN (alarm?)
		 *        8: NOS
		 *   [0]: used by GITx/SITx for demux; selects the
		 *        value stored in ASB1 (PRM0)
		 * description
		 * max
		 * critical
		 * unknown
		 */
		temp_pack = buf.pointer;

		if (temp_pack->package.count != 5) {
			dev_dbg(dev, "%s: invalid package count "
					"for object %d: %u\n", __func__,
					i + 1, temp_pack->package.count);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		name = &temp_pack->package.elements[1];
		tmax = &temp_pack->package.elements[2];
		tcrit = &temp_pack->package.elements[3];

		if (name->type != ACPI_TYPE_STRING) {
			dev_dbg(dev, "%s: object %d, string expected, got %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		if (tmax->type != ACPI_TYPE_INTEGER) {
			dev_dbg(dev, "%s: object %d, int expected (tmax), got: %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (tcrit->type != ACPI_TYPE_INTEGER) {
			dev_dbg(dev, "%s: object %d, int expected (tcrit), got: %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		tmp->temp[i].id = i;
		tmp->temp[i].handle = obj->reference.handle;

		snprintf(tmp->temp[i].input_attr_name, ATTR_NAME_SIZE, "temp%d_input", i);
		atk_init_attribute(&tmp->temp[i].input_attr, tmp->temp[i].input_attr_name,
				0444, atk_temp_input_show, NULL);

		tmp->temp[i].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);
		snprintf(tmp->temp[i].name_attr_name, ATTR_NAME_SIZE, "temp%d_name", i);
		atk_init_attribute(&tmp->temp[i].name_attr, tmp->temp[i].name_attr_name,
				0444, atk_temp_name_show, NULL);

		snprintf(tmp->temp[i].max_attr_name, ATTR_NAME_SIZE, "temp%d_max", i);
		atk_init_attribute(&tmp->temp[i].max_attr, tmp->temp[i].max_attr_name,
				0444, atk_temp_max_show, NULL);

		snprintf(tmp->temp[i].crit_attr_name, ATTR_NAME_SIZE, "temp%d_crit", i);
		atk_init_attribute(&tmp->temp[i].crit_attr, tmp->temp[i].crit_attr_name,
				0444, atk_temp_crit_show, NULL);

		dev_dbg(dev, "temp %u: %s [%llu-%llu]\n", tmp->temp[i].id,
				tmp->temp[i].acpi_name,
				tmax->integer.value, tcrit->integer.value);

		ACPI_FREE(buf.pointer);
	}

	temp_list = tmp;

	return 0;
cleanup:
	for (i = 0; i < tmp->count; i++)
		kfree(tmp->temp[i].acpi_name);
	kfree(tmp);

	return ret;
}

static int atk_enumerate_voltage(struct acpi_buffer *vlt_buf)
{
	struct device *dev = &atk_data.device->dev;
	union acpi_object *pack;
	union acpi_object *obj;
	struct atk_voltage_list *vlt;
	int ret, i;

	pack = vlt_buf->pointer;

	/* At least one element is expected */
	if (pack->package.count < 1) {
		dev_warn(dev, "%s: voltage pack is too small: %d\n", __func__,
				pack->package.count);
		return -EINVAL;
	}

	/* First field is the number of available readings */
	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(dev, "%s: voltage pack: invalid type for element 0: %d\n",
				__func__, obj->type);
	}

	if (obj->integer.value + 1 != pack->package.count) {
		dev_warn(dev, "%s: invalid voltage count %llu (should be %d)\n", __func__,
			obj->integer.value, pack->package.count - 1);
		return -EINVAL;
	}

	vlt = kzalloc(sizeof(*vlt) + sizeof(*vlt->voltage) * obj->integer.value, GFP_KERNEL);
	if (!vlt)
		return -ENOMEM;

	vlt->count = obj->integer.value;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buffer;
		union acpi_object *voltage_pack;
		union acpi_object *name;
		union acpi_object *vmax;
		union acpi_object *vmin;
		acpi_status status;

		obj = &pack->package.elements[i + 1];

		/* handle to voltage package */
		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type for element %d: %d\n",
					__func__, i, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		buffer.length = ACPI_ALLOCATE_BUFFER;
		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buffer, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_warn(dev, "%s: ACPI exception while evaluating object %d: %s\n",
					__func__, i, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Package:
		 * id
		 * description
		 * min
		 * max
		 * One
		 */
		voltage_pack = buffer.pointer;

		if (voltage_pack->package.count != 5) {
			dev_warn(dev, "%s: invalid package for object %d: %d\n",
					__func__, i, voltage_pack->package.count);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		name = &voltage_pack->package.elements[1];
		vmin = &voltage_pack->package.elements[2];
		vmax = &voltage_pack->package.elements[3];

		if (name->type != ACPI_TYPE_STRING) {
			dev_warn(dev, "%s: object %d, string expected, got %d\n",
					__func__, i, name->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (vmax->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (vmax), got: %d\n",
					__func__, i, vmax->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (vmin->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (vmin), got: %d\n",
					__func__, i, vmin->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		vlt->voltage[i].id = i;
		vlt->voltage[i].handle = obj->reference.handle;
		vlt->voltage[i].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);

		snprintf(vlt->voltage[i].input_attr_name, ATTR_NAME_SIZE, "in%d_input", i);
		atk_init_attribute(&vlt->voltage[i].input_attr,
				vlt->voltage[i].input_attr_name,
				0444, atk_voltage_input_show, NULL);

		snprintf(vlt->voltage[i].name_attr_name, ATTR_NAME_SIZE, "in%d_name", i);
		atk_init_attribute(&vlt->voltage[i].name_attr,
				vlt->voltage[i].name_attr_name,
				0444, atk_voltage_name_show, NULL);

		snprintf(vlt->voltage[i].max_attr_name, ATTR_NAME_SIZE, "in%d_max", i);
		atk_init_attribute(&vlt->voltage[i].max_attr,
				vlt->voltage[i].max_attr_name,
				0444, atk_voltage_max_show, NULL);

		snprintf(vlt->voltage[i].min_attr_name, ATTR_NAME_SIZE, "in%d_min", i);
		atk_init_attribute(&vlt->voltage[i].min_attr,
				vlt->voltage[i].min_attr_name,
				0444, atk_voltage_min_show, NULL);

		dev_dbg(dev, "voltage %u: %s [%llu-%llu]\n", vlt->voltage[i].id,
				vlt->voltage[i].acpi_name, vmin->integer.value,
				vmax->integer.value);
		ACPI_FREE(buffer.pointer);
	}

	voltage_list = vlt;

	return 0;

cleanup:
	for (i = 0; i < vlt->count; i++)
		kfree(vlt->voltage[i].acpi_name);
	kfree(vlt);

	return ret;
}

static int atk_enumerate_fan(struct acpi_buffer *fan_buf)
{
	struct device *dev = &atk_data.device->dev;
	union acpi_object *pack;
	union acpi_object *obj;
	struct atk_fan_list *fan;
	int ret, i;

	pack = fan_buf->pointer;

	if (pack->package.count < 1) {
		dev_warn(dev, "%s: fan package is too small: %d\n",
				__func__, pack->package.count);
		return -EINVAL;
	}

	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(dev, "%s: fan package: invalid type for element 0: %d\n",
				__func__, obj->type);
		return -EINVAL;
	}

	if (obj->integer.value + 1 != pack->package.count) {
		dev_warn(dev, "%s: invalid fan count %llu (should be %d)\n",
				__func__, obj->integer.value, pack->package.count - 1);
		return -EINVAL;
	}

	fan = kzalloc(sizeof(*fan) + sizeof(*fan->fan) * obj->integer.value, GFP_KERNEL);
	if (!fan)
		return -ENOMEM;

	fan->count = obj->integer.value;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
		union acpi_object *fan_pack;
		union acpi_object *unk;
		union acpi_object *name;
		union acpi_object *fmin;
		union acpi_object *fmax;
		acpi_status status;

		/* handle to fan package */
		obj = &pack->package.elements[i + 1];

		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type type for element %d: %d\n",
					__func__, i + 1, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buffer, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_warn(dev, "%s: ACPI exception while evaluating object %d: %s\n",
					__func__, i + 1, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Fan package:
		 * id (usual stuff)
		 * description
		 * min
		 * max
		 * unkwown
		 */
		fan_pack = buffer.pointer;

		if (fan_pack->package.count != 5) {
			dev_warn(dev, "%s: invalid package len for object %d: %d\n",
					__func__, i + 1, fan_pack->package.count);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		unk = &fan_pack->package.elements[0];
		name = &fan_pack->package.elements[1];
		fmin = &fan_pack->package.elements[2];
		fmax = &fan_pack->package.elements[3];

		if (unk->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (unk), got %d\n",
					__func__, i + 1, unk->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (name->type != ACPI_TYPE_STRING) {
			dev_warn(dev, "%s: object %d, string expected, got %d\n",
					__func__, i + 1, name->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (fmin->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (fmin), got %d\n",
					__func__, i + 1, fmin->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (fmax->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (fmax), got %d\n",
					__func__, i + 1, fmax->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		fan->fan[i].id = i;
		fan->fan[i].handle = obj->reference.handle;
		fan->fan[i].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);
		fan->fan[i].unknown = unk->integer.value;

		snprintf(fan->fan[i].input_attr_name, ATTR_NAME_SIZE, "fan%d_input", i);
		atk_init_attribute(&fan->fan[i].input_attr,
				fan->fan[i].input_attr_name,
				0444, atk_fan_input_show, NULL);

		snprintf(fan->fan[i].name_attr_name, ATTR_NAME_SIZE, "fan%d_name", i);
		atk_init_attribute(&fan->fan[i].name_attr,
				fan->fan[i].name_attr_name,
				0444, atk_fan_name_show, NULL);

		snprintf(fan->fan[i].min_attr_name, ATTR_NAME_SIZE, "fan%d_min", i);
		atk_init_attribute(&fan->fan[i].min_attr,
				fan->fan[i].min_attr_name,
				0444, atk_fan_min_show, NULL);

		snprintf(fan->fan[i].max_attr_name, ATTR_NAME_SIZE, "fan%d_max", i);
		atk_init_attribute(&fan->fan[i].max_attr,
				fan->fan[i].max_attr_name,
				0444, atk_fan_max_show, NULL);

		dev_dbg(dev, "fan %d: %s [%llu-%llu]\n", fan->fan[i].id,
				fan->fan[i].acpi_name, fmin->integer.value,
				fmax->integer.value);
		ACPI_FREE(buffer.pointer);
	}

	fan_list = fan;

	return 0;
cleanup:
	for (i = 0; i < fan->count; i++)
		kfree(fan->fan[i].acpi_name);
	kfree(fan);

	return ret;
}

static int atk_add(struct acpi_device *device)
{
	acpi_status ret;
	int err, i;
	struct acpi_buffer buf;
	union acpi_object *obj;
	struct acpi_namespace_node *search_ns;
	struct acpi_namespace_node *ns;

	dev_dbg(&device->dev, "atk: adding...\n");

	atk_data.device = device;
	atk_data.atk_handle = device->handle;

	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "MBIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "atk: method MBIF not found\n");
		return -ENODEV;
	}

	obj = buf.pointer;
	if (obj->package.count >= 2 && obj->package.elements[1].type == ACPI_TYPE_STRING) {
		dev_dbg(&device->dev, "board ID = %s\n",
				obj->package.elements[1].string.pointer);
	}
	ACPI_FREE(buf.pointer);

	/* Check for hwmon methods */
	search_ns = acpi_ns_map_handle_to_node(device->handle);
	if (!search_ns)
		return -ENODEV;

	/* RTMP: read temperature */
	ret = acpi_ns_get_node(search_ns, "RTMP", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RTMP not found\n");
		return -ENODEV;
	}
	atk_data.rtmp_handle = acpi_ns_convert_entry_to_handle(ns);

	/* RVLT: read voltage */
	ret = acpi_ns_get_node(search_ns, "RVLT", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RVLT not found\n");
		return -ENODEV;
	}
	atk_data.rvlt_handle = acpi_ns_convert_entry_to_handle(ns);

	/* RFAN: read fan status */
	ret = acpi_ns_get_node(search_ns, "RFAN", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RFAN not found\n");
		return -ENODEV;
	}
	atk_data.rfan_handle = acpi_ns_convert_entry_to_handle(ns);

	/* Enumerate temp data - TSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "TSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "TSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));
		return -ENODEV;
	}

	err = atk_enumerate_temp(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		return err;

	/* Enumerate voltage data - VSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "VSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "VSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));

		err = -ENODEV;
		goto cleanup;
	}

	err = atk_enumerate_voltage(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		goto cleanup;

	/* Enumerate fan data - FSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "FSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "TSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));

		err = -ENODEV;
		goto cleanup;
	}

	err = atk_enumerate_fan(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		goto cleanup;

	dev_dbg(&atk_data.device->dev, "registering hwmon device\n");
	atk_data.class_dev = hwmon_device_register(&atk_data.device->dev);
	if (IS_ERR(atk_data.class_dev)) {
		err = PTR_ERR(atk_data.class_dev);
		goto cleanup;
	}

	dev_dbg(&atk_data.device->dev, "populating sysfs directory\n");
	err = atk_create_files(&atk_data.device->dev);
	if (err)
		goto remove;

	acpi_driver_data(device) = &atk_data;

	return 0;
remove:
	atk_remove_files(&atk_data.device->dev);
	hwmon_device_unregister(atk_data.class_dev);

	return err;
cleanup:
	if (temp_list) {
		for (i = 0; i < temp_list->count; i++)
			kfree(temp_list->temp[i].acpi_name);
	}
	kfree(temp_list);

	if (voltage_list) {
		for (i = 0; i < voltage_list->count; i++)
			kfree(voltage_list->voltage[i].acpi_name);
	}
	kfree(voltage_list);

	if (fan_list) {
		for (i = 0; i < fan_list->count; i++)
			kfree(fan_list->fan[i].acpi_name);
	}
	kfree(fan_list);

	temp_list = NULL;
	voltage_list = NULL;
	fan_list = NULL;

	return err;
}

static int atk_remove(struct acpi_device *device, int type)
{
	dev_dbg(&device->dev, "removing...\n");

	acpi_driver_data(device) = NULL;

	hwmon_device_unregister(atk_data.class_dev);
	atk_remove_files(&atk_data.device->dev);

	return 0;
}

int atk_init(void)
{
	int ret;

	ret = acpi_bus_register_driver(&atk_driver);
	if (ret)
		pr_info("atk: acpi_bus_register_driver failed: %d\n", ret);

	return ret;
}

void atk_exit(void)
{
	acpi_bus_unregister_driver(&atk_driver);
}

module_init(atk_init);
module_exit(atk_exit);

MODULE_LICENSE("GPL");


Luca
[1]
http://thread.gmane.org/gmane.linux.drivers.sensors/12454/focus=12486
and:
http://thread.gmane.org/gmane.linux.drivers.sensors/12454/focus=12486
-- 
It can't rain forever,
but you can die before seeing the sun again.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-05-13 22:33 [RFC] ACPI based hwmon driver for ASUS Luca Tettamanti
@ 2007-05-21 21:29 ` Rudolf Marek
  2007-05-21 21:53   ` Luca
  0 siblings, 1 reply; 10+ messages in thread
From: Rudolf Marek @ 2007-05-21 21:29 UTC (permalink / raw)
  To: Luca Tettamanti; +Cc: lm-sensors, linux-acpi, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 931 bytes --]

Hello all,

Sorry for the delay. Glad that someone took a look into this.
ATK0110 ATK0110:00: atk: adding...
ATK0110 ATK0110:00: board ID = A8VE-SE
ATK0110 ATK0110:00: temp 0: CPU Temperature [900-1250]
ATK0110 ATK0110:00: temp 1: MB Temperature [700-1250]
ATK0110 ATK0110:00: voltage 0: Vcore Voltage [1450-1750]
ATK0110 ATK0110:00: voltage 1:  +3.3 Voltage [3000-3600]
ATK0110 ATK0110:00: voltage 2:  +5.0 Voltage [4500-5500]
ATK0110 ATK0110:00: voltage 3: +12.0 Voltage [11200-13200]
ATK0110 ATK0110:00: atk_enumerate_fan: invalid fan count 3 (should be 5)
ATK0110: probe of ATK0110:00 failed with error -22
ruik@ruik:~$

Not 100% success but at least something ;). I did some research on this some 
time ago (2005):
http://lists.lm-sensors.org/pipermail/lm-sensors/2005-October/014050.html


I'm attaching my dsdt.bin. Also keep in mind that we will need some 
"synchronizer" for the ACPI versus other drivers.

Thanks,
Rudolf

[-- Attachment #2: dsdt.bin --]
[-- Type: application/octet-stream, Size: 31026 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-05-21 21:29 ` [lm-sensors] " Rudolf Marek
@ 2007-05-21 21:53   ` Luca
  2007-05-22  6:19     ` Rudolf Marek
  0 siblings, 1 reply; 10+ messages in thread
From: Luca @ 2007-05-21 21:53 UTC (permalink / raw)
  To: Rudolf Marek; +Cc: lm-sensors, linux-acpi, linux-kernel

On 5/21/07, Rudolf Marek <r.marek@assembler.cz> wrote:
> ATK0110 ATK0110:00: atk_enumerate_fan: invalid fan count 3 (should be 5)
> ATK0110: probe of ATK0110:00 failed with error -22

Ok, the FAN package contains 5 items, but the enumerator only
advertises the first 3. I guess that the other 2 (chipset fan and
chassis2 fan) are not usable? Are the connectors for those two fans on
your motherboard? Do you get any reading from them?

Plus: your ACPI code uses the full "magic number" (first item in the
package) to demux the reading, while my board masks the upper 2 bytes;
I need to change the driver. Will send another iteration.

Luca

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-05-21 21:53   ` Luca
@ 2007-05-22  6:19     ` Rudolf Marek
  2007-06-02 22:28       ` Luca Tettamanti
  0 siblings, 1 reply; 10+ messages in thread
From: Rudolf Marek @ 2007-05-22  6:19 UTC (permalink / raw)
  To: Luca; +Cc: lm-sensors, linux-acpi, linux-kernel

Hi,

I have following readings:

w83627ehf-isa-0290
Adapter: ISA adapter
VCore:     +1.52 V  (min =  +0.00 V, max =  +1.74 V)
in1:      +12.30 V  (min = +13.46 V, max = +13.04 V) ALARM
AVCC:      +3.36 V  (min =  +4.08 V, max =  +3.95 V) ALARM
3VCC:      +3.36 V  (min =  +4.05 V, max =  +3.06 V) ALARM
in4:       +2.04 V  (min =  +1.78 V, max =  +2.04 V)
in5:       +1.60 V  (min =  +2.04 V, max =  +2.02 V) ALARM
in6:       +5.12 V  (min =  +6.12 V, max =  +6.53 V) ALARM
VSB:       +3.36 V  (min =  +4.08 V, max =  +4.08 V) ALARM
VBAT:      +3.30 V  (min =  +4.08 V, max =  +3.04 V) ALARM
in9:       +1.65 V  (min =  +0.98 V, max =  +2.04 V)
Case Fan:    0 RPM  (min =    0 RPM, div = 8)
CPU Fan:  1638 RPM  (min =    0 RPM, div = 4)
Aux Fan:  1436 RPM  (min = 4272 RPM, div = 4) ALARM
fan5:        0 RPM  (min =    0 RPM, div = 16)
Sys Temp:    +28°C  (high =   -65°C, hyst =   -34°C)   ALARM
CPU Temp:  +34.0°C  (high = +80.0°C, hyst = +75.0°C)
AUX Temp:  +38.5°C  (high = +80.0°C, hyst = +75.0°C)

Fan4 is disabled in the chip. I think the board has 4 connectors. I dont have 
time right now to check the manual.

Rudolf


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-05-22  6:19     ` Rudolf Marek
@ 2007-06-02 22:28       ` Luca Tettamanti
  2007-06-21 18:49         ` Rudolf Marek
  2007-06-21 19:02         ` Rudolf Marek
  0 siblings, 2 replies; 10+ messages in thread
From: Luca Tettamanti @ 2007-06-02 22:28 UTC (permalink / raw)
  To: Rudolf Marek; +Cc: lm-sensors, linux-acpi, linux-kernel

Il Tue, May 22, 2007 at 08:19:27AM +0200, Rudolf Marek ha scritto: 
> Hi,
> 
> I have following readings:
> 
> w83627ehf-isa-0290
> Adapter: ISA adapter
> VCore:     +1.52 V  (min =  +0.00 V, max =  +1.74 V)
> in1:      +12.30 V  (min = +13.46 V, max = +13.04 V) ALARM
> AVCC:      +3.36 V  (min =  +4.08 V, max =  +3.95 V) ALARM
> 3VCC:      +3.36 V  (min =  +4.05 V, max =  +3.06 V) ALARM
> in4:       +2.04 V  (min =  +1.78 V, max =  +2.04 V)
> in5:       +1.60 V  (min =  +2.04 V, max =  +2.02 V) ALARM
> in6:       +5.12 V  (min =  +6.12 V, max =  +6.53 V) ALARM
> VSB:       +3.36 V  (min =  +4.08 V, max =  +4.08 V) ALARM
> VBAT:      +3.30 V  (min =  +4.08 V, max =  +3.04 V) ALARM
> in9:       +1.65 V  (min =  +0.98 V, max =  +2.04 V)
> Case Fan:    0 RPM  (min =    0 RPM, div = 8)
> CPU Fan:  1638 RPM  (min =    0 RPM, div = 4)
> Aux Fan:  1436 RPM  (min = 4272 RPM, div = 4) ALARM
> fan5:        0 RPM  (min =    0 RPM, div = 16)
> Sys Temp:    +28°C  (high =   -65°C, hyst =   -34°C)   ALARM
> CPU Temp:  +34.0°C  (high = +80.0°C, hyst = +75.0°C)
> AUX Temp:  +38.5°C  (high = +80.0°C, hyst = +75.0°C)
> 
> Fan4 is disabled in the chip. I think the board has 4 connectors. I dont 
> have time right now to check the manual.

Ok, it makes sense :)

Name (FBUF, Package (0x06)
{
    0x03,
    CPUF,
    CHAF,
    PWRF,
    CHPF,
    CH2F
})

Clearly the first number is not the number of available readings (though
it matches the count in the other DSDTs I've seen); don't know what it
is :|

The last field of the individual fan packages is far more interesting: 0
means disabled, anything else means enabled. In your DSDT "CHIPSET FAN
Speed" is marked as disabled and there's no AML code to read the
rotation.
This may be true also for the other packages (temp and voltage), but for
now I've modified only the fan code.

Here's another iteration of the driver, please give it a try:

/*
 * Copyright (C) 2007 Luca Tettamanti <kronos.it@gmail.com>
 * Distribute under GPLv2.
 */

#define DEBUG

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hwmon.h>

#include <acpi/acpi.h>
#include <acpi/acnamesp.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>


#define ATK_HID "ATK0110"
#define ATK_DRV "atk-hwmon"
#define ASOC "_SB.PCI0.SBRG.ASOC"

struct atk_data {
	struct class_device *class_dev;
	acpi_handle atk_handle;
	struct acpi_device *device;

	acpi_handle rtmp_handle;
	acpi_handle rvlt_handle;
	acpi_handle rfan_handle;
} atk_data;


typedef ssize_t (*sysfs_show_func)(struct device *dev,
			struct device_attribute *attr, char *buf);

typedef ssize_t (*sysfs_store_func)(struct device *dev,
			struct device_attribute *attr, const char *buf,
			size_t count);


static void atk_init_attribute(struct device_attribute *attr, char *name,
		mode_t mode, sysfs_show_func show, sysfs_store_func store)
{
	attr->attr.name = name;
	attr->attr.mode = mode;
	attr->show = show;
	attr->store = store;
}

#define ATTR_NAME_SIZE 16 /* Worst case is "tempN_input" */

struct atk_temp {
	struct device_attribute label_attr;
	struct device_attribute input_attr;
	struct device_attribute max_attr;
	struct device_attribute crit_attr;
	char label_attr_name[ATTR_NAME_SIZE];
	char input_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	char crit_attr_name[ATTR_NAME_SIZE];
	u64 id;
	acpi_handle handle;
	char *acpi_name;
};

struct atk_temp_list {
	int count;
	struct atk_temp temp[];
};

#define input_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, input_attr)

static ssize_t atk_temp_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = input_to_atk_temp(attr);
	unsigned long temp;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;
	ssize_t count;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rtmp_handle, NULL, &params, &temp);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	/* ACPI returns centidegree */
	count = sprintf(buf, "%lu\n", temp * 10);

	return count;
}

#define label_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, label_attr)

static ssize_t atk_temp_label_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = label_to_atk_temp(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

enum atk_temp_pack_id {
	ATK_TEMP_PACK_MAX = 2,
	ATK_TEMP_PACK_CRIT = 3,
};

static int atk_temp_pack_read(acpi_handle handle, int pack_id,
		enum atk_temp_pack_id temp_id, unsigned long *temp)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception: %s\n",
				__func__, acpi_format_exception(status));
		return -EIO;
	}

	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[temp_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, temp_id, obj->type);
		err = -EIO;
		goto out;
	}

	*temp = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define max_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, max_attr)

static ssize_t atk_temp_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = max_to_atk_temp(attr);
	unsigned long temp;

	if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_MAX, &temp))
		return -EIO;

	return sprintf(buf, "%ld\n", temp * 10);
}

#define crit_to_atk_temp(attr) \
	container_of(attr, struct atk_temp, crit_attr)

static ssize_t atk_temp_crit_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_temp *a = crit_to_atk_temp(attr);
	unsigned long temp;

	if (atk_temp_pack_read(a->handle, a->id, ATK_TEMP_PACK_CRIT, &temp))
		return -EIO;

	return sprintf(buf, "%ld\n", temp * 10);
}

struct atk_voltage {
	struct device_attribute input_attr;
	struct device_attribute label_attr;
	struct device_attribute min_attr;
	struct device_attribute max_attr;
	char label_attr_name[ATTR_NAME_SIZE];
	char input_attr_name[ATTR_NAME_SIZE];
	char min_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	u64 id;
	acpi_handle handle;
	char const *acpi_name;
};

struct atk_voltage_list {
	int count;
	struct atk_voltage voltage[];
};

#define label_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, label_attr)

static ssize_t atk_voltage_label_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = label_to_atk_voltage(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

#define input_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, input_attr)

static ssize_t atk_voltage_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = input_to_atk_voltage(attr);
	unsigned long voltage;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rvlt_handle, NULL, &params, &voltage);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	return sprintf(buf, "%lu\n", voltage);
}

enum atk_voltage_pack_id {
	ATK_VOLTAGE_PACK_MIN = 2,
	ATK_VOLTAGE_PACK_MAX = 3,
};

static int atk_voltage_pack_read(acpi_handle handle, int pack_id,
		enum atk_voltage_pack_id volt_id, unsigned long *voltage)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[volt_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, volt_id, obj->type);
		err = -EIO;
		goto out;
	}

	*voltage = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define max_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, max_attr)

static ssize_t atk_voltage_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = max_to_atk_voltage(attr);
	unsigned long volt;

	if (atk_voltage_pack_read(a->handle, a->id, ATK_VOLTAGE_PACK_MAX, &volt))
		return -EIO;

	return sprintf(buf, "%lu\n", volt);
}

#define min_to_atk_voltage(attr) \
	container_of(attr, struct atk_voltage, min_attr)

static ssize_t atk_voltage_min_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_voltage *a = min_to_atk_voltage(attr);
	unsigned long volt;

	if (atk_voltage_pack_read(a->handle, a->id, ATK_VOLTAGE_PACK_MIN, &volt))
		return -EIO;

	return sprintf(buf, "%lu\n", volt);
}

struct atk_fan {
	struct device_attribute input_attr;
	struct device_attribute label_attr;
	struct device_attribute min_attr;
	struct device_attribute max_attr;
	char input_attr_name[ATTR_NAME_SIZE];
	char label_attr_name[ATTR_NAME_SIZE];
	char min_attr_name[ATTR_NAME_SIZE];
	char max_attr_name[ATTR_NAME_SIZE];
	u64 id;
	acpi_handle handle;
	char const *acpi_name;
};

struct atk_fan_list {
	int count;
	struct atk_fan fan[];
};

#define input_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, input_attr)

static ssize_t atk_fan_input_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = input_to_atk_fan(attr);
	unsigned long rotation;
	struct acpi_object_list params;
	union acpi_object id;
	acpi_status status;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = a->id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_integer(atk_data.rfan_handle, NULL, &params, &rotation);
	if (status != AE_OK) {
		dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
				acpi_format_exception(status));
		return -EIO;
	}

	return sprintf(buf, "%lu\n", rotation);
}

#define label_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, label_attr)

static ssize_t atk_fan_label_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = label_to_atk_fan(attr);

	return sprintf(buf, "%s\n", a->acpi_name);
}

enum atk_fan_pack_id {
	ATK_FAN_PACK_MIN = 2,
	ATK_FAN_PACK_MAX = 3,
};


static int atk_fan_pack_read(acpi_handle handle, int pack_id,
		enum atk_fan_pack_id fan_id, unsigned long *rot)
{
	struct acpi_buffer ret;
	struct acpi_object_list params;
	union acpi_object id;
	union acpi_object *pack;
	union acpi_object *obj;
	acpi_status status;
	int err = 0;

	ret.length = ACPI_ALLOCATE_BUFFER;

	id.type = ACPI_TYPE_INTEGER;
	id.integer.value = pack_id;

	params.count = 1;
	params.pointer = &id;

	status = acpi_evaluate_object_typed(handle, NULL, &params,
			&ret, ACPI_TYPE_PACKAGE);
	if (status != AE_OK) {
		dev_warn(&atk_data.device->dev, "%s: ACPI exception: %s\n",
				__func__, acpi_format_exception(status));
		return -EIO;
	}
	pack = ret.pointer;

	if (pack->package.count != 5) {
		dev_warn(&atk_data.device->dev, "%s: unexpected package size: %d\n",
				__func__, pack->package.count);
		err = -EIO;
		goto out;
	}

	obj = &pack->package.elements[fan_id];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&atk_data.device->dev, "%s: unexepected object type "
				"for element %d: %d\n", __func__, fan_id, obj->type);
		err = -EIO;
		goto out;
	}

	*rot = obj->integer.value;

out:
	ACPI_FREE(ret.pointer);

	return err;
}

#define min_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, min_attr)

static ssize_t atk_fan_min_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = min_to_atk_fan(attr);
	unsigned long rot;

	if (atk_fan_pack_read(a->handle, a->id, ATK_FAN_PACK_MIN, &rot))
		return -EIO;

	return sprintf(buf, "%ld\n", rot);
}

#define max_to_atk_fan(attr) \
	container_of(attr, struct atk_fan, max_attr)

static ssize_t atk_fan_max_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct atk_fan *a = max_to_atk_fan(attr);
	unsigned long rot;

	if (atk_fan_pack_read(a->handle, a->id, ATK_FAN_PACK_MAX, &rot))
		return -EIO;

	return sprintf(buf, "%ld\n", rot);;
}

static ssize_t atk_name_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "atk0110-0\n");
}

static struct device_attribute atk_name_attr = __ATTR(name, 0444, atk_name_show, NULL);

struct atk_temp_list *temp_list;
struct atk_voltage_list *voltage_list;
struct atk_fan_list *fan_list;

static int atk_add(struct acpi_device *device);
static int atk_remove(struct acpi_device *device, int type);

static struct acpi_driver atk_driver = {
	.name	= ATK_HID,
	.class	= "hwmon",
	.ids	= ATK_HID,
	.ops	= {
		.add	= atk_add,
		.remove	= atk_remove,
	},
};

static int atk_create_files(struct device *dev)
{
	int i;
	int ret;

	/* Temperatures */
	for (i = 0; i < temp_list->count; i++) {
		ret = device_create_file(dev, &temp_list->temp[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].label_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].max_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &temp_list->temp[i].crit_attr);
		if (ret)
			return ret;
	}

	/* Voltages */
	for (i = 0; i < voltage_list->count; i++) {
		ret = device_create_file(dev, &voltage_list->voltage[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].label_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].min_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &voltage_list->voltage[i].max_attr);
		if (ret)
			return ret;
	}

	/* Fans */
	for (i = 0; i < fan_list->count; i++) {
		ret = device_create_file(dev, &fan_list->fan[i].input_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].label_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].min_attr);
		if (ret)
			return ret;
		ret = device_create_file(dev, &fan_list->fan[i].max_attr);
		if (ret)
			return ret;
	}

	ret = device_create_file(dev, &atk_name_attr);

	return ret;
}

static void atk_remove_files(struct device *dev)
{
	int i;

	/* Temperatures */
	if (temp_list) {
		for (i = 0; i < temp_list->count; i++) {
			device_remove_file(dev, &temp_list->temp[i].input_attr);
			device_remove_file(dev, &temp_list->temp[i].label_attr);
			kfree(temp_list->temp[i].acpi_name);
			device_remove_file(dev, &temp_list->temp[i].max_attr);
			device_remove_file(dev, &temp_list->temp[i].crit_attr);
		}
	}
	kfree(temp_list);
	temp_list = NULL;

	/* Voltages */
	if (voltage_list) {
		for (i = 0; i < voltage_list->count; i++) {
			device_remove_file(dev, &voltage_list->voltage[i].input_attr);
			device_remove_file(dev, &voltage_list->voltage[i].label_attr);
			kfree(voltage_list->voltage[i].acpi_name);
			device_remove_file(dev, &voltage_list->voltage[i].min_attr);
			device_remove_file(dev, &voltage_list->voltage[i].max_attr);
		}
	}
	kfree(voltage_list);
	voltage_list = NULL;

	/* Fans */
	if (fan_list) {
		for (i = 0; i < fan_list->count; i++) {
			device_remove_file(dev, &fan_list->fan[i].input_attr);
			device_remove_file(dev, &fan_list->fan[i].label_attr);
			kfree(fan_list->fan[i].acpi_name);
			device_remove_file(dev, &fan_list->fan[i].min_attr);
			device_remove_file(dev, &fan_list->fan[i].max_attr);
		}
	}
	kfree(fan_list);
	voltage_list = NULL;

	device_remove_file(dev, &atk_name_attr);
}

static int atk_enumerate_temp(struct acpi_buffer *temp_buf)
{
	struct atk_temp_list *tmp;
	union acpi_object *pack;
	union acpi_object *obj;
	struct device *dev = &atk_data.device->dev;
	int i, ret;

	/* Result must be a package */
	pack = temp_buf->pointer;

	if (pack->package.count < 1) {
		dev_dbg(dev, "%s: temp package is too small: %d\n", __func__,
				pack->package.count);
		return -EINVAL;
	}

	/* First field is the number of available readings */
	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_dbg(dev, "%s: temp package: invalid type for "
				"element 0: %d\n", __func__, obj->type);
		return -EINVAL;
	}

	/* Sanity check */
	if (pack->package.count != obj->integer.value + 1) {
		dev_dbg(dev, "%s: temperature count (%llu) differs "
				"from package count (%u)\n", __func__,
				obj->integer.value, pack->package.count);
		return -EINVAL;
	}

	tmp = kzalloc(sizeof(*tmp) + sizeof(*tmp->temp) * obj->integer.value, GFP_KERNEL);
	if (!tmp)
		return -ENOMEM;

	tmp->count = obj->integer.value;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buf;
		union acpi_object *temp_pack;
		union acpi_object *name;
		union acpi_object *tmax;
		union acpi_object *tcrit;
		union acpi_object *acpi_id;
		acpi_status status;

		obj = &pack->package.elements[i + 1];

		/* obj is a handle to the temperature package */
		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type for element %d: %d\n",
					__func__, i, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		buf.length = ACPI_ALLOCATE_BUFFER;
		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buf, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_dbg(dev, "%s: ACPI exception on object %u: %s\n",
					__func__, i + 1, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Temperature package:
		 * byte buffer?
		 *   [3]: used by GITM/SITM to locate correct GITx/SITx,
		 *        method, same as the other package (GPID)
		 *   [2]: same as the other package, seems unused in
		 *        GITM/STIM
		 *   [1]: type?
		 *        1: cpu freq?
		 *        2: voltage
		 *        3: temperature
		 *        4: fan
		 *        7: Q-FAN (alarm?)
		 *        8: NOS
		 *   [0]: used by GITx/SITx for demux; selects the
		 *        value stored in ASB1 (PRM0)
		 * description
		 * max
		 * critical
		 * unknown
		 */
		temp_pack = buf.pointer;

		if (temp_pack->package.count != 5) {
			dev_dbg(dev, "%s: invalid package count "
					"for object %d: %u\n", __func__,
					i + 1, temp_pack->package.count);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		acpi_id = &temp_pack->package.elements[0];
		name = &temp_pack->package.elements[1];
		tmax = &temp_pack->package.elements[2];
		tcrit = &temp_pack->package.elements[3];

		if (acpi_id->type != ACPI_TYPE_INTEGER) {
			dev_dbg(dev, "%s: object %d, int expected (id), got: %d\n",
					__func__, i, acpi_id->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (name->type != ACPI_TYPE_STRING) {
			dev_dbg(dev, "%s: object %d, string expected, got %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (tmax->type != ACPI_TYPE_INTEGER) {
			dev_dbg(dev, "%s: object %d, int expected (tmax), got: %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (tcrit->type != ACPI_TYPE_INTEGER) {
			dev_dbg(dev, "%s: object %d, int expected (tcrit), got: %d\n",
					__func__, i, name->type);
			ACPI_FREE(buf.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		tmp->temp[i].id = acpi_id->integer.value;
		tmp->temp[i].handle = obj->reference.handle;

		snprintf(tmp->temp[i].input_attr_name, ATTR_NAME_SIZE, "temp%d_input", i);
		atk_init_attribute(&tmp->temp[i].input_attr, tmp->temp[i].input_attr_name,
				0444, atk_temp_input_show, NULL);

		tmp->temp[i].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);
		snprintf(tmp->temp[i].label_attr_name, ATTR_NAME_SIZE, "temp%d_label", i);
		atk_init_attribute(&tmp->temp[i].label_attr, tmp->temp[i].label_attr_name,
				0444, atk_temp_label_show, NULL);

		snprintf(tmp->temp[i].max_attr_name, ATTR_NAME_SIZE, "temp%d_max", i);
		atk_init_attribute(&tmp->temp[i].max_attr, tmp->temp[i].max_attr_name,
				0444, atk_temp_max_show, NULL);

		snprintf(tmp->temp[i].crit_attr_name, ATTR_NAME_SIZE, "temp%d_crit", i);
		atk_init_attribute(&tmp->temp[i].crit_attr, tmp->temp[i].crit_attr_name,
				0444, atk_temp_crit_show, NULL);

		dev_dbg(dev, "temp %d: %#llx %s [%llu-%llu]\n", i, tmp->temp[i].id,
				tmp->temp[i].acpi_name,
				tmax->integer.value, tcrit->integer.value);

		ACPI_FREE(buf.pointer);
	}

	temp_list = tmp;

	return 0;
cleanup:
	for (i = 0; i < tmp->count; i++)
		kfree(tmp->temp[i].acpi_name);
	kfree(tmp);

	return ret;
}

static int atk_enumerate_voltage(struct acpi_buffer *vlt_buf)
{
	struct device *dev = &atk_data.device->dev;
	union acpi_object *pack;
	union acpi_object *obj;
	struct atk_voltage_list *vlt;
	int ret, i;

	pack = vlt_buf->pointer;

	/* At least one element is expected */
	if (pack->package.count < 1) {
		dev_warn(dev, "%s: voltage pack is too small: %d\n", __func__,
				pack->package.count);
		return -EINVAL;
	}

	/* First field is the number of available readings */
	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(dev, "%s: voltage pack: invalid type for element 0: %d\n",
				__func__, obj->type);
	}

	if (obj->integer.value + 1 != pack->package.count) {
		dev_warn(dev, "%s: invalid voltage count %llu (should be %d)\n", __func__,
			obj->integer.value, pack->package.count - 1);
		return -EINVAL;
	}

	vlt = kzalloc(sizeof(*vlt) + sizeof(*vlt->voltage) * obj->integer.value, GFP_KERNEL);
	if (!vlt)
		return -ENOMEM;

	vlt->count = obj->integer.value;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buffer;
		union acpi_object *voltage_pack;
		union acpi_object *name;
		union acpi_object *vmax;
		union acpi_object *vmin;
		union acpi_object *acpi_id;
		acpi_status status;

		obj = &pack->package.elements[i + 1];

		/* handle to voltage package */
		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type for element %d: %d\n",
					__func__, i, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		buffer.length = ACPI_ALLOCATE_BUFFER;
		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buffer, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_warn(dev, "%s: ACPI exception while evaluating object %d: %s\n",
					__func__, i, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Package:
		 * id
		 * description
		 * min
		 * max
		 * One
		 */
		voltage_pack = buffer.pointer;

		if (voltage_pack->package.count != 5) {
			dev_warn(dev, "%s: invalid package for object %d: %d\n",
					__func__, i, voltage_pack->package.count);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		acpi_id = &voltage_pack->package.elements[0];
		name = &voltage_pack->package.elements[1];
		vmin = &voltage_pack->package.elements[2];
		vmax = &voltage_pack->package.elements[3];

		if (acpi_id->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (id), got: %d\n",
					__func__, i, acpi_id->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (name->type != ACPI_TYPE_STRING) {
			dev_warn(dev, "%s: object %d, string expected, got %d\n",
					__func__, i, name->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (vmax->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (vmax), got: %d\n",
					__func__, i, vmax->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (vmin->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (vmin), got: %d\n",
					__func__, i, vmin->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		vlt->voltage[i].id = acpi_id->integer.value;
		vlt->voltage[i].handle = obj->reference.handle;
		vlt->voltage[i].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);

		snprintf(vlt->voltage[i].input_attr_name, ATTR_NAME_SIZE, "in%d_input", i);
		atk_init_attribute(&vlt->voltage[i].input_attr,
				vlt->voltage[i].input_attr_name,
				0444, atk_voltage_input_show, NULL);

		snprintf(vlt->voltage[i].label_attr_name, ATTR_NAME_SIZE, "in%d_label", i);
		atk_init_attribute(&vlt->voltage[i].label_attr,
				vlt->voltage[i].label_attr_name,
				0444, atk_voltage_label_show, NULL);

		snprintf(vlt->voltage[i].max_attr_name, ATTR_NAME_SIZE, "in%d_max", i);
		atk_init_attribute(&vlt->voltage[i].max_attr,
				vlt->voltage[i].max_attr_name,
				0444, atk_voltage_max_show, NULL);

		snprintf(vlt->voltage[i].min_attr_name, ATTR_NAME_SIZE, "in%d_min", i);
		atk_init_attribute(&vlt->voltage[i].min_attr,
				vlt->voltage[i].min_attr_name,
				0444, atk_voltage_min_show, NULL);

		dev_dbg(dev, "voltage %d: %#llx %s [%llu-%llu]\n", i, vlt->voltage[i].id,
				vlt->voltage[i].acpi_name, vmin->integer.value,
				vmax->integer.value);
		ACPI_FREE(buffer.pointer);
	}

	voltage_list = vlt;

	return 0;

cleanup:
	for (i = 0; i < vlt->count; i++)
		kfree(vlt->voltage[i].acpi_name);
	kfree(vlt);

	return ret;
}

static int atk_enumerate_fan(struct acpi_buffer *fan_buf)
{
	struct device *dev = &atk_data.device->dev;
	union acpi_object *pack;
	union acpi_object *obj;
	struct atk_fan_list *fan;
	int ret, i;
	int count, next;

	pack = fan_buf->pointer;

	if (pack->package.count < 1) {
		dev_warn(dev, "%s: fan package is too small: %d\n",
				__func__, pack->package.count);
		return -EINVAL;
	}

	obj = &pack->package.elements[0];
	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(dev, "%s: fan package: invalid type for element 0: %d\n",
				__func__, obj->type);
		return -EINVAL;
	}

	/* Don't fail, it's not fatal */
	if (obj->integer.value + 1 != pack->package.count) {
		dev_dbg(dev, "%s: invalid fan count? %llu (should be %d)\n",
				__func__, obj->integer.value, pack->package.count - 1);
	}

	count = pack->package.count - 1;

	fan = kzalloc(sizeof(*fan) + sizeof(*fan->fan) * count, GFP_KERNEL);
	if (!fan)
		return -ENOMEM;

	fan->count = count;
	next = 0;
	for (i = 0; i < pack->package.count - 1; i++) {
		struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
		union acpi_object *fan_pack;
		union acpi_object *acpi_id;
		union acpi_object *name;
		union acpi_object *fmin;
		union acpi_object *fmax;
		union acpi_object *enable;
		acpi_status status;

		/* handle to fan package */
		obj = &pack->package.elements[i + 1];

		if (obj->type != ACPI_TYPE_ANY) {
			dev_warn(dev, "%s: invalid type type for element %d: %d\n",
					__func__, i + 1, obj->type);
			ret = -EINVAL;
			goto cleanup;
		}

		status = acpi_evaluate_object_typed(obj->reference.handle, NULL, NULL,
				&buffer, ACPI_TYPE_PACKAGE);
		if (status != AE_OK) {
			dev_warn(dev, "%s: ACPI exception while evaluating object %d: %s\n",
					__func__, i + 1, acpi_format_exception(status));
			ret = -EINVAL;
			goto cleanup;
		}

		/* Fan package:
		 * id (usual stuff)
		 * description
		 * min
		 * max
		 * enable bit (disabled = 0, enabled otherwise)
		 */
		fan_pack = buffer.pointer;

		if (fan_pack->package.count != 5) {
			dev_warn(dev, "%s: invalid package len for object %d: %d\n",
					__func__, i + 1, fan_pack->package.count);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		acpi_id = &fan_pack->package.elements[0];
		name = &fan_pack->package.elements[1];
		fmin = &fan_pack->package.elements[2];
		fmax = &fan_pack->package.elements[3];
		enable = &fan_pack->package.elements[4];

		if (acpi_id->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (id), got: %d\n",
					__func__, i, acpi_id->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (name->type != ACPI_TYPE_STRING) {
			dev_warn(dev, "%s: object %d, string expected, got %d\n",
					__func__, i + 1, name->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (fmin->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (fmin), got %d\n",
					__func__, i + 1, fmin->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (fmax->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (fmax), got %d\n",
					__func__, i + 1, fmax->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}
		if (enable->type != ACPI_TYPE_INTEGER) {
			dev_warn(dev, "%s: object %d, int expected (en), got %d\n",
					__func__, i + 1, enable->type);
			ACPI_FREE(buffer.pointer);
			ret = -EINVAL;
			goto cleanup;
		}

		dev_dbg(dev, "fan %d: %#llx %s [%llu-%llu] %s\n", i, acpi_id->integer.value,
				name->string.pointer, fmin->integer.value,
				fmax->integer.value,
				enable->integer.value ? "enabled" : "disabled");

		if (!enable->integer.value) {
			fan->count--;
			continue;
		}

		fan->fan[next].id = acpi_id->integer.value;
		fan->fan[next].handle = obj->reference.handle;
		fan->fan[next].acpi_name = kstrdup(name->string.pointer, GFP_KERNEL);

		snprintf(fan->fan[next].input_attr_name, ATTR_NAME_SIZE, "fan%d_input", next);
		atk_init_attribute(&fan->fan[next].input_attr,
				fan->fan[next].input_attr_name,
				0444, atk_fan_input_show, NULL);

		snprintf(fan->fan[next].label_attr_name, ATTR_NAME_SIZE, "fan%d_label", next);
		atk_init_attribute(&fan->fan[next].label_attr,
				fan->fan[next].label_attr_name,
				0444, atk_fan_label_show, NULL);

		snprintf(fan->fan[next].min_attr_name, ATTR_NAME_SIZE, "fan%d_min", next);
		atk_init_attribute(&fan->fan[next].min_attr,
				fan->fan[next].min_attr_name,
				0444, atk_fan_min_show, NULL);

		snprintf(fan->fan[next].max_attr_name, ATTR_NAME_SIZE, "fan%d_max", next);
		atk_init_attribute(&fan->fan[next].max_attr,
				fan->fan[next].max_attr_name,
				0444, atk_fan_max_show, NULL);

		next++;
		ACPI_FREE(buffer.pointer);
	}

	fan_list = fan;

	return 0;
cleanup:
	for (i = 0; i < fan->count; i++)
		kfree(fan->fan[i].acpi_name);
	kfree(fan);

	return ret;
}

static int atk_add(struct acpi_device *device)
{
	acpi_status ret;
	int err, i;
	struct acpi_buffer buf;
	union acpi_object *obj;
	struct acpi_namespace_node *search_ns;
	struct acpi_namespace_node *ns;

	dev_dbg(&device->dev, "atk: adding...\n");

	atk_data.device = device;
	atk_data.atk_handle = device->handle;

	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "MBIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "atk: method MBIF not found\n");
		return -ENODEV;
	}

	obj = buf.pointer;
	if (obj->package.count >= 2 && obj->package.elements[1].type == ACPI_TYPE_STRING) {
		dev_dbg(&device->dev, "board ID = %s\n",
				obj->package.elements[1].string.pointer);
	}
	ACPI_FREE(buf.pointer);

	/* Check for hwmon methods */
	search_ns = acpi_ns_map_handle_to_node(device->handle);
	if (!search_ns)
		return -ENODEV;

	/* RTMP: read temperature */
	ret = acpi_ns_get_node(search_ns, "RTMP", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RTMP not found\n");
		return -ENODEV;
	}
	atk_data.rtmp_handle = acpi_ns_convert_entry_to_handle(ns);

	/* RVLT: read voltage */
	ret = acpi_ns_get_node(search_ns, "RVLT", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RVLT not found\n");
		return -ENODEV;
	}
	atk_data.rvlt_handle = acpi_ns_convert_entry_to_handle(ns);

	/* RFAN: read fan status */
	ret = acpi_ns_get_node(search_ns, "RFAN", ACPI_NS_NO_UPSEARCH, &ns);
	if (ret != AE_OK) {
		dev_dbg(&device->dev, "method RFAN not found\n");
		return -ENODEV;
	}
	atk_data.rfan_handle = acpi_ns_convert_entry_to_handle(ns);

	/* Enumerate temp data - TSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "TSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "TSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));
		return -ENODEV;
	}

	err = atk_enumerate_temp(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		return err;

	/* Enumerate voltage data - VSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "VSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "VSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));

		err = -ENODEV;
		goto cleanup;
	}

	err = atk_enumerate_voltage(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		goto cleanup;

	/* Enumerate fan data - FSIF */
	buf.length = ACPI_ALLOCATE_BUFFER;
	ret = acpi_evaluate_object_typed(atk_data.atk_handle, "FSIF", NULL,
			&buf, ACPI_TYPE_PACKAGE);
	if (ret != AE_OK) {
		dev_warn(&device->dev, "TSIF: ACPI exception: %s\n",
				acpi_format_exception(ret));

		err = -ENODEV;
		goto cleanup;
	}

	err = atk_enumerate_fan(&buf);
	ACPI_FREE(buf.pointer);
	if (err)
		goto cleanup;

	dev_dbg(&atk_data.device->dev, "registering hwmon device\n");
	atk_data.class_dev = hwmon_device_register(&atk_data.device->dev);
	if (IS_ERR(atk_data.class_dev)) {
		err = PTR_ERR(atk_data.class_dev);
		goto cleanup;
	}

	dev_dbg(&atk_data.device->dev, "populating sysfs directory\n");
	err = atk_create_files(&atk_data.device->dev);
	if (err)
		goto remove;

	acpi_driver_data(device) = &atk_data;

	return 0;
remove:
	atk_remove_files(&atk_data.device->dev);
	hwmon_device_unregister(atk_data.class_dev);

	return err;
cleanup:
	if (temp_list) {
		for (i = 0; i < temp_list->count; i++)
			kfree(temp_list->temp[i].acpi_name);
	}
	kfree(temp_list);

	if (voltage_list) {
		for (i = 0; i < voltage_list->count; i++)
			kfree(voltage_list->voltage[i].acpi_name);
	}
	kfree(voltage_list);

	if (fan_list) {
		for (i = 0; i < fan_list->count; i++)
			kfree(fan_list->fan[i].acpi_name);
	}
	kfree(fan_list);

	temp_list = NULL;
	voltage_list = NULL;
	fan_list = NULL;

	return err;
}

static int atk_remove(struct acpi_device *device, int type)
{
	dev_dbg(&device->dev, "removing...\n");

	acpi_driver_data(device) = NULL;

	hwmon_device_unregister(atk_data.class_dev);
	atk_remove_files(&atk_data.device->dev);

	return 0;
}

int atk_init(void)
{
	int ret;

	ret = acpi_bus_register_driver(&atk_driver);
	if (ret)
		pr_info("atk: acpi_bus_register_driver failed: %d\n", ret);

	return ret;
}

void atk_exit(void)
{
	acpi_bus_unregister_driver(&atk_driver);
}

module_init(atk_init);
module_exit(atk_exit);

MODULE_LICENSE("GPL");



Luca
-- 
"New processes are created by other processes, just like new
 humans. New humans are created by other humans, of course,
 not by processes." -- Unix System Administration Handbook

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-06-02 22:28       ` Luca Tettamanti
@ 2007-06-21 18:49         ` Rudolf Marek
  2007-06-21 19:33           ` Luca
  2007-06-21 19:02         ` Rudolf Marek
  1 sibling, 1 reply; 10+ messages in thread
From: Rudolf Marek @ 2007-06-21 18:49 UTC (permalink / raw)
  To: Luca Tettamanti; +Cc: lm-sensors, linux-acpi, linux-kernel

Hello Luca,

Sorry for delay,

> Ok, it makes sense :)
> 
> Name (FBUF, Package (0x06)
> {
>     0x03,
>     CPUF,
>     CHAF,
>     PWRF,
>     CHPF,
>     CH2F
> })
> 
> Clearly the first number is not the number of available readings (though
> it matches the count in the other DSDTs I've seen); don't know what it
> is :|

Maybe study the Pro2.dll from Asus Probe II?


> The last field of the individual fan packages is far more interesting: 0
> means disabled, anything else means enabled. In your DSDT "CHIPSET FAN
> Speed" is marked as disabled and there's no AML code to read the
> rotation.
> This may be true also for the other packages (temp and voltage), but for
> now I've modified only the fan code.

Ok.

Fan3 is disabled because the chip is programed to disabled - (IO pin)

> Here's another iteration of the driver, please give it a try:

It loads now. I have moved my extra fan to some other connector, now it is fan1 
for w83627EHF chip.


ATK0110 ATK0110:00: removing...
ATK0110 ATK0110:00: atk: adding...
ATK0110 ATK0110:00: board ID = A8VE-SE
ATK0110 ATK0110:00: temp 0: 0x6030000 CPU Temperature [900-1250]
ATK0110 ATK0110:00: temp 1: 0x6030001 MB Temperature [700-1250]
ATK0110 ATK0110:00: voltage 0: 0x6020000 Vcore Voltage [1450-1750]
ATK0110 ATK0110:00: voltage 1: 0x6020001  +3.3 Voltage [3000-3600]
ATK0110 ATK0110:00: voltage 2: 0x6020002  +5.0 Voltage [4500-5500]
ATK0110 ATK0110:00: voltage 3: 0x6020003 +12.0 Voltage [11200-13200]
ATK0110 ATK0110:00: atk_enumerate_fan: invalid fan count? 3 (should be 5)
ATK0110 ATK0110:00: fan 0: 0x6040000 CPU FAN Speed [0-1800] enabled
ATK0110 ATK0110:00: fan 1: 0x6040001 CHASSIS FAN Speed [0-1800] enabled
ATK0110 ATK0110:00: fan 2: 0x6040002 POWER FAN Speed [0-1800] enabled
ATK0110 ATK0110:00: fan 3: 0x6040005 CHIPSET FAN Speed [0-1800] disabled
ATK0110 ATK0110:00: fan 4: 0x6040006 CHASSIS2 FAN Speed [0-1800] enabled
ATK0110 ATK0110:00: registering hwmon device
ATK0110 ATK0110:00: populating sysfs directory

Although it says it has been registered to hwmon class, I cant find it in sysfs. 
I have loaded/unloaded the driver several times before I checked the dir, so 
maybe is something wrong?

Does it show up in your /sys/class/hwmon?

Thanks,

Rudolf

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-06-02 22:28       ` Luca Tettamanti
  2007-06-21 18:49         ` Rudolf Marek
@ 2007-06-21 19:02         ` Rudolf Marek
  2007-06-21 19:15           ` Alexey Starikovskiy
  1 sibling, 1 reply; 10+ messages in thread
From: Rudolf Marek @ 2007-06-21 19:02 UTC (permalink / raw)
  To: Luca Tettamanti; +Cc: lm-sensors, linux-acpi, linux-kernel

Hi again,

Of course it is not there because I removed it myself :/ The "sensors" command 
will just produce "general parse error" this is because of the unknown device 
class (imho). So I removed that and forgot. Well now I have the sysfs files:

/sys/class/hwmon/hwmon2/device/:
bus         fan0_min    fan2_input  fan3_label    in0_input  in1_label  in2_max 
    in3_min    temp0_crit   temp1_input
driver      fan1_input  fan2_label  fan3_max      in0_label  in1_max    in2_min 
    name       temp0_input  temp1_label
fan0_input  fan1_label  fan2_max    fan3_min      in0_max    in1_min 
in3_input  path       temp0_label  temp1_max
fan0_label  fan1_max    fan2_min    hid           in0_min    in2_input 
in3_label  power      temp0_max    uevent
fan0_max    fan1_min    fan3_input  hwmon:hwmon2  in1_input  in2_label  in3_max 
    subsystem  temp1_crit


Please note that in kernelsrc/Documentation/hwmon/sysfs-interface
is defined the interface. We have fans starting from 1 and not 0
Also temperatures are in milidegrees. So your 3200 should be 32000.
Temps file starts also from 1 and not 0. (so no temp0...)

Please can you fix this issues? I will do the review later once this things are 
fixed. Values seems to match.

Thanks,
Rudolf

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-06-21 19:02         ` Rudolf Marek
@ 2007-06-21 19:15           ` Alexey Starikovskiy
  2007-06-21 20:11             ` Luca
  0 siblings, 1 reply; 10+ messages in thread
From: Alexey Starikovskiy @ 2007-06-21 19:15 UTC (permalink / raw)
  To: Rudolf Marek; +Cc: Luca Tettamanti, lm-sensors, linux-acpi, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1763 bytes --]

Hi,

I think you might be interested in following patch, which implements _ACPI_ driver for the same hardware...
It is only "proof of concept" at the moment, but it does main thing -- reads hwmon device using ACPI interfaces.

Regards,
Alex.

Rudolf Marek wrote:
> Hi again,
> 
> Of course it is not there because I removed it myself :/ The "sensors"
> command will just produce "general parse error" this is because of the
> unknown device class (imho). So I removed that and forgot. Well now I
> have the sysfs files:
> 
> /sys/class/hwmon/hwmon2/device/:
> bus         fan0_min    fan2_input  fan3_label    in0_input  in1_label 
> in2_max    in3_min    temp0_crit   temp1_input
> driver      fan1_input  fan2_label  fan3_max      in0_label  in1_max   
> in2_min    name       temp0_input  temp1_label
> fan0_input  fan1_label  fan2_max    fan3_min      in0_max    in1_min
> in3_input  path       temp0_label  temp1_max
> fan0_label  fan1_max    fan2_min    hid           in0_min    in2_input
> in3_label  power      temp0_max    uevent
> fan0_max    fan1_min    fan3_input  hwmon:hwmon2  in1_input  in2_label 
> in3_max    subsystem  temp1_crit
> 
> 
> Please note that in kernelsrc/Documentation/hwmon/sysfs-interface
> is defined the interface. We have fans starting from 1 and not 0
> Also temperatures are in milidegrees. So your 3200 should be 32000.
> Temps file starts also from 1 and not 0. (so no temp0...)
> 
> Please can you fix this issues? I will do the review later once this
> things are fixed. Values seems to match.
> 
> Thanks,
> Rudolf
> -
> To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


[-- Attachment #2: asoc.patch --]
[-- Type: text/x-patch, Size: 5162 bytes --]

ASOC

From: Alexey Starikovskiy <aystarik@gmail.com>


---

 drivers/acpi/Makefile |    1 
 drivers/acpi/asoc.c   |  154 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 155 insertions(+), 0 deletions(-)

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index d4336f1..0e61225 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY)	+= acpi_memhotplug.o
 obj-y				+= cm_sbs.o
 obj-$(CONFIG_ACPI_SBS)		+= sbs.o
+obj-m				+= asoc.o
diff --git a/drivers/acpi/asoc.c b/drivers/acpi/asoc.c
new file mode 100644
index 0000000..996ca9e
--- /dev/null
+++ b/drivers/acpi/asoc.c
@@ -0,0 +1,154 @@
+/*
+ *  acpi_ac.c - ACPI ASUS hwmon driver
+ *
+ *  Copyright (C) 2007 Alexey Starikovskiy <astarikovskiy@suse.de>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+
+MODULE_AUTHOR("Alexey Starikovskiy");
+MODULE_DESCRIPTION("ACPI ASUS hwmon Driver");
+MODULE_LICENSE("GPL");
+
+static int acpi_asoc_add(struct acpi_device *device);
+static int acpi_asoc_remove(struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_asoc_driver = {
+	.name = "asoc",
+	.class = "ACPI ASUS hwmon",
+	.ids = "ATK0110",
+	.ops = {
+		.add = acpi_asoc_add,
+		.remove = acpi_asoc_remove,
+		},
+};
+
+struct acpi_asoc {
+	struct acpi_device * device;
+};
+
+static acpi_status asoc_read_sif(struct acpi_device *device, char prefix)
+{
+	int i, ret = 0;
+	unsigned long value;
+	acpi_status status = 0;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer buffer2 = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *fsif = NULL;
+	union acpi_object *fdesc = NULL;
+	union acpi_object arg = {.type = ACPI_TYPE_INTEGER};
+	struct acpi_object_list arglist = {.count = 1, .pointer = &arg};
+	char name[5];
+
+	snprintf(name, 5, "%cSIF", prefix);
+	status = acpi_evaluate_object(device->handle, name, NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Evaluating %s failed, status = %d\n", name, (int)status);
+		return -ENODEV;
+	}
+
+	fsif = buffer.pointer;
+	if (!fsif || (fsif->type != ACPI_TYPE_PACKAGE) || (!fsif->package.count)) {
+		printk(KERN_ERR PREFIX "Invalid data\n");
+		ret = -EFAULT;
+		goto error;
+	}
+	for (i = 1; i < fsif->package.count; ++i) {
+		status = acpi_evaluate_object(fsif->package.elements[i].reference.handle, NULL, NULL, &buffer2);
+		if (ACPI_FAILURE(status)) {
+			printk("element evaluation failed, status = %d\n", (int)status);
+			ret = -EFAULT;
+			goto error;
+		}
+		fdesc = buffer2.pointer;
+		snprintf(name, 5, "%cGET", prefix);
+		arg.integer.value = i - 1;
+		status = acpi_evaluate_integer(device->handle, name, &arglist, &value);
+		if (ACPI_FAILURE(status)) {
+			printk("%s evaluation failed, status = %d\n", name, (int)status);
+			ret = -EFAULT;
+			kfree(buffer2.pointer);
+			goto error;
+		}
+		printk(KERN_INFO PREFIX "\"%s\" [%d,%d] = %ld\n",
+			fdesc->package.elements[1].string.pointer,
+			(int)fdesc->package.elements[2].integer.value,
+			(int)fdesc->package.elements[3].integer.value,
+			value);
+		kfree(fdesc);
+		buffer2.pointer = NULL;
+		buffer2.length = ACPI_ALLOCATE_BUFFER;
+	}
+error:
+	kfree(buffer.pointer);
+	return ret;
+}
+
+static int acpi_asoc_add(struct acpi_device *device)
+{
+	int i, result = 0;
+	acpi_status status = AE_OK;
+	struct acpi_ac *ac = NULL;
+	char prefix[] = {'V', 'T', 'F'};
+
+	if (!device)
+		return -EINVAL;
+	for (i = 0; i < 3; ++i) {
+		result = asoc_read_sif(device, prefix[i]);
+		if (result)
+			return result;
+	}
+	return result;
+}
+
+static int acpi_asoc_remove(struct acpi_device *device, int type)
+{
+	acpi_status status = AE_OK;
+
+	if (!device)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init acpi_asoc_init(void)
+{
+	if (acpi_disabled)
+		return -ENODEV;
+	acpi_bus_register_driver(&acpi_asoc_driver);
+	return 0;
+}
+
+static void __exit acpi_asoc_exit(void)
+{
+	acpi_bus_unregister_driver(&acpi_asoc_driver);
+}
+
+module_init(acpi_asoc_init);
+module_exit(acpi_asoc_exit);

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-06-21 18:49         ` Rudolf Marek
@ 2007-06-21 19:33           ` Luca
  0 siblings, 0 replies; 10+ messages in thread
From: Luca @ 2007-06-21 19:33 UTC (permalink / raw)
  To: Rudolf Marek; +Cc: lm-sensors, linux-acpi, linux-kernel

On 6/21/07, Rudolf Marek <r.marek@assembler.cz> wrote:
> > Ok, it makes sense :)
> >
> > Name (FBUF, Package (0x06)
> > {
> >     0x03,
> >     CPUF,
> >     CHAF,
> >     PWRF,
> >     CHPF,
> >     CH2F
> > })
> >
> > Clearly the first number is not the number of available readings (though
> > it matches the count in the other DSDTs I've seen); don't know what it
> > is :|
>
> Maybe study the Pro2.dll from Asus Probe II?

Done that ;-) Disassembling is quite painful though, and most of the
work is performed by a kernel driver which I find a bit difficult to
decode.

Will fix the other issues and resend.

Luca

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [lm-sensors] [RFC] ACPI based hwmon driver for ASUS
  2007-06-21 19:15           ` Alexey Starikovskiy
@ 2007-06-21 20:11             ` Luca
  0 siblings, 0 replies; 10+ messages in thread
From: Luca @ 2007-06-21 20:11 UTC (permalink / raw)
  To: Alexey Starikovskiy; +Cc: Rudolf Marek, lm-sensors, linux-acpi, linux-kernel

On 6/21/07, Alexey Starikovskiy <aystarik@gmail.com> wrote:
> Hi,
>
> I think you might be interested in following patch, which implements _ACPI_ driver for the same hardware...
> It is only "proof of concept" at the moment, but it does main thing -- reads hwmon device using ACPI interfaces.

Well, reading is easy and is already implemented in the driver that I
posted. The problem is to figure out how to control the various stuff
(fans, and maybe some of the other ASUS features).

Luca

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2007-06-21 20:11 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-05-13 22:33 [RFC] ACPI based hwmon driver for ASUS Luca Tettamanti
2007-05-21 21:29 ` [lm-sensors] " Rudolf Marek
2007-05-21 21:53   ` Luca
2007-05-22  6:19     ` Rudolf Marek
2007-06-02 22:28       ` Luca Tettamanti
2007-06-21 18:49         ` Rudolf Marek
2007-06-21 19:33           ` Luca
2007-06-21 19:02         ` Rudolf Marek
2007-06-21 19:15           ` Alexey Starikovskiy
2007-06-21 20:11             ` Luca

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).