[PATCH 2.6] New driver: w83l785ts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Greg,

Here is a patch that adds a new chip driver to Linux 2.6.1 (+ previous
patches). The w83l785ts driver handles a single chipset, the Winbond
W83L785TS-S. I am the author of the original driver as found in
lm_sensors 2.8.3. Wolfgang Ziegler AKA fago made an initial port to
Linux 2.6, which I reviewed, and here it is.

diff -ruN linux-2.6.1-k1/drivers/i2c/chips/Kconfig linux-2.6.1-k2/drivers/i2c/chips/Kconfig
--- linux-2.6.1-k1/drivers/i2c/chips/Kconfig	Sun Jan 18 09:57:28 2004
+++ linux-2.6.1-k2/drivers/i2c/chips/Kconfig	Sun Jan 18 10:39:32 2004
@@ -136,4 +136,16 @@
 	  This driver can also be built as a module.  If so, the module
 	  will be called w83781d.
 
+config SENSORS_W83L785TS
+	tristate "Winbond W83L785TS-S"
+	depends on I2C && EXPERIMENTAL
+	select I2C_SENSOR
+	help
+	  If you say yes here you get support for the Winbond W83L785TS-S
+	  sensor chip, which is used on the Asus A7N8X, among other
+	  motherboards.
+	  
+	  This driver can also be built as a module.  If so, the module
+	  will be called w83l785ts.
+
 endmenu
diff -ruN linux-2.6.1-k1/drivers/i2c/chips/Makefile linux-2.6.1-k2/drivers/i2c/chips/Makefile
--- linux-2.6.1-k1/drivers/i2c/chips/Makefile	Sun Jan 18 09:57:28 2004
+++ linux-2.6.1-k2/drivers/i2c/chips/Makefile	Sun Jan 18 12:04:37 2004
@@ -15,3 +15,4 @@
 obj-$(CONFIG_SENSORS_LM85)	+= lm85.o
 obj-$(CONFIG_SENSORS_LM90)	+= lm90.o
 obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
+obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
diff -ruN linux-2.6.1-k1/drivers/i2c/chips/w83l785ts.c linux-2.6.1-k2/drivers/i2c/chips/w83l785ts.c
--- linux-2.6.1-k1/drivers/i2c/chips/w83l785ts.c	Thu Jan  1 01:00:00 1970
+++ linux-2.6.1-k2/drivers/i2c/chips/w83l785ts.c	Sun Jan 18 16:44:01 2004
@@ -0,0 +1,309 @@
+/*
+ * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
+ *               monitoring
+ * Copyright (C) 2003-2004  Jean Delvare <khali at linux-fr.org>
+ *
+ * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
+ * by Winbond. It reports a single external temperature with a 1 deg
+ * resolution and a 3 deg accuracy. Datasheet can be obtained from
+ * Winbond's website at:
+ *   http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
+ *
+ * Ported to Linux 2.6 by Wolfgang Ziegler <nuppla at gmx.at> and Jean Delvare
+ * <khali at linux-fr.org>.
+ *
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/*
+ * Address to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(w83l785ts);
+
+/*
+ * The W83L785TS-S registers
+ * Manufacturer ID is 0x5CA3 for Winbond.
+ */
+
+#define W83L785TS_REG_MAN_ID1		0x4D
+#define W83L785TS_REG_MAN_ID2		0x4C
+#define W83L785TS_REG_CHIP_ID		0x4E
+#define W83L785TS_REG_CONFIG		0x40
+#define W83L785TS_REG_TYPE		0x52
+#define W83L785TS_REG_TEMP		0x27
+#define W83L785TS_REG_TEMP_OVER		0x53 /* not sure about this one */
+
+/*
+ * Conversions
+ * The W83L785TS-S uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val)	((val & 0x80 ? val-0x100 : val) * 1000)
+
+/*
+ * Functions declaration
+ */
+
+static int w83l785ts_attach_adapter(struct i2c_adapter *adapter);
+static int w83l785ts_detect(struct i2c_adapter *adapter, int address,
+	int kind);
+static int w83l785ts_detach_client(struct i2c_client *client);
+static void w83l785ts_update_client(struct i2c_client *client);
+
+/*
+ * Driver data (common to all clients)
+ */
+ 
+static struct i2c_driver w83l785ts_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "w83l785ts",
+	.id		= I2C_DRIVERID_W83L785TS,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= w83l785ts_attach_adapter,
+	.detach_client	= w83l785ts_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct w83l785ts_data {
+	
+	struct semaphore update_lock;
+	char valid; /* zero until following fields are valid */
+	unsigned long last_updated; /* in jiffies */
+
+	/* registers values */
+	u8 temp, temp_over;
+};
+
+/*
+ * Internal variables
+ */
+
+static int w83l785ts_id = 0;
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_temp(struct device *dev, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct w83l785ts_data *data = i2c_get_clientdata(client);
+	w83l785ts_update_client(client);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
+}
+
+static ssize_t show_temp_over(struct device *dev, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct w83l785ts_data *data = i2c_get_clientdata(client);
+	w83l785ts_update_client(client);
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
+}
+
+static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL)
+static DEVICE_ATTR(temp_max, S_IRUGO, show_temp_over, NULL)
+
+/*
+ * Real code
+ */
+
+static int w83l785ts_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
+		return 0;
+	return i2c_detect(adapter, &addr_data, w83l785ts_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client;
+	struct w83l785ts_data *data;
+	int err = 0;
+	const char *name = "";
+
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		goto exit;
+
+	if (!(new_client = kmalloc(sizeof(struct i2c_client) +  
+		sizeof(struct w83l785ts_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto exit;
+	}
+	memset(new_client, 0x00, sizeof(struct i2c_client) +
+	       sizeof(struct w83l785ts_data));
+
+
+	/* The W83L785TS-specific data is placed right after the common I2C
+	 * client data. */
+	data = (struct w83l785ts_data *) (new_client + 1);
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &w83l785ts_driver;
+	new_client->flags = 0;
+
+	/*
+	 * Now we do the remaining detection. A negative kind means that
+	 * the driver was loaded with no force parameter (default), so we
+	 * must both detect and identify the chip (actually there is only
+	 * one possible kind of chip for now, W83L785TS-S). A zero kind means
+	 * that the driver was loaded with the force parameter, the detection
+	 * step shall be skipped. A positive kind means that the driver
+	 * was loaded with the force parameter and a given kind of chip is
+	 * requested, so both the detection and the identification steps
+	 * are skipped.
+	 */
+	if (kind < 0) { /* detection */
+		if (((i2c_smbus_read_byte_data(new_client,
+		      W83L785TS_REG_CONFIG) & 0x80) != 0x00)
+		 || ((i2c_smbus_read_byte_data(new_client,
+		      W83L785TS_REG_TYPE) & 0xFC) != 0x00)) {
+			dev_dbg(&adapter->dev,
+				"W83L785TS-S detection failed at 0x%02x.\n",
+				address);
+			goto exit_free;
+		}
+	}
+
+	if (kind <= 0) { /* identification */
+		u16 man_id;
+		u8 chip_id;
+
+		man_id = (i2c_smbus_read_byte_data(new_client,
+			 W83L785TS_REG_MAN_ID1) << 8) +
+			 i2c_smbus_read_byte_data(new_client,
+			 W83L785TS_REG_MAN_ID2);
+		chip_id = i2c_smbus_read_byte_data(new_client,
+			  W83L785TS_REG_CHIP_ID);
+
+		if (man_id == 0x5CA3) { /* Winbond */
+			if (chip_id == 0x70) { /* W83L785TS-S */
+				kind = w83l785ts;			
+				name = "w83l785ts";
+			}
+		}
+	
+		if (kind <= 0) { /* identification failed */
+			dev_info(&adapter->dev,
+				 "Unsupported chip (man_id=0x%04X, "
+				 "chip_id=0x%02X).\n", man_id, chip_id);
+			goto exit_free;
+		}
+	}
+
+	/* We can fill in the remaining client fields. */
+	strlcpy(new_client->name, name, I2C_NAME_SIZE);
+	new_client->id = w83l785ts_id++;
+	data->valid = 0;
+	init_MUTEX(&data->update_lock);
+
+	/* Tell the I2C layer a new client has arrived. */
+	if ((err = i2c_attach_client(new_client))) 
+		goto exit_free;
+
+	/*
+	 * Initialize the W83L785TS chip
+	 * Nothing yet, assume it is already started.
+	 */
+
+	/* Register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_temp_input);
+	device_create_file(&new_client->dev, &dev_attr_temp_max);
+
+	return 0;
+
+exit_free:
+	kfree(new_client);
+exit:
+	return err;
+}
+
+static int w83l785ts_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	if ((err = i2c_detach_client(client))) {
+		dev_err(&client->dev, "Client deregistration failed, "
+			"client not detached.\n");
+		return err;
+	}
+
+	kfree(client);
+	return 0;
+}
+
+static void w83l785ts_update_client(struct i2c_client *client)
+{
+	struct w83l785ts_data *data = i2c_get_clientdata(client);
+
+	down(&data->update_lock);
+
+	if (!data->valid
+	 || (jiffies - data->last_updated > HZ * 2)
+	 || (jiffies < data->last_updated)) {
+		dev_dbg(&client->dev, "Updating w83l785ts data.\n");
+		data->temp = i2c_smbus_read_byte_data(client,
+			     W83L785TS_REG_TEMP);
+		data->temp_over = i2c_smbus_read_byte_data(client,
+				  W83L785TS_REG_TEMP_OVER);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+}
+
+static int __init sensors_w83l785ts_init(void)
+{
+	return i2c_add_driver(&w83l785ts_driver);
+}
+
+static void __exit sensors_w83l785ts_exit(void)
+{
+	i2c_del_driver(&w83l785ts_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali at linux-fr.org>");
+MODULE_DESCRIPTION("W83L785TS-S driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_w83l785ts_init);
+module_exit(sensors_w83l785ts_exit);
diff -ruN linux-2.6.1-k1/include/linux/i2c-id.h linux-2.6.1-k2/include/linux/i2c-id.h
--- linux-2.6.1-k1/include/linux/i2c-id.h	Sun Jan 18 09:57:28 2004
+++ linux-2.6.1-k2/include/linux/i2c-id.h	Sun Jan 18 10:42:06 2004
@@ -156,6 +156,7 @@
 #define I2C_DRIVERID_LM83 1040
 #define I2C_DRIVERID_LM90 1042
 #define I2C_DRIVERID_ASB100 1043
+#define I2C_DRIVERID_W83L785TS 1047
 
 /*
  * ---- Adapter types ----------------------------------------------------


-- 
Jean Delvare
http://www.ensicaen.ismra.fr/~delvare/



[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux