Files
lm-sensors/kernel/chips/mtp008.c
T
Kyosti Malkki 1cfe63804a (Kyösti) Cleanups
Copy sysctl enums to chip drivers from sensors.h for now,
   as seen in drivers included in 2.5 tree. File no longer included
   from kernel side.

   Apply i2c-proc change in CVS tagged -km2.

   Partial clean and sort of includes everywhere.

   Add i2c-dev.h, as a partial copy from i2c.
   Add to sensors.h from i2c-proc.h to compile things.
   Remove i2c-isa.h.

   Reflect header file changes to lib/ and prog/.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1705 7894878c-1315-0410-8ee3-d5d059ff63e0
2003-01-21 20:01:27 +00:00

1212 lines
34 KiB
C

/*
mtp008.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2001 Kris Van Hees <aedil@alchar.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/slab.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-proc.h>
#include <linux/init.h>
#include <asm/errno.h>
#include <asm/io.h>
#include "version.h"
MODULE_LICENSE("GPL");
/* Addresses to scan */
static unsigned short normal_i2c[] = {SENSORS_I2C_END};
static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END};
static unsigned int normal_isa[] = {SENSORS_ISA_END};
static unsigned int normal_isa_range[] = {SENSORS_ISA_END};
/* Insmod parameters */
SENSORS_INSMOD_1(mtp008);
/* The MTP008 registers */
/* in0 .. in6 */
#define MTP008_REG_IN(nr) (0x20 + (nr))
#define MTP008_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define MTP008_REG_IN_MIN(nr) (0x2c + (nr) * 2)
/* temp1 */
#define MTP008_REG_TEMP 0x27
#define MTP008_REG_TEMP_MAX 0x39
#define MTP008_REG_TEMP_MIN 0x3a
/* fan1 .. fan3 */
#define MTP008_REG_FAN(nr) (0x27 + (nr))
#define MTP008_REG_FAN_MIN(nr) (0x3a + (nr))
#define MTP008_REG_CONFIG 0x40
#define MTP008_REG_INT_STAT1 0x41
#define MTP008_REG_INT_STAT2 0x42
#define MTP008_REG_SMI_MASK1 0x43
#define MTP008_REG_SMI_MASK2 0x44
#define MTP008_REG_NMI_MASK1 0x45
#define MTP008_REG_NMI_MASK2 0x46
#define MTP008_REG_VID_FANDIV 0x47
#define MTP008_REG_I2C_ADDR 0x48
#define MTP008_REG_RESET_VID4 0x49
#define MTP008_REG_OVT_PROP 0x50
#define MTP008_REG_BEEP_CTRL1 0x51
#define MTP008_REG_BEEP_CTRL2 0x52
/* pwm1 .. pwm3 nr range 1-3 */
#define MTP008_REG_PWM_CTRL(nr) (0x52 + (nr))
#define MTP008_REG_PIN_CTRL1 0x56
#define MTP008_REG_PIN_CTRL2 0x57
#define MTP008_REG_CHIPID 0x58
/*
* Pin control register configuration constants.
*/
#define MTP008_CFG_VT1_PII 0x08
#define MTP008_CFG_VT2_AIN 0x00
#define MTP008_CFG_VT2_VT 0x03
#define MTP008_CFG_VT2_PII 0x04
#define MTP008_CFG_VT2_MASK 0x06
#define MTP008_CFG_VT3_VT 0x01
/* sensor pin types */
#define VOLTAGE 1
#define THERMISTOR 2
#define PIIDIODE 3
/*
* Conversion routines and macros. Rounding and limit checking is only done on
* the TO_REG variants.
*
* Note that IN values are expressed as 100 times the actual voltage to avoid
* having to use floating point values. As such, IN values are between 0 and
* 409 (0V to 4.096V).
*/
#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255))
#define IN_FROM_REG(val) (((val) * 16) / 10)
/*
* The fan cotation count (as stored in the register) is calculated using the
* following formula:
* count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div)
* and the rpm is therefore:
* rpm = 1350000 / (count * div)
*/
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm == 0)
return 255;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
return SENSORS_LIMIT(
(1350000 + rpm * div / 2) / (rpm * div),
1, 254
);
}
#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 \
: (val) == 255 ? 0 \
: 1350000 / \
((val) * (div)) \
)
/*
* Temperatures are stored as two's complement values of the Celsius value. It
* actually uses 10 times the Celsius value to avoid using floating point
* values.
*/
#define TEMP_TO_REG(val) ( \
(val) < 0 \
? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \
: SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \
)
#define TEMP_FROM_REG(val) ( \
( \
(val) > 0x80 ? (val) - 0x100 \
: (val) \
) * 10 \
)
/*
* VCORE voltage:
* 0x00 to 0x0f = 2.05 to 1.30 (0.05 per unit)
* 0x10 to 0x1e = 3.50 to 2.10 (0.10 per unit)
* 0x1f = No CPU
*/
#define VID_FROM_REG(val) ((val) == 0x1f \
? 0 \
: (val) < 0x10 ? 205 - (val) * 5 \
: 510 - (val) * 10)
/*
* Fan divider.
*/
#define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val) == 8 ? 3 \
: (val) == 4 ? 2 \
: (val) == 2 ? 1 \
: 0)
/*
* Alarms (interrupt status).
*/
#define ALARMS_FROM_REG(val) (val)
/*
* Beep controls.
*/
#define BEEPS_FROM_REG(val) (val)
#define BEEPS_TO_REG(val) (val)
/*
* PWM control. nr range 1 to 3
*/
#define PWM_FROM_REG(val) (val)
#define PWM_TO_REG(val) (val)
#define PWMENABLE_FROM_REG(nr, val) (((val) >> ((nr) + 3)) & 1)
/* Initial limits */
#define MTP008_INIT_IN_0 (vid) /* VCore 1 */
#define MTP008_INIT_IN_1 330 /* +3.3V */
#define MTP008_INIT_IN_2 (1200 * 10 / 38) /* +12V */
#define MTP008_INIT_IN_3 (vid) /* VCore 2 */
#define MTP008_INIT_IN_5 ((11861 + 7 * (-1200)) / 36) /* -12V */
#define MTP008_INIT_IN_6 150 /* Vtt */
#define MTP008_INIT_IN_PCT 10
#define MTP008_INIT_IN_MIN_0 (MTP008_INIT_IN_0 - \
MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_0 (MTP008_INIT_IN_0 + \
MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MIN_1 (MTP008_INIT_IN_1 - \
MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_1 (MTP008_INIT_IN_1 + \
MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MIN_2 (MTP008_INIT_IN_2 - \
MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_2 (MTP008_INIT_IN_2 + \
MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MIN_3 (MTP008_INIT_IN_3 - \
MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_3 (MTP008_INIT_IN_3 + \
MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MIN_5 (MTP008_INIT_IN_5 - \
MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_5 (MTP008_INIT_IN_5 + \
MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MIN_6 (MTP008_INIT_IN_6 - \
MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_IN_MAX_6 (MTP008_INIT_IN_6 + \
MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100)
#define MTP008_INIT_FAN_MIN_1 3000
#define MTP008_INIT_FAN_MIN_2 3000
#define MTP008_INIT_FAN_MIN_3 3000
#define MTP008_INIT_TEMP_OVER 700 /* 70 Celsius */
#define MTP008_INIT_TEMP_HYST 500 /* 50 Celsius */
#define MTP008_INIT_TEMP2_OVER 700 /* 70 Celsius */
#define MTP008_INIT_TEMP2_HYST 500 /* 50 Celsius */
/*
* For each registered MTP008, we need to keep some data in memory. The
* structure itself is dynamically allocated, at the same time when a new
* mtp008 client is allocated.
*/
struct mtp008_data {
int sysctl_id;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if 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 */
u8 temp; /* Register value */
u8 temp_max; /* Register value */
u8 temp_min; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 vid; /* Register encoding */
u8 fan_div[3]; /* Register encoding */
u16 alarms; /* Register encoding */
u16 beeps; /* Register encoding */
u8 pwm[4]; /* Register value */
u8 sens[3]; /* 1 = Analog input,
2 = Thermistor,
3 = PII/Celeron diode */
u8 pwmenable; /* Register 0x57 value */
};
static int mtp008_attach_adapter(struct i2c_adapter *adapter);
static int mtp008_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
static int mtp008_detach_client(struct i2c_client *client);
static int mtp008_read_value(struct i2c_client *client, u8 register);
static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value);
static void mtp008_update_client(struct i2c_client *client);
static void mtp008_init_client(struct i2c_client *client);
static void mtp008_in(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_temp_add(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_vid(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_beep(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_pwm(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_sens(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void mtp008_getsensortype(struct mtp008_data *data, u8 inp);
static int mtp008_id = 0;
static struct i2c_driver mtp008_driver =
{
.owner = THIS_MODULE,
.name = "MTP008 sensor driver",
.id = I2C_DRIVERID_MTP008,
.flags = I2C_DF_NOTIFY,
.attach_adapter = mtp008_attach_adapter,
.detach_client = mtp008_detach_client,
};
/* -- SENSORS SYSCTL START -- */
#define MTP008_SYSCTL_IN0 1000 /* Volts * 100 */
#define MTP008_SYSCTL_IN1 1001
#define MTP008_SYSCTL_IN2 1002
#define MTP008_SYSCTL_IN3 1003
#define MTP008_SYSCTL_IN4 1004
#define MTP008_SYSCTL_IN5 1005
#define MTP008_SYSCTL_IN6 1006
#define MTP008_SYSCTL_FAN1 1101 /* Rotations/min */
#define MTP008_SYSCTL_FAN2 1102
#define MTP008_SYSCTL_FAN3 1103
#define MTP008_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */
#define MTP008_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */
#define MTP008_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */
#define MTP008_SYSCTL_VID 1300 /* Volts * 100 */
#define MTP008_SYSCTL_PWM1 1401
#define MTP008_SYSCTL_PWM2 1402
#define MTP008_SYSCTL_PWM3 1403
#define MTP008_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */
#define MTP008_SYSCTL_SENS2 1502
#define MTP008_SYSCTL_SENS3 1503
#define MTP008_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
#define MTP008_SYSCTL_ALARMS 2001 /* bitvector */
#define MTP008_SYSCTL_BEEP 2002 /* bitvector */
#define MTP008_ALARM_IN0 0x0001
#define MTP008_ALARM_IN1 0x0002
#define MTP008_ALARM_IN2 0x0004
#define MTP008_ALARM_IN3 0x0008
#define MTP008_ALARM_IN4 0x0100
#define MTP008_ALARM_IN5 0x0200
#define MTP008_ALARM_IN6 0x0400
#define MTP008_ALARM_FAN1 0x0040
#define MTP008_ALARM_FAN2 0x0080
#define MTP008_ALARM_FAN3 0x0800
#define MTP008_ALARM_TEMP1 0x0010
#define MTP008_ALARM_TEMP2 0x0100
#define MTP008_ALARM_TEMP3 0x0200
/* -- SENSORS SYSCTL END -- */
/* The /proc/sys entries */
/* These files are created for each detected chip. This is just a template;
though at first sight, you might think we could use a statically
allocated list, we need some way to get back to the parent - which
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table mtp008_dir_table_template[] =
{
{MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in},
{MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
{MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
{MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan},
{MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp},
{MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
{MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add},
{MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid},
{MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div},
{MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms},
{MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep},
{MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
{MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
{MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm},
{MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
{MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
{MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens},
{0}
};
/* This function is called when:
* mtp008_driver is inserted (when this module is loaded), for each available
* adapter when a new adapter is inserted (and mtp008_driver is still present)
*/
static int mtp008_attach_adapter(struct i2c_adapter *adapter)
{
struct i2c_client_address_data mtp008_addr_data;
mtp008_addr_data.normal_i2c = addr_data.normal_i2c;
mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range;
mtp008_addr_data.probe = addr_data.probe;
mtp008_addr_data.probe_range = addr_data.probe_range;
mtp008_addr_data.ignore = addr_data.ignore;
mtp008_addr_data.ignore_range = addr_data.ignore_range;
mtp008_addr_data.force = addr_data.forces->force;
return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect);
}
int mtp008_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind)
{
const char *type_name = "";
const char *client_name = "";
int is_isa, err, sysid;
struct i2c_client *new_client;
struct mtp008_data *data;
err = 0;
is_isa = i2c_is_isa_adapter(adapter);
if (is_isa ||
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto ERROR0;
/*
* We presume we have a valid client. We now create the client
* structure, even though we cannot fill it completely yet. But it
* allows us to use mtp008_(read|write)_value().
*/
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct mtp008_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
data = (struct mtp008_data *) (new_client + 1);
new_client->addr = address;
new_client->data = data;
new_client->adapter = adapter;
new_client->driver = &mtp008_driver;
new_client->flags = 0;
/*
* Remaining detection.
*/
if (kind < 0) {
if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac)
goto ERROR1;
}
/*
* Fill in the remaining client fields and put it into the global list.
*/
type_name = "mtp008";
client_name = "MTP008 chip";
strcpy(new_client->name, client_name);
data->type = kind;
new_client->id = mtp008_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
/*
* Tell the I2C layer that a new client has arrived.
*/
if ((err = i2c_attach_client(new_client)))
goto ERROR1;
/*
* Register a new directory entry with the sensors module.
*/
if ((sysid = i2c_register_entry(new_client, type_name,
mtp008_dir_table_template)) < 0) {
err = sysid;
goto ERROR2;
}
data->sysctl_id = sysid;
/*
* Initialize the MTP008 chip.
*/
mtp008_init_client(new_client);
return 0;
/*
* Error handling. Bad programming practise but very code efficient.
*/
ERROR2:
i2c_detach_client(new_client);
ERROR1:
kfree(new_client);
ERROR0:
return err;
}
static int mtp008_detach_client(struct i2c_client *client)
{
int err;
i2c_deregister_entry(
((struct mtp008_data *) (client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("mtp008.o: Deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client);
return 0;
}
static int mtp008_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg) & 0xff;
}
static int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new MTP008. It should set limits, etc. */
static void mtp008_init_client(struct i2c_client *client)
{
int vid;
u8 save1, save2;
struct mtp008_data *data;
data = client->data;
/*
* Initialize the Myson MTP008 hardware monitoring chip.
* Save the pin settings that the BIOS hopefully set.
*/
save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
mtp008_write_value(client, MTP008_REG_CONFIG,
(mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80);
mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1);
mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2);
mtp008_getsensortype(data, save2);
/*
* Retrieve the VID setting (needed for the default limits).
*/
vid = mtp008_read_value(client, MTP008_REG_VID_FANDIV) & 0x0f;
vid |= (mtp008_read_value(client, MTP008_REG_RESET_VID4) & 0x01) << 4;
vid = VID_FROM_REG(vid);
/*
* Set the default limits.
*
* Setting temp sensors is done as follows:
*
* Register 0x57: 0 0 0 0 x x x x
* | \ / +-- AIN5/VT3
* | +----- AIN4/VT2/PII
* +-------- VT1/PII
*/
mtp008_write_value(client, MTP008_REG_IN_MAX(0),
IN_TO_REG(MTP008_INIT_IN_MAX_0));
mtp008_write_value(client, MTP008_REG_IN_MIN(0),
IN_TO_REG(MTP008_INIT_IN_MIN_0));
mtp008_write_value(client, MTP008_REG_IN_MAX(1),
IN_TO_REG(MTP008_INIT_IN_MAX_1));
mtp008_write_value(client, MTP008_REG_IN_MIN(1),
IN_TO_REG(MTP008_INIT_IN_MIN_1));
mtp008_write_value(client, MTP008_REG_IN_MAX(2),
IN_TO_REG(MTP008_INIT_IN_MAX_2));
mtp008_write_value(client, MTP008_REG_IN_MIN(2),
IN_TO_REG(MTP008_INIT_IN_MIN_2));
mtp008_write_value(client, MTP008_REG_IN_MAX(3),
IN_TO_REG(MTP008_INIT_IN_MAX_3));
mtp008_write_value(client, MTP008_REG_IN_MIN(3),
IN_TO_REG(MTP008_INIT_IN_MIN_3));
mtp008_write_value(client, MTP008_REG_IN_MAX(5),
IN_TO_REG(MTP008_INIT_IN_MAX_5));
mtp008_write_value(client, MTP008_REG_IN_MIN(5),
IN_TO_REG(MTP008_INIT_IN_MIN_5));
mtp008_write_value(client, MTP008_REG_IN_MAX(6),
IN_TO_REG(MTP008_INIT_IN_MAX_6));
mtp008_write_value(client, MTP008_REG_IN_MIN(6),
IN_TO_REG(MTP008_INIT_IN_MIN_6));
mtp008_write_value(client, MTP008_REG_TEMP_MAX,
TEMP_TO_REG(MTP008_INIT_TEMP_OVER));
mtp008_write_value(client, MTP008_REG_TEMP_MIN,
TEMP_TO_REG(MTP008_INIT_TEMP_HYST));
mtp008_write_value(client, MTP008_REG_IN_MAX(4),
TEMP_TO_REG(MTP008_INIT_TEMP2_OVER));
mtp008_write_value(client, MTP008_REG_IN_MIN(4),
TEMP_TO_REG(MTP008_INIT_TEMP2_HYST));
mtp008_write_value(client, MTP008_REG_FAN_MIN(1),
FAN_TO_REG(MTP008_INIT_FAN_MIN_1, 2));
mtp008_write_value(client, MTP008_REG_FAN_MIN(2),
FAN_TO_REG(MTP008_INIT_FAN_MIN_2, 2));
mtp008_write_value(client, MTP008_REG_FAN_MIN(3),
FAN_TO_REG(MTP008_INIT_FAN_MIN_3, 2));
/*
* Start monitoring.
*/
mtp008_write_value(
client, MTP008_REG_CONFIG,
(mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01
);
}
static void mtp008_update_client(struct i2c_client *client)
{
int i;
u8 inp;
struct mtp008_data *data;
data = client->data;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
#ifdef DEBUG
printk("Starting MTP008 update\n");
#endif
/*
* Read in the analog inputs. We're reading AIN4 and AIN5 as
* regular analog inputs, even though they may have been
* configured as temperature readings instead. Interpretation
* of these values is done elsewhere.
*/
for (i = 0; i < 7; i++) {
data->in[i] =
mtp008_read_value(client, MTP008_REG_IN(i));
data->in_max[i] =
mtp008_read_value(client, MTP008_REG_IN_MAX(i));
data->in_min[i] =
mtp008_read_value(client, MTP008_REG_IN_MIN(i));
}
/*
* Read the temperature sensor.
*/
data->temp = mtp008_read_value(client, MTP008_REG_TEMP);
data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX);
data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN);
/*
* Read the first 2 fan dividers and the VID setting. Read the
* third fan divider from a different register.
*/
inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
data->vid = inp & 0x0f;
data->vid |= (mtp008_read_value(client,
MTP008_REG_RESET_VID4) & 0x01) << 4;
data->fan_div[0] = (inp >> 4) & 0x03;
data->fan_div[1] = inp >> 6;
data->fan_div[2] =
mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6;
/*
* Read the interrupt status registers.
*/
data->alarms =
(mtp008_read_value(client,
MTP008_REG_INT_STAT1) & 0xdf) |
(mtp008_read_value(client,
MTP008_REG_INT_STAT2) & 0x0f) << 8;
/*
* Read the beep control registers.
*/
data->beeps =
(mtp008_read_value(client,
MTP008_REG_BEEP_CTRL1) & 0xdf) |
(mtp008_read_value(client,
MTP008_REG_BEEP_CTRL2) & 0x8f) << 8;
/*
* Read the sensor configuration.
*/
inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
mtp008_getsensortype(data, inp);
data->pwmenable = inp;
/*
* Read the PWM registers if enabled.
*/
for (i = 1; i <= 3; i++)
{
if(PWMENABLE_FROM_REG(i, inp))
data->pwm[i-1] = mtp008_read_value(client,
MTP008_REG_PWM_CTRL(i));
else
data->pwm[i-1] = 255;
}
/*
* Read the fan sensors. Skip 3 if PWM1 enabled.
*/
for (i = 1; i <= 3; i++) {
if(i == 3 && PWMENABLE_FROM_REG(1, inp)) {
data->fan[2] = 0;
data->fan_min[2] = 0;
} else {
data->fan[i-1] = mtp008_read_value(client,
MTP008_REG_FAN(i));
data->fan_min[i-1] = mtp008_read_value(client,
MTP008_REG_FAN_MIN(i));
}
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
static void mtp008_getsensortype(struct mtp008_data *data, u8 inp)
{
inp &= 0x0f;
data->sens[0] = (inp >> 3) + 2; /* 2 or 3 */
data->sens[1] = ((inp >> 1) & 0x03) + 1; /* 1, 2 or 3 */
data->sens[2] = (inp & 0x01) + 1; /* 1 or 2 */
}
/* The next few functions are the call-back functions of the /proc/sys and
sysctl files. Which function is used is defined in the ctl_table in
the extra1 field.
Each function must return the magnitude (power of 10 to divide the date
with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
put a maximum of *nrels elements in results reflecting the data of this
file, and set *nrels to the number it actually put in it, if operation==
SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
large enough (by checking the incoming value of *nrels). This is not very
good practice, but as long as you put less than about 5 values in results,
you can assume it is large enough. */
void mtp008_in(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
int nr;
struct mtp008_data *data;
nr = ctl_name - MTP008_SYSCTL_IN0;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 2;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
results[0] = IN_FROM_REG(data->in_min[nr]);
results[1] = IN_FROM_REG(data->in_max[nr]);
results[2] = IN_FROM_REG(data->in[nr]);
} else {
results[0] = 0;
results[1] = 0;
results[2] = 0;
}
*nrels_mag = 3;
break;
case SENSORS_PROC_REAL_WRITE:
if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) {
if (*nrels_mag >= 1) {
data->in_min[nr] = IN_TO_REG(results[0]);
mtp008_write_value(client, MTP008_REG_IN_MIN(nr),
data->in_min[nr]);
}
if (*nrels_mag >= 2) {
data->in_max[nr] = IN_TO_REG(results[1]);
mtp008_write_value(client, MTP008_REG_IN_MAX(nr),
data->in_max[nr]);
}
}
}
}
void mtp008_fan(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
int nr;
struct mtp008_data *data;
nr = ctl_name - MTP008_SYSCTL_FAN1;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
results[1] = FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr]));
*nrels_mag = 2;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 1) {
data->fan_min[nr] =
FAN_TO_REG(results[0],
DIV_FROM_REG(data->fan_div[nr]));
mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1),
data->fan_min[nr]);
}
}
}
void mtp008_temp(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct mtp008_data *data;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 1;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = TEMP_FROM_REG(data->temp_max);
results[1] = TEMP_FROM_REG(data->temp_min);
results[2] = TEMP_FROM_REG(data->temp);
*nrels_mag = 3;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 1) {
data->temp_max = TEMP_TO_REG(results[0]);
mtp008_write_value(client, MTP008_REG_TEMP_MAX,
data->temp_max);
}
if (*nrels_mag >= 2) {
data->temp_min = TEMP_TO_REG(results[1]);
mtp008_write_value(client, MTP008_REG_TEMP_MIN,
data->temp_min);
}
}
}
void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
int nr;
struct mtp008_data *data;
nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1; /* AIN4 or AIN5 */
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 1;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
if(data->sens[nr - 3] != VOLTAGE) {
results[0] = TEMP_FROM_REG(data->in_max[nr]);
results[1] = TEMP_FROM_REG(data->in_min[nr]);
results[2] = TEMP_FROM_REG(data->in[nr]);
} else {
results[0] = 0;
results[1] = 0;
results[2] = 0;
}
*nrels_mag = 3;
break;
case SENSORS_PROC_REAL_WRITE:
if(data->sens[nr - 3] != VOLTAGE) {
if (*nrels_mag >= 1) {
data->in_max[nr] = TEMP_TO_REG(results[0]);
mtp008_write_value(client, MTP008_REG_TEMP_MAX,
data->in_max[nr]);
}
if (*nrels_mag >= 2) {
data->in_min[nr] = TEMP_TO_REG(results[1]);
mtp008_write_value(client, MTP008_REG_TEMP_MIN,
data->in_min[nr]);
}
}
}
}
void mtp008_vid(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct mtp008_data *data;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 2;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = VID_FROM_REG(data->vid);
*nrels_mag = 1;
}
}
void mtp008_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct mtp008_data *data;
u8 val;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
results[2] = DIV_FROM_REG(data->fan_div[2]);
*nrels_mag = 3;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 3) {
data->fan_div[2] = DIV_TO_REG(results[2]);
val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1);
val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6;
mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val);
}
if (*nrels_mag >= 1) {
val = mtp008_read_value(client, MTP008_REG_VID_FANDIV);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
val = (val & 0x3f) |
(data->fan_div[1] & 0x03) << 6;
}
data->fan_div[0] = DIV_TO_REG(results[0]);
val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4;
mtp008_write_value(client, MTP008_REG_VID_FANDIV, val);
}
}
}
void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct mtp008_data *data;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = ALARMS_FROM_REG(data->alarms);
*nrels_mag = 1;
}
}
void mtp008_beep(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct mtp008_data *data;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = BEEPS_FROM_REG(data->beeps);
*nrels_mag = 1;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 1) {
data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f;
mtp008_write_value(client, MTP008_REG_BEEP_CTRL1,
data->beeps & 0xff);
mtp008_write_value(client, MTP008_REG_BEEP_CTRL2,
data->beeps >> 8);
}
}
}
void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
int nr;
struct mtp008_data *data;
nr = ctl_name - MTP008_SYSCTL_PWM1;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
mtp008_update_client(client);
results[0] = PWM_FROM_REG(data->pwm[nr]);
results[1] = PWMENABLE_FROM_REG(nr + 1, data->pwmenable);
*nrels_mag = 2;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 1) {
if (*nrels_mag >= 2) {
if(results[1])
data->pwmenable |= 0x10 << nr;
else
data->pwmenable &= ~(0x10 << nr);
mtp008_write_value(client, MTP008_REG_PIN_CTRL2,
data->pwmenable);
}
data->pwm[nr] = PWM_TO_REG(results[0]);
mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr),
data->pwm[nr]);
}
}
}
void mtp008_sens(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
const char *opts = "";
int nr;
u8 tmp;
struct mtp008_data *data;
nr = 1 + ctl_name - MTP008_SYSCTL_SENS1;
data = client->data;
switch (operation) {
case SENSORS_PROC_REAL_INFO:
*nrels_mag = 0;
break;
case SENSORS_PROC_REAL_READ:
results[0] = data->sens[nr - 1];
*nrels_mag = 1;
break;
case SENSORS_PROC_REAL_WRITE:
if (*nrels_mag >= 1) {
tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2);
switch (nr) {
case 1: /* VT or PII */
opts = "2 or 3";
switch (results[0]) {
case THERMISTOR:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp & ~MTP008_CFG_VT1_PII);
data->sens[0] = 2;
return;
case PIIDIODE:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp | MTP008_CFG_VT1_PII);
data->sens[0] = 3;
return;
}
break;
case 2: /* AIN, VT or PII */
tmp &= ~MTP008_CFG_VT2_MASK;
opts = "1, 2 or 3";
switch (results[0]) {
case VOLTAGE:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp | MTP008_CFG_VT2_AIN);
data->sens[1] = 1;
return;
case THERMISTOR:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp | MTP008_CFG_VT2_VT);
data->sens[1] = 2;
return;
case PIIDIODE:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp | MTP008_CFG_VT2_PII);
data->sens[1] = 3;
return;
}
break;
case 3: /* AIN or VT */
opts = "1 or 2";
switch (results[0]) {
case VOLTAGE:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp & ~MTP008_CFG_VT3_VT);
data->sens[2] = 1;
return;
case THERMISTOR:
mtp008_write_value(
client, MTP008_REG_PIN_CTRL2,
tmp | MTP008_CFG_VT3_VT);
data->sens[2] = 2;
return;
}
break;
}
printk("mtp008.o: Invalid sensor type %ld "
"for sensor %d; must be %s.\n",
results[0], nr, opts);
}
}
}
static int __init sm_mtp008_init(void)
{
printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE);
return i2c_add_driver(&mtp008_driver);
}
static void __exit sm_mtp008_exit(void)
{
i2c_del_driver(&mtp008_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Kris Van Hees <aedil@alchar.org>");
MODULE_DESCRIPTION("MTP008 driver");
module_init(sm_mtp008_init);
module_exit(sm_mtp008_exit);