All of lore.kernel.org
 help / color / mirror / Atom feed
* [lm-sensors] asm58 (Mozart-2) driver for 2.6.30
@ 2009-06-28 10:31 Michael Loßin
  2009-07-01 19:37 ` Jean Delvare
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Michael Loßin @ 2009-06-28 10:31 UTC (permalink / raw)
  To: lm-sensors

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

Hello everyone,

I understand since there is no datasheet available for the asm58  
(Mozart-2) chip,
it won't be officially supported by lm-sensors.

Anyway, I ported the old driver by Rigel Freden found at

http://lists.lm-sensors.org/pipermail/lm-sensors/2006-March/015735.html

to the 2.6.30 kernel. So if anyone wants to get this chip working,
just apply the attached kernel patch and update sensors3.conf
with the asm58 section.

This is a "works-for-me" patch, but I'd be interested in any adjustments or
fixes that need to be done.

Regards,

Michael

[-- Attachment #2: asm58-2.6.30.patch --]
[-- Type: application/octet-stream, Size: 14357 bytes --]

diff -urN linux-2.6.30/Documentation/hwmon/asm58 linux-2.6.30-asm58/Documentation/hwmon/asm58
--- linux-2.6.30/Documentation/hwmon/asm58	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-asm58/Documentation/hwmon/asm58	2009-06-27 10:35:24.000000000 +0200
@@ -0,0 +1,42 @@
+Kernel driver asm58
+===================
+
+Supported Chips:
+* Asus ASM58 Mozart-2
+* Asus AS2K129R Mozart-2
+* Asus ??? Mozart-2
+  All three seem to be the same save for exterior markings
+  Prefix: 'asm58'
+  Addresses scanned: I2C 0x77
+  Datasheet: not available
+
+
+Description
+-----------
+
+This chip is discontinued and unsupported by the manufacturer. The driver was
+developed with xmbmon205 source as a reference.  It is found on
+the following Asus mainboards:
+ P4B533-VM
+ P4S333 (in conjunction with another asus chip?)
+ P4S333-VM
+ Terminator P4
+
+Registers:
+temp:
+temp1: 0x27
+temp2: 0x13
+fan:
+fan 1:  0x28,  divisor on register 0xA1 (bits 4-5)
+fan 2: 0x29,  divisor on register 0xA1 (bits 6-7)
+voltage:
+in0=r(0x20)
+in1=r(0x22)
+in2=r(0x23)
+in3=r(0x24)
+config, or bank register at 0x40
+
+notes:
+ * there is another undocumented asus chip at i2c 0x77 on a different board
+ * there is reason to believe this chip implements a watchdog and perhaps other
+   features, probably fan control among them
diff -urN linux-2.6.30/drivers/hwmon/Kconfig linux-2.6.30-asm58/drivers/hwmon/Kconfig
--- linux-2.6.30/drivers/hwmon/Kconfig	2009-06-27 10:31:32.000000000 +0200
+++ linux-2.6.30-asm58/drivers/hwmon/Kconfig	2009-06-27 10:35:24.000000000 +0200
@@ -260,6 +260,16 @@
 	  This driver can also be built as a module. If so, the module
 	  will be called asus_atk0110.
 
+config SENSORS_ASM58
+        tristate "Asus Mozart-2"
+        depends on HWMON && I2C && EXPERIMENTAL
+        help
+          If you say yes here you get support for the ASM58 Mozart-2 sensor
+          chip found on some Asus mainboards.
+
+          This driver can also be built as a module.  If so, the module
+          will be called asm58.
+
 config SENSORS_ATXP1
 	tristate "Attansic ATXP1 VID controller"
 	depends on I2C && EXPERIMENTAL
diff -urN linux-2.6.30/drivers/hwmon/Makefile linux-2.6.30-asm58/drivers/hwmon/Makefile
--- linux-2.6.30/drivers/hwmon/Makefile	2009-06-27 10:31:32.000000000 +0200
+++ linux-2.6.30-asm58/drivers/hwmon/Makefile	2009-06-27 10:35:24.000000000 +0200
@@ -24,6 +24,7 @@
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_ASM58)	+= asm58.o
 obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
 obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
 obj-$(CONFIG_SENSORS_ADT7470)	+= adt7470.o
diff -urN linux-2.6.30/drivers/hwmon/asm58.c linux-2.6.30-asm58/drivers/hwmon/asm58.c
--- linux-2.6.30/drivers/hwmon/asm58.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-asm58/drivers/hwmon/asm58.c	2009-06-28 12:11:03.000000000 +0200
@@ -0,0 +1,387 @@
+/*
+ * asm58.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+ *
+ * Copyright (c) 2009 Michael Loßin <michael.lossin@web.de>
+ *
+ * based on the 2.6.15 version by Rigel Freden <rigelf@users.sf.net>,
+ * inspired by the w83l785ts driver
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Address to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static const unsigned short normal_i2c[] = { 0x77, I2C_CLIENT_END };
+
+/*
+ * Insmod parameters
+ */
+
+I2C_CLIENT_INSMOD_1(asm58);
+
+/*
+ * The ASM58 registers
+ */
+
+#define ASM58_REG_CONFIG	0x40
+#define ASM58_REG_MODE		0x4E
+#define ASM58_REG_CHIP_ID	0x58
+#define ASM58_REG_VEND_ID	0x4F
+#define ASM58_REG_TEMP(i)	( (i) ? 0x13 : 0x27 )
+#define ASM58_REG_FAN(i)	( 0x28 + (i) )
+#define ASM58_REG_FANDIV	0xA1
+#define ASM58_REG_IN(i)		( ! (i) ? 0x20 : 0x20 + (i) + 1 )
+
+/*
+ * Conversions
+ */
+
+#define ASM58_FANDIV_FROM_REG(r) ( 1 << (r) )
+
+#define ASM58_RPM_FROM_REG(f,r) \
+( ( (f) == 255 || (f) == 0 ) ? 0 : 1350000 / ( (f) * (r) ) )
+
+#define ASM58_TEMP_FROM_REG(t) ( ( (t) & 0x80 ? (t) -0x100 : (t) ) * 1000 )
+
+#define ASM58_IN_FROM_REG(i) ( (i) * 16 )
+
+/*
+ * Functions declaration
+ */
+static int asm58_probe(struct i2c_client *client,
+		       const struct i2c_device_id *id);
+static int asm58_detect(struct i2c_client *client, int kind,
+		 	struct i2c_board_info *info);
+static int asm58_remove(struct i2c_client *client);
+static struct asm58_data *asm58_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id asm58_id[] = {
+	{ "asm58", asm58 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, asm58_id);
+
+static struct i2c_driver asm58_driver = {
+	.class		= I2C_CLASS_HWMON,
+	.driver = {
+		.name	= "asm58",
+	},
+	.probe		= asm58_probe,
+	.remove		= asm58_remove,
+	.id_table	= asm58_id,
+	.detect		= asm58_detect,
+	.address_data	= &addr_data,
+};
+
+static int asm58_read_byte(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int asm58_write_byte(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct asm58_data {
+	struct device *hwmon_dev;
+	struct mutex update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 temp[2];
+	u8 fan[2];
+	u8 fan_div[2];		/* shifted to the right */
+	u8 in[4];
+};
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+			 char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct asm58_data *data = asm58_update_device(dev);
+	return sprintf(buf, "%d\n",
+		       ASM58_TEMP_FROM_REG(data->temp[attr->index]));
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+
+
+static int asm58_fan_min_lim[] = { 5314, 2657, 1328, 664 };
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+			char *buf)
+{
+	int n, fan_rpm, div_mod = 0;
+	struct asm58_data *data = asm58_update_device(dev);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	n = attr->index;
+	fan_rpm = ASM58_RPM_FROM_REG(data->fan[n],
+				     ASM58_FANDIV_FROM_REG(data->fan_div[n]));
+
+	/* if the fan is spinning below 125% of the current divisor limit,
+	   increment */
+	if ((fan_rpm < (5 * asm58_fan_min_lim[data->fan_div[n]]) / 4)
+	    && (data->fan_div[n] < 3))
+	  /* could set to the "right" value here instead of incrementing */
+		div_mod = 1;
+	/* if above 300%, decrement */
+	else if ((fan_rpm > 3 * asm58_fan_min_lim[data->fan_div[n]])
+		 && (data->fan_div[n] > 0))
+		div_mod = -1;
+
+	if (div_mod) {
+		mutex_lock(&data->update_lock);
+		data->fan_div[n] += div_mod;
+		data->valid = 0;
+		/* just masking and shifting for the hardware fandiv reg */
+		asm58_write_byte(to_i2c_client(dev), ASM58_REG_FANDIV,
+				 ((data->fan_div[0] & 0x03) << 4) |
+				 ((data->fan_div[1] & 0x03) << 6));
+		mutex_unlock(&data->update_lock);
+	}
+
+	return sprintf(buf, "%d\n", fan_rpm);
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+
+
+static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
+		       char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct asm58_data *data = asm58_update_device(dev);
+	return sprintf(buf, "%d\n", ASM58_IN_FROM_REG(data->in[attr->index]));
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+
+/*
+ * Real code
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int asm58_detect(struct i2c_client *new_client, int kind,
+			struct i2c_board_info *info)
+{
+	int chipid, subtype, vendid;
+	struct i2c_adapter *adapter = new_client->adapter;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	chipid = asm58_read_byte(new_client, ASM58_REG_CHIP_ID);
+	subtype = asm58_read_byte(new_client, ASM58_REG_MODE);
+	if (!(chipid == 0x56 && subtype == 0x94) &&
+	    !(chipid == 0x10 && subtype == 0x5c))
+		return -ENODEV;
+
+	asm58_write_byte(new_client, ASM58_REG_MODE, 0x00);
+	vendid = asm58_read_byte(new_client, ASM58_REG_VEND_ID);
+	if (!((chipid == 0x56 && subtype == 0x94 && vendid == 0x36) ||
+	      (chipid == 0x56 && subtype == 0x94 && vendid == 0x06) ||
+	      (chipid == 0x10 && subtype == 0x5c && vendid == 0xa3)) )
+		return -ENODEV;
+
+	strlcpy(info->type, "asm58", I2C_NAME_SIZE);
+
+	return 0;
+}
+
+static int asm58_probe(struct i2c_client *new_client,
+		       const struct i2c_device_id *id)
+{
+	struct asm58_data *data;
+	int err = 0;
+
+	data = kzalloc(sizeof(struct asm58_data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	i2c_set_clientdata(new_client, data);
+	data->valid = 0;
+	mutex_init(&data->update_lock);
+
+	/* Default values in case the first read fails (unlikely). */
+	data->temp[0] = data->temp[1] = 0;
+	data->fan[0] = data->fan[1] = 0;
+	data->fan_div[0] = data->fan_div[1] = 0;
+	data->in[0] = data->in[1] = data->in[2] = data->in[3] = 0;
+
+	/*
+	 * Initialize the ASM58 chip
+	 * Nothing yet, assume it is already started.
+	 */
+
+	if ((err = device_create_file(&new_client->dev,
+				&sensor_dev_attr_temp1_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				&sensor_dev_attr_temp2_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_fan1_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_fan2_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_in0_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_in1_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_in2_input.dev_attr))
+	 || (err = device_create_file(&new_client->dev,
+				 &sensor_dev_attr_in3_input.dev_attr)))
+		goto exit_remove;
+
+	/* Register sysfs hooks */
+	data->hwmon_dev = hwmon_device_register(&new_client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove;
+	}
+
+	return 0;
+
+exit_remove:
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_temp1_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_temp2_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_fan1_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_fan2_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_in0_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_in1_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_in2_input.dev_attr);
+	device_remove_file(&new_client->dev,
+			   &sensor_dev_attr_in3_input.dev_attr);
+	kfree(data);
+exit:
+	return err;
+}
+
+static int asm58_remove(struct i2c_client *client)
+{
+	struct asm58_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_temp1_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_temp2_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_fan1_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_fan2_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_in0_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_in1_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_in2_input.dev_attr);
+	device_remove_file(&client->dev,
+			   &sensor_dev_attr_in3_input.dev_attr);
+	kfree(data);
+	return 0;
+}
+
+static struct asm58_data *asm58_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct asm58_data *data = i2c_get_clientdata(client);
+	int i;
+	int fandiv;
+
+	mutex_lock(&data->update_lock);
+
+	if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) {
+		dev_dbg(&client->dev, "Updating asm58 data.\n");
+
+		for (i = 0; i < 4; i++)
+			data->in[i] = asm58_read_byte(client,ASM58_REG_IN(i));
+
+		fandiv = asm58_read_byte(client, ASM58_REG_FANDIV);
+		data->fan_div[0] = (fandiv >> 4) & 0x03;
+		data->fan_div[1] = fandiv >> 6;
+
+		for (i = 0; i < 2; i++) {
+			data->fan[i] =
+				asm58_read_byte(client, ASM58_REG_FAN(i));
+			data->temp[i] =
+				asm58_read_byte(client, ASM58_REG_TEMP(i));
+		}
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	mutex_unlock(&data->update_lock);
+
+	return data;
+}
+
+static int __init sensors_asm58_init(void)
+{
+	return i2c_add_driver(&asm58_driver);
+}
+
+static void __exit sensors_asm58_exit(void)
+{
+	i2c_del_driver(&asm58_driver);
+}
+
+MODULE_AUTHOR("Michael Loßin <michael.lossin@web.de>");
+MODULE_DESCRIPTION("Asus Mozart-2 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_asm58_init);
+module_exit(sensors_asm58_exit);

[-- Attachment #3: sensors3.conf.asm58 --]
[-- Type: application/octet-stream, Size: 441 bytes --]

chip "asm58-*"
# this is the default config for the asus p4b533-vm mobo--will probably be fine
# for any but the p4s333
   label   temp1   "CPU Temp"
   label   temp2   "M/B Temp"
   label   fan1    "CPU Fan"
   label   fan2    "Case Fan"
   label   in0     "VCore"
   label   in1     "V3.3"
   label   in2     "V5.0"
   label   in3     "V12.0"

   compute in2 ((6.8/10)+1)*@ ,  @/((6.8/10)+1)
   compute in3 ((30/10)+1)*@  ,  @/((30/10)+1)

[-- Attachment #4: Type: text/plain, Size: 153 bytes --]

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

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

end of thread, other threads:[~2009-07-03 19:25 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-28 10:31 [lm-sensors] asm58 (Mozart-2) driver for 2.6.30 Michael Loßin
2009-07-01 19:37 ` Jean Delvare
2009-07-03  7:07 ` Hans de Goede
2009-07-03 19:25 ` Michael Loßin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.