Patch for linux-2.6.11.4 that adds the driver for vt1211 hw-sensor chip

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

 



Hello,

This is a patch for the linux-2.6.11.4 kernel that adds the driver
for the VIA vt1211 hw-sensor chip.

The driver is written by Mark D. Studebaker, I have just ported it to
the 2.6 kernel series from 2.4.

Best Regards,


Signed-off-by: Lars Ekman <Lars.Ekman at bredband.net>



diff -uprN linux-2.6.11.4.org/drivers/i2c/chips/Kconfig linux-2.6.11.4/drivers/i2c/chips/Kconfig
--- linux-2.6.11.4.org/drivers/i2c/chips/Kconfig	2005-03-02 08:38:10.000000000 +0100
+++ linux-2.6.11.4/drivers/i2c/chips/Kconfig	2005-03-19 14:13:34.997239397 +0100
@@ -276,6 +276,18 @@ config SENSORS_VIA686A
  	  This driver can also be built as a module.  If so, the module
  	  will be called via686a.

+config SENSORS_VT1211
+	tristate "Via VT1211 SuperIO and HW monitoring chip"
+	depends on I2C && EXPERIMENTAL
+	select I2C_SENSOR
+	help
+	  If you say yes here you get access to all the sensors
+	  accessible via the VT1211 HW monitoring chip (temperature, fan speed,
+	  voltage etc.). This chip is used on many VIA Mini-ITX boards.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called vt1211.
+
  config SENSORS_W83781D
  	tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F"
  	depends on I2C && EXPERIMENTAL
diff -uprN linux-2.6.11.4.org/drivers/i2c/chips/Makefile linux-2.6.11.4/drivers/i2c/chips/Makefile
--- linux-2.6.11.4.org/drivers/i2c/chips/Makefile	2005-03-02 08:38:34.000000000 +0100
+++ linux-2.6.11.4/drivers/i2c/chips/Makefile	2005-03-19 12:13:39.000000000 +0100
@@ -33,6 +33,7 @@ obj-$(CONFIG_SENSORS_RTC8564)	+= rtc8564
  obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
  obj-$(CONFIG_SENSORS_SMSC47M1)	+= smsc47m1.o
  obj-$(CONFIG_SENSORS_VIA686A)	+= via686a.o
+obj-$(CONFIG_SENSORS_VT1211)	+= vt1211.o
  obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
  obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o

diff -uprN linux-2.6.11.4.org/drivers/i2c/chips/vt1211.c linux-2.6.11.4/drivers/i2c/chips/vt1211.c
--- linux-2.6.11.4.org/drivers/i2c/chips/vt1211.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11.4/drivers/i2c/chips/vt1211.c	2005-03-19 12:13:47.966755644 +0100
@@ -0,0 +1,979 @@
+/*
+    vt1211.c - Part of lm_sensors, Linux kernel modules
+                for hardware monitoring
+ 
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123 at yahoo.com>
+
+    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.
+
+
+
+    Adapted to Linux 2.6 kernel by Lars Ekman <emil71se at yahoo.com>
+
+    Updates (latest first):
+
+    Sat Mar 19 11:23:36 CET 2005 -
+	Preparations for official release;
+	    - Remove all the #ifdef stuff (ALL_SENSORS and no convertion 
+	      is now default).
+	    - Remove normal_xxx_range defines which cause compile warnings.
+	    - Remove uch_config  initialization in vt1211_init_client 
+	      (trust the BIOS initialization instead, or use setting in 
+	      sensors.conf)
+    Tue Oct  5 17:17:01 CEST 2004 -
+	Corrected faulty return value in "show_temp_hyst()".
+    Sun Sep 26 09:14:36 CEST 2004 -
+	Register conversion optional (disabled by default)
+
+*/
+
+/* Supports VIA VT1211 Super I/O sensors via ISA (LPC) accesses only. */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+#include <asm/io.h>
+
+/*
+  Even if the register values are not converted to uasable units in
+  the driver, the values should still be multiplied with some
+  factor. This factor seems to be 10 for 2.4 versions and 1000 now for
+  2.6. The multiplier for "in" values (voltages) was commented out in
+  the 2.4 version, and I don't know the correct setting for 2.6 yet.
+  Since there is still some confusion around this I made
+  easy-to-change macros for the factors.
+ */
+#define TEMP3_MULTIPLIER 1000
+#define TEMP_MULTIPLIER 1000
+#define IN_MULTIPLIER 10		/* Or what? */
+
+
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Initialize the base address of the sensors");
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
+
+SENSORS_INSMOD_1(vt1211);
+
+/* modified from kernel/include/traps.c */
+#define	REG	0x2e	/* The register to read/write */
+#define	DEV	0x07	/* Register: Logical device select */
+#define	VAL	0x2f	/* The value to read/write */
+#define PME	0x0b	/* The device with the hardware monitor */
+#define	DEVID	0x20	/* Register: Device ID */
+
+static inline void
+superio_outb(int reg, int val)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int
+superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
+
+static inline void
+superio_select(void)
+{
+	outb(DEV, REG);
+	outb(PME, VAL);
+}
+
+static inline void
+superio_enter(void)
+{
+	outb(0x87, REG);
+	outb(0x87, REG);
+}
+
+static inline void
+superio_exit(void)
+{
+	outb(0xAA, REG);
+}
+
+
+#define VT1211_DEVID 0x3c
+#define VT1211_ACT_REG 0x30
+#define VT1211_BASE_REG 0x60
+#define VT1211_EXTENT 0x80
+
+/* pwm numbered 1-2 */
+#define VT1211_REG_PWM(nr) (0x5f + (nr))
+#define VT1211_REG_PWM_CTL 0x51
+
+/* The VT1211 registers */
+/* We define the sensors as follows. Somewhat convoluted to minimize
+   changes from via686a.
+	Sensor		Voltage Mode	Temp Mode
+	--------	------------	---------
+	Reading 1			temp3
+	Reading 3			temp1	not in vt1211
+	UCH1/Reading2	in0		temp2
+	UCH2		in1		temp4
+	UCH3		in2		temp5
+	UCH4		in3		temp6
+	UCH5		in4		temp7
+	3.3V		in5
+	-12V		in6			not in vt1211
+*/
+
+/* ins numbered 0-6 */
+#define VT1211_REG_IN_MAX(nr) ((nr)==0 ? 0x3d : 0x29 + ((nr) * 2))
+#define VT1211_REG_IN_MIN(nr) ((nr)==0 ? 0x3e : 0x2a + ((nr) * 2))
+#define VT1211_REG_IN(nr)     (0x21 + (nr))
+
+/* fans numbered 1-2 */
+#define VT1211_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VT1211_REG_FAN(nr)     (0x28 + (nr))
+
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f, 0x22, 0x23, 0x24, 0x25 };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d, 0x2b, 0x2d, 0x2f, 0x31 };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e, 0x2c, 0x2e, 0x30, 0x32 };
+
+/* temps numbered 1-7 */
+#define VT1211_REG_TEMP(nr)		(regtemp[(nr) - 1])
+#define VT1211_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
+#define VT1211_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VT1211_REG_TEMP_LOW3	0x4b	/* bits 7-6 */
+#define VT1211_REG_TEMP_LOW2	0x49	/* bits 5-4 */
+#define VT1211_REG_TEMP_LOW47	0x4d
+
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID    0x45
+#define VT1211_REG_FANDIV 0x47
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* temps 1-7; voltages 0-6 */
+#define ISTEMP(i, ch_config) ((i) == 1 ? 1 : \
+			      (i) == 3 ? 1 : \
+			      (i) == 2 ? ((ch_config) >> 2) & 0x01 : \
+			                 ((ch_config) >> ((i)-1)) & 0x01)
+#define ISVOLT(i, ch_config) ((i) > 4 ? 1 : !(((ch_config) >> ((i)+2)) & 0x01))
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) SENSORS_LIMIT((val), 0, 255)
+
+#define TEMP3_FROM_REG(val) ((val)*TEMP3_MULTIPLIER)
+#define TEMP3_FROM_REG10(val) (((val)*TEMP3_MULTIPLIER)/4)
+#define TEMP3_TO_REG(val) (SENSORS_LIMIT(((val)<0? \
+	(((val)-(TEMP3_MULTIPLIER/2))/TEMP3_MULTIPLIER):\
+	((val)+(TEMP3_MULTIPLIER/2))/TEMP3_MULTIPLIER),0,255))
+#define TEMP_FROM_REG(val) ((val)*TEMP_MULTIPLIER)
+#define TEMP_FROM_REG10(val) (((val)*TEMP_MULTIPLIER)/4)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0? \
+	(((val)-(TEMP_MULTIPLIER/2))/TEMP_MULTIPLIER):\
+	((val)+(TEMP_MULTIPLIER/2))/TEMP_MULTIPLIER),0,255))
+
+
+/********* FAN RPM CONVERT ********/
+/* But this chip saturates back at 0, not at 255 like all the other chips.
+   So, 0 means 0 RPM */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+	if (rpm == 0)
+		return 0;
+	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+	return SENSORS_LIMIT((1310720 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define MIN_TO_REG(a,b) FAN_TO_REG(a,b)
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1310720/((val)*(div)))
+
+
+
+struct vt1211_data {
+	struct i2c_client client;
+	struct semaphore lock;
+	enum chips type;
+
+	struct semaphore update_lock;
+	char valid;		/* !=0 if following fields are valid */
+	unsigned long last_updated;	/* In jiffies */
+
+	u8 in[7];		/* Register value */
+	u8 in_max[7];		/* Register value */
+	u8 in_min[7];		/* Register value */
+	u16 temp[7];		/* Register value 10 bit */
+	u8 temp_over[7];	/* Register value */
+	u8 temp_hyst[7];	/* Register value */
+	u8 fan[2];		/* Register value */
+	u8 fan_min[2];		/* Register value */
+	u8 fan_div[2];		/* Register encoding, shifted right */
+	u16 alarms;		/* Register encoding */
+	u8 pwm[2];		/* Register value */
+	u8 pwm_ctl;		/* Register value */
+	u8 vid;			/* Register encoding */
+	u8 vrm;
+	u8 uch_config;
+};
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter);
+static int vt1211_detach_client(struct i2c_client *client);
+static struct vt1211_data *vt1211_update_device(struct device *dev);
+
+static struct i2c_driver vt1211_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "vt1211",
+	.id		= I2C_DRIVERID_VT1211,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter	= vt1211_attach_adapter,
+	.detach_client	= vt1211_detach_client,
+};
+
+static inline int vt_rdval(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void vt1211_write_value(
+	struct i2c_client *client, u8 reg, u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
+static void vt1211_init_client(struct i2c_client *client)
+{
+	struct vt1211_data *data = i2c_get_clientdata(client);
+
+	data->vrm = DEFAULT_VRM;
+	/* set "default" interrupt mode for alarms, which isn't the default */
+	vt1211_write_value(client, VT1211_REG_TEMP1_CONFIG, 0);
+	vt1211_write_value(client, VT1211_REG_TEMP2_CONFIG, 0);
+}
+
+#define IN_FROM_REG(val,n) ((val)*IN_MULTIPLIER)
+#define IN_TO_REG(val,n)  (SENSORS_LIMIT( \
+	(((val)+(IN_MULTIPLIER/2))/IN_MULTIPLIER),0,255))
+
+/* ----------------------------------------------------------------------
+   Temerature file definitions;
+*/
+
+static ssize_t show_temp(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	if (nr == 2) {
+		return sprintf(buf, "%d\n", TEMP3_FROM_REG10(data->temp[nr]));
+	}
+	return sprintf(buf, "%d\n", TEMP_FROM_REG10(data->temp[nr]));
+}
+static ssize_t show_temp_over(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	if (nr == 2) {
+		return sprintf(buf, "%d\n", 
+			       TEMP3_FROM_REG(data->temp_over[nr]));
+	}
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over[nr]));
+}
+
+static ssize_t set_temp_over(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	if (nr == 2) {
+		data->temp_over[nr] = TEMP3_TO_REG(val);
+	} else {
+		data->temp_over[nr] = TEMP_TO_REG(val);
+	}
+	vt1211_write_value(client,
+			   VT1211_REG_TEMP_OVER(nr + 1),
+			   data->temp_over[nr]);
+	return count;
+}
+
+static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	if (nr == 2) {
+		return sprintf(buf, "%d\n", 
+			       TEMP3_FROM_REG(data->temp_hyst[nr]));
+	}
+	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst[nr]));
+}
+
+static ssize_t set_temp_hyst(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	if (nr == 2) {
+		data->temp_hyst[nr] = TEMP3_TO_REG(val);
+	} else {
+		data->temp_hyst[nr] = TEMP_TO_REG(val);
+	}
+	vt1211_write_value(client,
+			   VT1211_REG_TEMP_HYST(nr + 1),
+			   data->temp_hyst[nr]);
+	return count;
+}
+
+#define show_temp_offset(offset)					\
+static ssize_t show_temp_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_temp(dev, buf, 0x##offset -1);			\
+}									\
+static ssize_t show_temp_over_##offset (struct device *dev, char *buf)\
+{									\
+	return show_temp_over(dev, buf, 0x##offset -1);			\
+}									\
+static ssize_t set_temp_over_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_temp_over(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t show_temp_hyst_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_temp_hyst(dev, buf, 0x##offset -1);			\
+}									\
+static ssize_t set_temp_hyst_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_temp_hyst(dev, buf, count, 0x##offset - 1);		\
+}									\
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL); \
+static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,		\
+		   show_temp_over_##offset, set_temp_over_##offset);	\
+static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR,		\
+		show_temp_hyst_##offset, set_temp_hyst_##offset)
+
+
+show_temp_offset(1);
+show_temp_offset(2);
+show_temp_offset(3);
+show_temp_offset(4);
+show_temp_offset(5);
+show_temp_offset(6);
+show_temp_offset(7);
+
+/* ----------------------------------------------------------------------
+   uch_config;
+ */
+
+static ssize_t show_uch_config(struct device *dev, char *buf)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", data->uch_config & 0x7c);
+}
+
+static ssize_t set_uch_config(
+	struct device *dev, const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = vt1211_update_device(dev);
+	long val = simple_strtol(buf, NULL, 10);
+	data->uch_config = (data->uch_config & 0x83)|(val & 0x7c);
+	vt1211_write_value(client, VT1211_REG_UCH_CONFIG, data->uch_config);
+	return count;
+}
+
+static DEVICE_ATTR(uch_config, S_IRUGO | S_IWUSR, 
+		   show_uch_config, set_uch_config);
+
+/* ----------------------------------------------------------------------
+   Fan definitions;
+ */
+
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", 
+		       FAN_FROM_REG(data->fan[nr],
+				    DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", 
+		       FAN_FROM_REG(data->fan_min[nr],
+				    DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t set_fan_min(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	data->fan_min[nr] = MIN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+	vt1211_write_value(client, VT1211_REG_FAN_MIN(nr+1), 
+			   data->fan_min[nr]);
+	return count;
+}
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+}
+static ssize_t set_fan_div(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	int old = vt_rdval(client, VT1211_REG_FANDIV);
+	if (nr == 0) {
+		data->fan_div[0] = DIV_TO_REG(val);
+		old = (old & 0xcf) | (data->fan_div[0] << 4);
+	} else {
+		data->fan_div[1] = DIV_TO_REG(val);
+		old = (old & 0x3f) | (data->fan_div[1] << 6);
+	}
+	vt1211_write_value(client, VT1211_REG_FANDIV, old);
+	return count;
+}
+static ssize_t show_fan_pwm(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+}
+static ssize_t set_fan_pwm(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	data->pwm[nr] = PWM_TO_REG(val);
+	vt1211_write_value(client, VT1211_REG_PWM(nr+1), data->pwm[nr]);
+	return count;
+}
+static ssize_t show_fan_pwm_ctl(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n",(data->pwm_ctl >> (3 + (4 * nr))) & 1);
+}
+static ssize_t set_fan_pwm_ctl(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	if (val) {
+		data->pwm_ctl |= (0x08 << (4 * nr));
+		vt1211_write_value(client, VT1211_REG_PWM_CTL, data->pwm_ctl);
+	} else {
+		data->pwm_ctl &= ~ (0x08 << (4 * nr));
+		vt1211_write_value(client, VT1211_REG_PWM_CTL, data->pwm_ctl);
+	}
+	return count;
+}
+
+#define show_fan_offset(offset)						\
+static ssize_t show_fan_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_min_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan_min(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_fan_min_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_fan_min(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_div_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan_div(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_fan_div_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_fan_div(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_pwm_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan_pwm(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_fan_pwm_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_fan_pwm(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t show_fan_pwm_ctl_##offset (struct device *dev, char *buf)\
+{									\
+	return show_fan_pwm_ctl(dev, buf, 0x##offset - 1);		\
+}									\
+static ssize_t set_fan_pwm_ctl_##offset (				\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_fan_pwm_ctl(dev, buf, count, 0x##offset - 1);	\
+}									\
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL); \
+static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\
+	show_fan_min_##offset, set_fan_min_##offset);			\
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\
+	 show_fan_div_##offset, set_fan_div_##offset);			\
+static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR,		\
+	show_fan_pwm_##offset, set_fan_pwm_##offset);			\
+static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO | S_IWUSR,		\
+	show_fan_pwm_ctl_##offset, set_fan_pwm_ctl_##offset);
+
+show_fan_offset(1);
+show_fan_offset(2);
+
+/* ----------------------------------------------------------------------
+   Voltage (in) file definitions;
+*/
+
+static ssize_t show_in(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr));
+}
+static ssize_t show_in_min(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr));
+}
+static ssize_t set_in_min(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	data->in_min[nr] = IN_TO_REG(val, nr);
+	vt1211_write_value(client, VT1211_REG_IN_MIN(nr), data->in_min[nr]);
+	return count;
+}
+static ssize_t show_in_max(struct device *dev, char *buf, int nr)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr));
+}
+static ssize_t set_in_max(
+	struct device *dev, const char *buf, size_t count, int nr)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	data->in_max[nr] = IN_TO_REG(val, nr);
+	vt1211_write_value(client, VT1211_REG_IN_MAX(nr), data->in_max[nr]);
+	return count;
+}
+
+#define show_in_offset(offset)						\
+static ssize_t show_in_##offset (struct device *dev, char *buf)		\
+{									\
+	return show_in(dev, buf, 0x##offset);				\
+}									\
+static ssize_t show_in_min_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_in_min(dev, buf, 0x##offset);			\
+}									\
+static ssize_t set_in_min_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_in_min(dev, buf, count, 0x##offset);			\
+}									\
+static ssize_t show_in_max_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_in_max(dev, buf, 0x##offset);			\
+}									\
+static ssize_t set_in_max_##offset (					\
+	struct device *dev, const char *buf, size_t count)		\
+{									\
+	return set_in_max(dev, buf, count, 0x##offset);			\
+}									\
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, NULL); \
+static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR,			\
+	show_in_min_##offset, set_in_min_##offset);			\
+static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR,			\
+	show_in_max_##offset, set_in_max_##offset);			\
+
+show_in_offset(0);
+show_in_offset(1);
+show_in_offset(2);
+show_in_offset(3);
+show_in_offset(4);
+show_in_offset(5);
+show_in_offset(6);
+
+static ssize_t show_vid(struct device *dev, char *buf)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, char *buf)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", data->vrm);
+}
+static ssize_t set_vrm(
+	struct device *dev, const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	data->vrm = val;
+	return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+/* ----------------------------------------------------------------------
+   alarm file definitions;
+*/
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+	struct vt1211_data *data = vt1211_update_device(dev);
+	return sprintf(buf, "%d\n", data->alarms);
+}
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+
+/*
+  END of file definitions
+  ---------------------------------------------------------------------- */
+
+/*
+  (vt1211_update_device is almost unchanged since 2.4.x)
+ */
+static struct vt1211_data *vt1211_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct vt1211_data *data = i2c_get_clientdata(client);
+
+	down(&data->update_lock);
+
+	if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+	    (jiffies < data->last_updated) || !data->valid) {
+
+		int i, j;
+
+		dev_dbg(&client->dev, "Starting vt1211 update\n");
+
+		for (i = 0; i <= 5; i++) {
+			if(ISVOLT(i, data->uch_config)) {
+				data->in[i] =vt_rdval(client,VT1211_REG_IN(i));
+				data->in_min[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MIN(i));
+				data->in_max[i] = vt_rdval(client,
+				                        VT1211_REG_IN_MAX(i));
+			} else {
+				data->in[i] = 0;
+				data->in_min[i] = 0;
+				data->in_max[i] = 0;
+			}
+		}
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+						     VT1211_REG_FAN_MIN(i));
+		}
+		for (i = 2; i <= 7; i++) {
+			if(ISTEMP(i, data->uch_config)) {
+				data->temp[i - 1] = vt_rdval(client,
+					             VT1211_REG_TEMP(i)) << 2;
+				switch(i) {
+					case 1:
+						/* ? */
+						j = 0;
+						break;
+					case 2:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW2) &
+						                    0x30) >> 4;
+						break;
+					case 3:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW3) &
+						                    0xc0) >> 6;
+						break;
+					case 4:
+					case 5:
+					case 6:
+					case 7:
+					default:
+						j = (vt_rdval(client,
+						  VT1211_REG_TEMP_LOW47) >>
+						            ((i-4)*2)) & 0x03;
+						break;
+ 
+				}
+				data->temp[i - 1] |= j;
+				data->temp_over[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_OVER(i));
+				data->temp_hyst[i - 1] = vt_rdval(client,
+					              VT1211_REG_TEMP_HYST(i));
+			} else {
+				data->temp[i - 1] = 0;
+				data->temp_over[i - 1] = 0;
+				data->temp_hyst[i - 1] = 0;
+			}
+		}
+
+		for (i = 1; i <= 2; i++) {
+			data->fan[i - 1] = vt_rdval(client, VT1211_REG_FAN(i));
+			data->fan_min[i - 1] = vt_rdval(client,
+			                                VT1211_REG_FAN_MIN(i));
+			data->pwm[i - 1] = vt_rdval(client, VT1211_REG_PWM(i));
+		}
+
+		data->pwm_ctl = vt_rdval(client, VT1211_REG_PWM_CTL);
+		i = vt_rdval(client, VT1211_REG_FANDIV);
+		data->fan_div[0] = (i >> 4) & 0x03;
+		data->fan_div[1] = i >> 6;
+		data->alarms = vt_rdval(client, VT1211_REG_ALARM1) |
+		                    (vt_rdval(client, VT1211_REG_ALARM2) << 8);
+		data->vid= vt_rdval(client, VT1211_REG_VID) & 0x1f;
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	up(&data->update_lock);
+
+	return data;
+}
+
+
+int vt1211_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *new_client = 0;
+	struct vt1211_data *data;
+	int err = 0;
+	u8 val;
+	const char *client_name = "vt1211";
+
+	if (!i2c_is_isa_adapter(adapter)) {
+		return 0;
+	}
+
+	if(force_addr) {
+		printk("vt1211.o: forcing ISA address 0x%04X\n", address);
+		address = force_addr & ~(VT1211_EXTENT - 1);
+		superio_enter();
+		superio_select();
+		superio_outb(VT1211_BASE_REG, address >> 8);
+		superio_outb(VT1211_BASE_REG+1, address & 0xff);
+		superio_exit();
+	}
+
+	superio_enter();
+	superio_select();
+	if((val = 0x01 & superio_inb(VT1211_ACT_REG)) == 0)
+		superio_outb(VT1211_ACT_REG, 1);
+	superio_exit();
+
+	if (!request_region(address, VT1211_EXTENT, "vt1211-sensors")) {
+		err = -EBUSY;
+		goto ERROR0;
+	}
+
+	/* OK. For now, we presume we have a valid client. We now
+	   create the client structure, even though we cannot fill it
+	   completely yet.
+	   */
+
+	if (!(data = kmalloc(sizeof(struct vt1211_data), GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto ERROR3;
+	}
+	memset(data, 0, sizeof(struct vt1211_data));
+	new_client = &data->client;
+
+	init_MUTEX(&data->lock);
+	i2c_set_clientdata(new_client, data);
+	new_client->addr = address;
+	new_client->adapter = adapter;
+	new_client->driver = &vt1211_driver;
+	new_client->flags = 0;
+
+	/* Fill in the remaining client fields and put into the global list */
+	strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
+	data->type = vt1211;
+
+	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 ERROR3;
+
+	vt1211_init_client(new_client);
+
+	device_create_file(&new_client->dev, &dev_attr_uch_config);
+
+	device_create_file(&new_client->dev, &dev_attr_temp1_input);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max);
+	device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp2_input);
+	device_create_file(&new_client->dev, &dev_attr_temp2_max);
+	device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp3_input);
+	device_create_file(&new_client->dev, &dev_attr_temp3_max);
+	device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp4_input);
+	device_create_file(&new_client->dev, &dev_attr_temp4_max);
+	device_create_file(&new_client->dev, &dev_attr_temp4_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp5_input);
+	device_create_file(&new_client->dev, &dev_attr_temp5_max);
+	device_create_file(&new_client->dev, &dev_attr_temp5_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp6_input);
+	device_create_file(&new_client->dev, &dev_attr_temp6_max);
+	device_create_file(&new_client->dev, &dev_attr_temp6_max_hyst);
+	device_create_file(&new_client->dev, &dev_attr_temp7_input);
+	device_create_file(&new_client->dev, &dev_attr_temp7_max);
+	device_create_file(&new_client->dev, &dev_attr_temp7_max_hyst);
+
+	device_create_file(&new_client->dev, &dev_attr_fan1_input);
+	device_create_file(&new_client->dev, &dev_attr_fan2_input);
+	device_create_file(&new_client->dev, &dev_attr_fan1_div);
+	device_create_file(&new_client->dev, &dev_attr_fan2_div);
+	device_create_file(&new_client->dev, &dev_attr_fan1_min);
+	device_create_file(&new_client->dev, &dev_attr_fan2_min);
+	device_create_file(&new_client->dev, &dev_attr_fan1_pwm);
+	device_create_file(&new_client->dev, &dev_attr_fan2_pwm);
+	device_create_file(&new_client->dev, &dev_attr_fan1_pwm_enable);
+	device_create_file(&new_client->dev, &dev_attr_fan2_pwm_enable);
+
+	device_create_file(&new_client->dev, &dev_attr_in0_ref);
+	device_create_file(&new_client->dev, &dev_attr_alarms);
+	device_create_file(&new_client->dev, &dev_attr_vrm);
+	device_create_file(&new_client->dev, &dev_attr_in0_input);
+	device_create_file(&new_client->dev, &dev_attr_in0_min);
+	device_create_file(&new_client->dev, &dev_attr_in0_max);
+	device_create_file(&new_client->dev, &dev_attr_in1_input);
+	device_create_file(&new_client->dev, &dev_attr_in1_min);
+	device_create_file(&new_client->dev, &dev_attr_in1_max);
+	device_create_file(&new_client->dev, &dev_attr_in2_input);
+	device_create_file(&new_client->dev, &dev_attr_in2_min);
+	device_create_file(&new_client->dev, &dev_attr_in2_max);
+	device_create_file(&new_client->dev, &dev_attr_in3_input);
+	device_create_file(&new_client->dev, &dev_attr_in3_min);
+	device_create_file(&new_client->dev, &dev_attr_in3_max);
+	device_create_file(&new_client->dev, &dev_attr_in4_input);
+	device_create_file(&new_client->dev, &dev_attr_in4_min);
+	device_create_file(&new_client->dev, &dev_attr_in4_max);
+	device_create_file(&new_client->dev, &dev_attr_in5_input);
+	device_create_file(&new_client->dev, &dev_attr_in5_min);
+	device_create_file(&new_client->dev, &dev_attr_in5_max);
+	device_create_file(&new_client->dev, &dev_attr_in6_input);
+	device_create_file(&new_client->dev, &dev_attr_in6_min);
+	device_create_file(&new_client->dev, &dev_attr_in6_max);
+	return 0;
+
+ ERROR3:
+	kfree(new_client);
+	release_region(address, VT1211_EXTENT);
+ ERROR0:
+	return err;
+}
+
+
+static int vt1211_attach_adapter(struct i2c_adapter *adapter)
+{
+	return i2c_detect(adapter, &addr_data, vt1211_detect);
+}
+
+static int vt1211_detach_client(struct i2c_client *client)
+{
+	int err;
+
+	/* release ISA region first */
+	release_region(client->addr, VT1211_EXTENT);
+
+	/* now it's safe to scrap the rest */
+	if ((err = i2c_detach_client(client))) {
+		dev_err(&client->dev,
+		    "Client deregistration failed, client not detached.\n");
+		return err;
+	}
+
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static int vt1211_find(int *address)
+{
+	u16 val;
+
+	superio_enter();
+	val= superio_inb(DEVID);
+	if(VT1211_DEVID != val) {
+		superio_exit();
+		return -ENODEV;
+	}
+
+	superio_select();
+	val = (superio_inb(VT1211_BASE_REG) << 8) |
+	       superio_inb(VT1211_BASE_REG + 1);
+	*address = val & ~(VT1211_EXTENT - 1);
+	if (*address == 0 && force_addr == 0) {
+		printk("vt1211: base address not set-use force_addr=0xaddr\n");
+		superio_exit();
+		return -ENODEV;
+	}
+	if (force_addr)
+		*address = force_addr;	/* so detect will get called */
+
+	superio_exit();
+	return 0;
+}
+
+
+
+
+
+static int __init sm_vt1211_init(void)
+{
+	int addr;
+
+	/*printk("vt1211 for 2.6: (%s %s) loaded ...\n", __DATE__, __TIME__);
+	 */
+	if (vt1211_find(&addr)) {
+		printk("vt1211: not detected, module not inserted.\n");
+		return -ENODEV;
+	}
+	normal_isa[0] = addr;
+	return i2c_add_driver(&vt1211_driver);
+}
+
+static void __exit sm_vt1211_exit(void)
+{
+	i2c_del_driver(&vt1211_driver);
+}
+
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123 at yahoo.com>, "
+	      "Updated to 2.6 by Lars Ekman <emil71se at yahoo.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(sm_vt1211_init);
+module_exit(sm_vt1211_exit);





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

  Powered by Linux