vt1211.c for 2.6 included in this mail

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

 



Hello,

I have tried to port the vt1211.c to 2.6. Everything except the
thermistor temperatures should be there.

Only the CPU temp (temp3) is implemented. The others requires a
table for ln(x) etc, and I don't have those sensors on my board.
I might fix it later.

I have only tested by doing "cat" on the files, for example;

# cd /sys/devices/platform/i2c-0/0-6000
# cat temp3_input in?_input
48246
1241
4758
12436
3331

The values are in the recommended units; millidegree Celsius and 
millivolt. (this was not correct in some other ongoing port I
have seen)

I have only tested on my VIA Mini-ITX; EPIA-CL 6000, and not for
instance the "force_addr" and other stuff I do not have myself.

I have also only tested stuff I understand, i.e *_input,
not alarms, div, pwm, vrm etc. A best effort to implement these
things is however made.


I will use this module myself, and I hope you find some of my work
useful.

Best Regards,
Lars Ekman

-------------- next part --------------
/*
    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>
*/

/* 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>

/*
  If ALL_SENSORS is defined code (and /sys files) for all sensors in
  the original vt1211.c are included. If ALL_SENSORS is un-defined only
  the ones which seems to be "known" in the example "/etc/sensors.conf"
  are included. For instance only "temp3" which is the only temp sensor
  supported in this version.

#define ALL_SENSORS
*/
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 short normal_i2c_range[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] = { 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) >> 1) & 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)


/*
  Only "temp3" can be computed! This should be the CPU temp.
  From the example "etc/sensors.conf" for vt1211;

	compute temp3  (@ - 65) / 0.9686,  (@ * 0.9686) + 65

  The 10 bits seems to be fixed point 8.2 bit. Which gives this
  formula for "temp3" (val in millidegree Celcius);

	val = 1000 * (reg - (65*4)) / 4 / 0.9686   =>
	val = 258 * (reg - 260)

  or for 8 bit registers;

	val = 1032 * (reg - 65)

  (BTW: 67596 = 65.5 * 1032)
 */
#define TEMP3_FROM_REG10(val) (258 * ((int)val - 260))
#define TEMP3_FROM_REG(val) (1032 * ((int)val - 65))
#define TEMP3_TO_REG(val) (SENSORS_LIMIT(((val + 67596) / 1032),0,255))

/* 
   Others than "temp3" are not yet implmented!!
   This should be a FP computation including a ln(x) operation
   which would be implemented as a lookup table (see "via686a.c").

   Here is the formula from "/etc/sensors.conf" ('`' is the ln(x));

   (1 / (((1 / 3435) * (` ((253 - @) / (@ - 43)))) + (1 / 298.15)))  - 273.15

   (this is not hard to do, I just don't need it myself /L Ekman)
*/
#define TEMP_FROM_REG10(val) (val)
#define TEMP_FROM_REG(val)   (val)
#define TEMP_TO_REG(val)     (SENSORS_LIMIT((val),0,255))

#define IN_FROM_REG(val) /*(((val)*10+5)/10)*/ (val)
#define IN_TO_REG(val)  (SENSORS_LIMIT((((val) * 10 + 5)/10),0,255))


/********* FAN RPM CONVERSIONS ********/
/* 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);
}

/* ----------------------------------------------------------------------
   Voltage translation;

   This is according to the example /etc/sensors.conf;

    compute in2 ((@ * 100) - 3) / (0.5952 * 95.8), (@ * 0.5952 * 0.958) + .03
    compute in3 ((@ * 100) - 3) / (0.4167 * 95.8), (@ * 0.4167 * 0.958) + .03
    compute in4 ((@ * 100) - 3) / (0.1754 * 95.8), (@ * 0.1754 * 0.958) + .03
    compute in5 ((@ * 100) - 3) / (0.6296 * 95.8), (@ * 0.6296 * 0.958) + .03

    in2 is labled "VCore1" and should have K = 1.0 (fault in sensors.conf?)

    The unit should be millivolt not 1/100's of volts.  And to get
    better precision in the integer calculations we multiply divisor
    and dividend(?) with 8192.
 
    Example;

	in2_input = 1000*reg - 30 / (0.5952 * 95.8) =
		8192 * (1000*reg - 30) / 8192 * (0.5952 * 95.8) =
		(8192000*reg - 245760) / 467109

	No overflow because; (8192000*255) < 2^31

    I got these results (EPIA-CL 6000 Mini-ITX);

	lm_sensors	BIOS
	1.241		1.189	(VCore)
	4.758		4.983
	12.436		12.196
	3.331		3.260

*/
static int nconstant(int nr)
{
	switch (nr) {
	case 2: return 784794;	/* VCore: 8192 * (1.0 * 95.8) */
	case 3: return 327023;	/* 5.0V: 8192 * (0.4167 * 95.8) */
	case 4: return 137653;	/* 12V: 8192 * (0.1754 * 95.8) */
	case 5: return 494105;	/* 3.3V: 8192 * (0.6296 * 95.8) */
	default:;
	}
	return  784794;		/* (K = 1.0) */
}

static int IN_FROM_REG_fn(u8 reg, int nr)
{
	return ((int)reg*8192000 - 245760) / nconstant(nr);
}

static u8 IN_TO_REG_fn(int val, int nr)
{
	int reg = (val * nconstant(nr) + 245760) / 8192000;
	return (SENSORS_LIMIT(reg,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]));
	}
	dev_warn(dev, "Untranslated value read from temp%d\n", nr+1);
	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", 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(3);
#ifdef ALL_SENSORS
show_temp_offset(1);
show_temp_offset(2);
show_temp_offset(4);
show_temp_offset(5);
show_temp_offset(6);
show_temp_offset(7);
#endif

/* ----------------------------------------------------------------------
   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_fn(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_fn(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_fn(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_fn(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_fn(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(2);
show_in_offset(3);
show_in_offset(4);
show_in_offset(5);
#ifdef ALL_SENSORS
show_in_offset(0);
show_in_offset(1);
show_in_offset(6);
#endif

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_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_uch_config);

	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_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);


#ifdef ALL_SENSORS
	device_create_file(&new_client->dev, &dev_attr_temp1_input);
	device_create_file(&new_client->dev, &dev_attr_temp2_input);
	device_create_file(&new_client->dev, &dev_attr_temp4_input);
	device_create_file(&new_client->dev, &dev_attr_temp5_input);
	device_create_file(&new_client->dev, &dev_attr_temp6_input);
	device_create_file(&new_client->dev, &dev_attr_temp7_input);

	device_create_file(&new_client->dev, &dev_attr_temp1_max);
	device_create_file(&new_client->dev, &dev_attr_temp2_max);
	device_create_file(&new_client->dev, &dev_attr_temp4_max);
	device_create_file(&new_client->dev, &dev_attr_temp5_max);
	device_create_file(&new_client->dev, &dev_attr_temp6_max);
	device_create_file(&new_client->dev, &dev_attr_temp7_max);

	device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
	device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
	device_create_file(&new_client->dev, &dev_attr_temp4_max_hyst);
	device_create_file(&new_client->dev, &dev_attr_temp5_max_hyst);
	device_create_file(&new_client->dev, &dev_attr_temp6_max_hyst);
	device_create_file(&new_client->dev, &dev_attr_temp7_max_hyst);

	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_in6_input);
	device_create_file(&new_client->dev, &dev_attr_in6_min);
	device_create_file(&new_client->dev, &dev_attr_in6_max);
#endif
	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