Initial ADM1030 and ADM1031 support. Contributed by Alexandre

d'Alton.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@2573 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Jean Delvare committed 2004-06-03 21:32:19 +00:00
1 parent 77dd1086a8
commit be77f5a4ce
9 files changed
+1046

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
Kernel driver `adm1031.o'
=========================
Status: Driver supports basic temp and fan readings, min/max/crit settings
alarms. This driver does not support auto fan control.
Supported chips:
* Analog Devices ADM1030
Prefix: `adm1030'
Addresses scanned: I2C 0x2c to 0x2e
Datasheet: Publicly available at the Analog Devices website
http://products.analog.com/products/info.asp?product=ADM1030
* Analog Devices ADM1031
Prefix: `adm1031'
Addresses scanned: I2C 0x2c to 0x2e
Datasheet: Publicly available at the Analog Devices website
http://products.analog.com/products/info.asp?product=ADM1031
Author: Alexandre d'Alton <alex@alexdalton.org>
License: GPL
Module Parameters
-----------------
* force: short array (min = 1, max = 48)
List of adapter,address pairs to boldly assume to be present
* force_adm1030: short array (min = 1, max = 48)
List of adapter,address pairs which are unquestionably assumed to contain
a `adm1030' chip
* force_adm1031: short array (min = 1, max = 48)
List of adapter,address pairs which are unquestionably assumed to contain
a `adm1031' chip
* probe: short array (min = 1, max = 48)
List of adapter,address pairs to scan additionally
* probe_range: short array (min = 1, max = 48)
List of adapter,start-addr,end-addr triples to scan additionally
* ignore: short array (min = 1, max = 48)
List of adapter,address pairs not to scan
* ignore_range: short array (min = 1, max = 48)
List of adapter,start-addr,end-addr triples not to scan
Description
-----------
ADM1030 and ADM1031 are digital temperature sensors and fan controllers.
They sense their own temperature as well as the temperature of up to one
(ADM1030) or two (ADM1031) external diodes.
All temperature values are given in degrees Celcius. Resolution
is 0.5 degree for the local temperature, 0.125 degree for the remote
temperatures.
Each temperature channel has its own high and low limits, plus a critical
limit.
The ADM1030 monitors a single fan speed, while the ADM1031 monitors up to
two. Each fan channel has its own low speed limit.
Chip Features
-------------
Chip 'adm1030'
LABEL LABEL CLASS COMPUTE CLASS MODE MAGN
temp1 - - R- 3
temp1_high temp1 temp1 RW 3
temp1_low temp1 temp1 RW 3
temp1_crit temp1 temp1 RW 3
temp2 - - R- 3
temp2_high temp2 temp2 RW 3
temp2_low temp2 temp2 RW 3
temp2_crit temp2 temp2 RW 3
fan1_input - - R- 0
fan1_min fan1 fan1 RW 0
fan1_div fan1 - RW 0
alarms - - R- 0
Chip 'adm1031'
LABEL LABEL CLASS COMPUTE CLASS MODE MAGN
temp1 - - R- 3
temp1_high temp1 temp1 RW 3
temp1_low temp1 temp1 RW 3
temp1_crit temp1 temp1 RW 3
temp2 - - R- 3
temp2_high temp2 temp2 RW 3
temp2_low temp2 temp2 RW 3
temp2_crit temp2 temp2 RW 3
temp3 - - R- 3
temp3_high temp3 temp3 RW 3
temp3_low temp3 temp3 RW 3
temp3_crit temp3 temp3 RW 3
fan1_input - - R- 0
fan1_min fan1 fan1 RW 0
fan1_div fan1 - RW 0
fan2_input - - R- 0
fan2_min fan2 fan2 RW 0
fan2_div fan2 - RW 0
alarms - - R- 0
+35
View File
@@ -2150,3 +2150,38 @@ chip "pc87366-*"
set fan1_min 2000
set fan2_min 2000
set fan3_min 2000
chip "adm1030-*"
label temp1 "SYS Temp"
label temp2 "CPU Temp"
label temp1_crit "SYS Crit"
label temp2_crit "CPU Crit"
set temp1_max 50000
set temp2_max 65000
label fan1 "CPU Fan"
set fan1_min 2000
chip "adm1031-*"
label temp1 "SYS Temp"
label temp2 "CPU Temp"
label temp3 "AUX Temp"
label temp1_crit "SYS Crit"
label temp2_crit "CPU Crit"
label temp3_crit "AUX Crit"
set temp1_max 50000
set temp2_max 65000
set temp3_max 65000
label fan1 "CPU Fan"
label fan2 "Case Fan"
set fan1_min 2000
set fan2_min 2000
+1
View File
@@ -25,6 +25,7 @@ KERNELCHIPSDIR := $(MODULE_DIR)
# defined value verbatim into the command-list of rules...
# These targets are NOT included in 'mkpatch' ...
KERNELCHIPSTARGETS :=
KERNELCHIPSTARGETS += $(MODULE_DIR)/adm1031.o
ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; fi),1)
KERNELCHIPSTARGETS += $(MODULE_DIR)/bmcsensors.o
endif
+658
View File
@@ -0,0 +1,658 @@
/*
adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Based on lm75.c and lm85.c
Supports Analog Devices ADM1030 and ADM1031
Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-proc.h>
#include "version.h"
/* Following macros take channel parameter starting from 0 */
#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4 * (nr))
#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4 * (nr))
#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4 * (nr))
#define ADM1031_REG_TEMP(nr) (0x0a + (nr))
#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
#define ADM1031_REG_STATUS(nr) (0x02 + (nr))
#define ADM1031_REG_FAN_PWM 0x22
#define ADM1031_REG_CONF1 0x00
#define ADM1031_REG_CONF2 0x01
#define ADM1031_REG_EXT_TEMP 0x06
#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert (unused) */
#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto fan mode */
#define ADM1031_CONF2_PWM1_ENABLE 0x01
#define ADM1031_CONF2_PWM2_ENABLE 0x02
#define ADM1031_CONF2_TACH1_ENABLE 0x04
#define ADM1031_CONF2_TACH2_ENABLE 0x08
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
/* 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_2(adm1030, adm1031);
/*
* Proc entries
* These files are created for each detected ADM1031.
*/
/* -- SENSORS SYSCTL START -- */
#define ADM1031_SYSCTL_TEMP1 1200
#define ADM1031_SYSCTL_TEMP2 1201
#define ADM1031_SYSCTL_TEMP3 1202
#define ADM1031_SYSCTL_FAN1 1210
#define ADM1031_SYSCTL_FAN2 1211
#define ADM1031_SYSCTL_FAN_DIV 1220
#define ADM1031_SYSCTL_ALARMS 1250
#define ADM1031_ALARM_FAN1_MIN 0x0001
#define ADM1031_ALARM_FAN1_FLT 0x0002
#define ADM1031_ALARM_TEMP2_HIGH 0x0004
#define ADM1031_ALARM_TEMP2_LOW 0x0008
#define ADM1031_ALARM_TEMP2_CRIT 0x0010
#define ADM1031_ALARM_TEMP2_DIODE 0x0020
#define ADM1031_ALARM_TEMP1_HIGH 0x0040
#define ADM1031_ALARM_TEMP1_LOW 0x0080
#define ADM1031_ALARM_FAN2_MIN 0x0100
#define ADM1031_ALARM_FAN2_FLT 0x0200
#define ADM1031_ALARM_TEMP3_HIGH 0x0400
#define ADM1031_ALARM_TEMP3_LOW 0x0800
#define ADM1031_ALARM_TEMP3_CRIT 0x1000
#define ADM1031_ALARM_TEMP3_DIODE 0x2000
#define ADM1031_ALARM_TEMP1_CRIT 0x4000
#define ADM1031_ALARM_THERMAL 0x8000
/* -- SENSORS SYSCTL END -- */
static void adm1031_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void adm1031_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void adm1031_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void adm1031_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static ctl_table adm1031_dir_table_template[] = {
{ADM1031_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_temp},
{ADM1031_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_temp},
{ADM1031_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_temp},
{ADM1031_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_fan},
{ADM1031_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_fan_div},
{ADM1031_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_fan},
{ADM1031_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_alarms},
{0}
};
static ctl_table adm1030_dir_table_template[] = {
{ADM1031_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_temp},
{ADM1031_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_temp},
{ADM1031_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_fan},
{ADM1031_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_fan_div},
{ADM1031_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
&i2c_proc_real,
&i2c_sysctl_real, NULL, &adm1031_alarms},
{0}
};
/* Each client has this additional data */
struct adm1031_data {
struct i2c_client client;
int sysctl_id;
struct semaphore update_lock;
int chip_type;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 alarms;
u8 conf1;
u8 conf2;
u8 fan[2];
u8 fan_min[2];
u8 fan_pwm[2];
u8 fan_div[2];
s8 temp[3];
u8 auto_temp[3];
u8 ext_temp[3];
s8 temp_min[3];
s8 temp_max[3];
s8 temp_crit[3];
};
static int adm1031_attach_adapter(struct i2c_adapter *adapter);
static int adm1031_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
static void adm1031_init_client(struct i2c_client *client);
static int adm1031_detach_client(struct i2c_client *client);
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg);
static inline int adm1031_write_value(struct i2c_client *client, u8 reg,
unsigned int value);
static void adm1031_update_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver adm1031_driver = {
.owner = THIS_MODULE,
.name = "ADM1031/ADM1030 sensor driver",
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1031_attach_adapter,
.detach_client = adm1031_detach_client,
};
static int adm1031_id = 0;
#define TEMP_TO_REG(val) ((val) < 0 ? (((val) - 500) / 1000) : \
(((val) + 500) / 1000))
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_FROM_REG_EXT(val,ext) (TEMP_FROM_REG(val) + (ext) * 125)
#define FAN_FROM_REG(reg,div) ((reg) ? \
(11250 * 60) / ((reg) * (div)) : 0)
#define FAN_TO_REG(reg,div) FAN_FROM_REG(SENSORS_LIMIT(reg, 0, \
65535), div)
#define FAN_DIV_TO_REG(val) ((val) == 8 ? 0xc0 : \
(val) == 4 ? 0x80 : \
(val) == 1 ? 0x00 : 0x40)
#define FAN_DIV_FROM_REG(reg) (1 << (((reg)&0xc0) >> 6))
#define AUTO_TEMP_OFF_FROM_REG(reg) (((((reg) >> 3) & 0x1f) << 2) - 5)
static int adm1031_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, adm1031_detect);
}
/* This function is called by i2c_detect */
static int
adm1031_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind)
{
struct i2c_client *new_client;
struct adm1031_data *data;
int err = 0;
const char *type_name = "";
const char *client_name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm1031_data));
new_client = &data->client;
new_client->data = data;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1031_driver;
new_client->flags = 0;
if (kind < 0) {
int id, co;
id = i2c_smbus_read_byte_data(new_client, 0x3d);
co = i2c_smbus_read_byte_data(new_client, 0x3e);
if (((id != 0x31) || (id != 0x30)) && (co != 0x41))
goto exit_free;
kind = (id == 0x30) ? adm1030 : adm1031;
}
if (kind <= 0)
kind = adm1031;
/* Given the detected chip type, set the chip name and the
* auto fan control helper table. */
if (kind == adm1030) {
type_name = "adm1030";
client_name = "ADM1030 chip";
} else if (kind == adm1031) {
type_name = "adm1031";
client_name = "ADM1031 chip";
}
data->chip_type = kind;
strcpy(new_client->name, client_name);
new_client->id = adm1031_id++;
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
if (kind == adm1030) {
if ((err = i2c_register_entry(new_client, type_name,
adm1030_dir_table_template)) < 0)
goto exit_detach;
} else if (kind == adm1031) {
if ((err = i2c_register_entry(new_client, type_name,
adm1031_dir_table_template)) < 0)
goto exit_detach;
}
data->sysctl_id = err;
/* Initialize the ADM1031 chip */
adm1031_init_client(new_client);
return 0;
exit_detach:
i2c_detach_client(new_client);
exit_free:
kfree(new_client);
exit:
return err;
}
static int adm1031_detach_client(struct i2c_client *client)
{
int ret;
struct adm1031_data *data = client->data;
i2c_deregister_entry(data->sysctl_id);
if ((ret = i2c_detach_client(client)) != 0) {
return ret;
}
kfree(client);
return 0;
}
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int adm1031_write_value(struct i2c_client *client, u8 reg,
unsigned int value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
static void adm1031_init_client(struct i2c_client *client)
{
unsigned int read_val;
unsigned int mask;
struct adm1031_data *data = client->data;
mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
if (data->chip_type == adm1031) {
mask |= (ADM1031_CONF2_PWM2_ENABLE |
ADM1031_CONF2_TACH2_ENABLE);
}
/* Initialize the ADM1031 chip (enable fan speed reading) */
read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
if ((read_val | mask) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF2,
read_val | mask);
}
read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
ADM1031_CONF1_MONITOR_ENABLE);
}
}
static void adm1031_update_client(struct i2c_client *client)
{
struct adm1031_data *data = client->data;
int chan;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
#ifdef DEBUG
printk(KERN_INFO "adm1031.o: Starting chip update\n");
#endif
for (chan = 0;
chan < ((data->chip_type == adm1031) ? 3 : 2);
chan++) {
u8 oldh, newh;
oldh = adm1031_read_value(client,
ADM1031_REG_TEMP(chan));
data->ext_temp[chan] = adm1031_read_value(client,
ADM1031_REG_EXT_TEMP);
newh = adm1031_read_value(client,
ADM1031_REG_TEMP(chan));
if (newh != oldh) {
data->ext_temp[chan] = adm1031_read_value(client,
ADM1031_REG_EXT_TEMP);
#ifdef DEBUG
oldh = adm1031_read_value(client,
ADM1031_REG_TEMP(chan));
/* oldh is actually newer */
if (newh != oldh)
printk(KERN_INFO "adm1031.o: Remote "
"temperature may be wrong.\n");
#endif
}
data->temp[chan] = newh;
data->temp_min[chan] = adm1031_read_value(client,
ADM1031_REG_TEMP_MIN(chan));
data->temp_max[chan] = adm1031_read_value(client,
ADM1031_REG_TEMP_MAX(chan));
data->temp_crit[chan] = adm1031_read_value(client,
ADM1031_REG_TEMP_CRIT(chan));
data->auto_temp[chan] = adm1031_read_value(client,
ADM1031_REG_AUTO_TEMP(chan));
}
data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
data->alarms = adm1031_read_value(client, ADM1031_REG_STATUS(0))
| (adm1031_read_value(client, ADM1031_REG_STATUS(1))
<< 8);
if (data->chip_type == adm1030) {
data->alarms &= 0xc0ff;
}
for (chan = 0;
chan < (data->chip_type == adm1030 ? 1 : 2);
chan++) {
data->fan_div[chan] = adm1031_read_value(client,
ADM1031_REG_FAN_DIV(chan));
data->fan_min[chan] = adm1031_read_value(client,
ADM1031_REG_FAN_MIN(chan));
data->fan[chan] = adm1031_read_value(client,
ADM1031_REG_FAN_SPEED(chan));
data->fan_pwm[chan] = (adm1031_read_value(client,
ADM1031_REG_FAN_PWM) >> (4 * chan))
& 0x0f;
}
data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
static int __init sensors_adm1031_init(void)
{
printk(KERN_INFO "adm1031.o version %s (%s)\n", LM_VERSION, LM_DATE);
return i2c_add_driver(&adm1031_driver);
}
static void __exit sensors_adm1031_exit(void)
{
i2c_del_driver(&adm1031_driver);
}
static void
adm1031_temp(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
int ext;
struct adm1031_data *data = client->data;
int tmp;
int nr = ctl_name - ADM1031_SYSCTL_TEMP1;
ext = nr == 0 ?
((data->ext_temp[nr] >> 6) & 0x3) * 2 :
(((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 3;
else if (operation == SENSORS_PROC_REAL_READ) {
adm1031_update_client(client);
results[0] = TEMP_FROM_REG(data->temp_max[nr]);
results[1] = TEMP_FROM_REG(data->temp_min[nr]);
results[2] = TEMP_FROM_REG(data->temp_crit[nr]);
results[3] = TEMP_FROM_REG_EXT(data->temp[nr], ext);
*nrels_mag = 4;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
tmp = SENSORS_LIMIT(results[0], -55000, 127000);
data->temp_max[nr] = TEMP_TO_REG(tmp);
adm1031_write_value(client,
ADM1031_REG_TEMP_MAX(nr),
data->temp_max[nr]);
}
if (*nrels_mag >= 2) {
tmp = SENSORS_LIMIT(results[1], -55000, 127000);
data->temp_min[nr] = TEMP_TO_REG(tmp);
adm1031_write_value(client,
ADM1031_REG_TEMP_MIN(nr),
data->temp_min[nr]);
}
if (*nrels_mag >= 3) {
tmp = SENSORS_LIMIT(results[2], -55000, 127000);
data->temp_crit[nr] = TEMP_TO_REG(tmp);
adm1031_write_value(client,
ADM1031_REG_TEMP_CRIT(nr),
data->temp_crit[nr]);
}
}
}
/*
* That function checks the cases where the fan reading is not
* relevant. It is used to provide 0 as fan reading when the fan is
* not supposed to run.
*/
static int trust_fan_readings(struct adm1031_data * data, int chan)
{
int res = 0;
if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
switch(data->conf1 & 0x60) {
case 0x00: /* remote temp1 controls fan1 remote temp2
controls fan2 */
res = chan == 0 ? data->temp[1] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[1]) :
data->temp[2] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[2]);
break;
case 0x20: /* remote temp1 controls both fans*/
if (data->chip_type == adm1030)
res = 0;
else
res = data->temp[1] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[1]);
break;
case 0x40: /* remote temp2 controls both fans*/
if (data->chip_type == adm1030)
res = 0;
else
res = data->temp[2] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[2]);
break;
case 0x60: /* max controls both fans */
if (data->chip_type == adm1030){
res = data->temp[0] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[0])
|| data->temp[1] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[1]);
} else {
res = data->temp[0] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[0])
|| data->temp[1] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[1])
|| data->temp[2] >
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[2]);
}
break;
default:
res = 0;
break;
}
} else {
res = data->fan_pwm[chan] > 0;
}
return res;
}
static void adm1031_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct adm1031_data *data = client->data;
int nr = ctl_name - ADM1031_SYSCTL_FAN1;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
adm1031_update_client(client);
results[0] = FAN_FROM_REG(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr]));
results[1] = trust_fan_readings(data, nr) ?
FAN_FROM_REG(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->fan_min[nr] = FAN_TO_REG(results[0],
FAN_DIV_FROM_REG(data->fan_div[nr]));
adm1031_write_value(client,
ADM1031_REG_FAN_MIN(nr),
data->fan_min[nr]);
}
}
}
static void adm1031_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct adm1031_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
adm1031_update_client(client);
results[0] = FAN_DIV_FROM_REG(data->fan_div[0]);
*nrels_mag = 1;
if(data->chip_type == adm1031) {
results[1] = FAN_DIV_FROM_REG(data->fan_div[1]);
*nrels_mag = 2;
}
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
int old_div = FAN_DIV_FROM_REG(data->fan_div[0]);
data->fan_div[0] = FAN_DIV_TO_REG(results[0]);
adm1031_write_value(client,
ADM1031_REG_FAN_DIV(0),
data->fan_div[0]);
data->fan_min[0] = data->fan_min[0] * old_div /
FAN_DIV_FROM_REG(data->fan_div[0]);
adm1031_write_value(client,
ADM1031_REG_FAN_MIN(0),
data->fan_min[0]);
}
if (*nrels_mag >= 2) {
int old_div = FAN_DIV_FROM_REG(data->fan_div[1]);
data->fan_div[1] = FAN_DIV_TO_REG(results[1]);
adm1031_write_value(client,
ADM1031_REG_FAN_DIV(1),
data->fan_div[1]);
data->fan_min[1] = data->fan_min[1] * old_div /
FAN_DIV_FROM_REG(data->fan_div[1]);
adm1031_write_value(client,
ADM1031_REG_FAN_MIN(1),
data->fan_min[1]);
}
}
}
static void adm1031_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct adm1031_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
adm1031_update_client(client);
results[0] = data->alarms;
*nrels_mag = 1;
}
}
MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm1031_init);
module_exit(sensors_adm1031_exit);
+115
View File
@@ -360,6 +360,119 @@ static sensors_chip_feature adm1023_features[] =
{ 0 }
};
static sensors_chip_feature adm1030_features[] =
{
{ SENSORS_ADM1031_TEMP1, "temp1",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_TEMP1, VALUE(4), 3 },
{ SENSORS_ADM1031_TEMP1_MAX, "temp1_max",
SENSORS_ADM1031_TEMP1, SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(1), 3 },
{ SENSORS_ADM1031_TEMP1_MIN, "temp1_min",
SENSORS_ADM1031_TEMP1, SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(2), 3 },
{ SENSORS_ADM1031_TEMP1_CRIT, "temp1_crit", SENSORS_ADM1031_TEMP1,
SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(3), 3 },
{ SENSORS_ADM1031_TEMP2, "temp2",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_TEMP2, VALUE(4), 3 },
{ SENSORS_ADM1031_TEMP2_MAX, "temp2_max",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(1), 3 },
{ SENSORS_ADM1031_TEMP2_MIN, "temp2_min",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(2), 3 },
{ SENSORS_ADM1031_TEMP2_CRIT, "temp2_crit",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(3), 3 },
{ SENSORS_ADM1031_FAN1, "fan1",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_FAN1, VALUE(2), 0 },
{ SENSORS_ADM1031_FAN1_MIN, "fan1_min",
SENSORS_ADM1031_FAN1, SENSORS_ADM1031_FAN1, RW,
ADM1031_SYSCTL_FAN1, VALUE(1), 0 },
{ SENSORS_ADM1031_FAN1_DIV, "fan1_div",
SENSORS_ADM1031_FAN1, NOMAP,
RW, ADM1031_SYSCTL_FAN_DIV, VALUE(1), 0 },
{ SENSORS_ADM1031_ALARMS, "alarms",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_ALARMS, VALUE(1), 0 },
{ 0 }
};
static sensors_chip_feature adm1031_features[] =
{
{ SENSORS_ADM1031_TEMP1, "temp1",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_TEMP1, VALUE(4), 3 },
{ SENSORS_ADM1031_TEMP1_MAX, "temp1_max",
SENSORS_ADM1031_TEMP1, SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(1), 3 },
{ SENSORS_ADM1031_TEMP1_MIN, "temp1_min",
SENSORS_ADM1031_TEMP1, SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(2), 3 },
{ SENSORS_ADM1031_TEMP1_CRIT, "temp1_crit",
SENSORS_ADM1031_TEMP1, SENSORS_ADM1031_TEMP1,
RW, ADM1031_SYSCTL_TEMP1, VALUE(3), 3 },
{ SENSORS_ADM1031_TEMP2, "temp2",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_TEMP2, VALUE(4), 3 },
{ SENSORS_ADM1031_TEMP2_MAX, "temp2_max",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(1), 3 },
{ SENSORS_ADM1031_TEMP2_MIN, "temp2_min",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(2), 3 },
{ SENSORS_ADM1031_TEMP2_CRIT, "temp2_crit",
SENSORS_ADM1031_TEMP2, SENSORS_ADM1031_TEMP2,
RW, ADM1031_SYSCTL_TEMP2, VALUE(3), 3 },
{ SENSORS_ADM1031_TEMP3, "temp3",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_TEMP3, VALUE(4), 3 },
{ SENSORS_ADM1031_TEMP3_MAX, "temp3_max",
SENSORS_ADM1031_TEMP3, SENSORS_ADM1031_TEMP3,
RW, ADM1031_SYSCTL_TEMP3, VALUE(1), 3 },
{ SENSORS_ADM1031_TEMP3_MIN, "temp3_min",
SENSORS_ADM1031_TEMP3, SENSORS_ADM1031_TEMP3,
RW, ADM1031_SYSCTL_TEMP3, VALUE(2), 3 },
{ SENSORS_ADM1031_TEMP3_CRIT, "temp3_crit",
SENSORS_ADM1031_TEMP3, SENSORS_ADM1031_TEMP3,
RW, ADM1031_SYSCTL_TEMP3, VALUE(3), 3 },
{ SENSORS_ADM1031_FAN1, "fan1",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_FAN1, VALUE(2), 0 },
{ SENSORS_ADM1031_FAN1_MIN, "fan1_min",
SENSORS_ADM1031_FAN1, SENSORS_ADM1031_FAN1,
RW, ADM1031_SYSCTL_FAN1, VALUE(1), 0 },
{ SENSORS_ADM1031_FAN1_DIV, "fan1_div",
SENSORS_ADM1031_FAN1, NOMAP,
RW, ADM1031_SYSCTL_FAN_DIV, VALUE(1), 0 },
{ SENSORS_ADM1031_FAN2, "fan2",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_FAN2, VALUE(2), 0 },
{ SENSORS_ADM1031_FAN2_MIN, "fan2_min",
SENSORS_ADM1031_FAN2, SENSORS_ADM1031_FAN2,
RW, ADM1031_SYSCTL_FAN2, VALUE(1), 0 },
{ SENSORS_ADM1031_FAN2_DIV, "fan2_div",
SENSORS_ADM1031_FAN2, NOMAP,
RW, ADM1031_SYSCTL_FAN_DIV, VALUE(2), 0 },
{ SENSORS_ADM1031_ALARMS, "alarms",
NOMAP, NOMAP,
R, ADM1031_SYSCTL_ALARMS, VALUE(1), 0 },
{ 0 }
};
static sensors_chip_feature max1617_features[] =
{
{ SENSORS_MAX1617_TEMP, "temp", NOMAP, NOMAP,
@@ -5100,5 +5213,7 @@ sensors_chip_features sensors_chip_features_list[] =
{ SENSORS_MAX1619_PREFIX, max1619_features },
{ SENSORS_XEONTEMP_PREFIX, xeontemp_features },
{ SENSORS_MAX6650_PREFIX, max6650_features },
{ SENSORS_ADM1030_PREFIX, adm1030_features },
{ SENSORS_ADM1031_PREFIX, adm1031_features },
{ 0 }
};
+32
View File
@@ -184,6 +184,38 @@
#define SENSORS_ADM1021_ALARMS 81 /* R */
#define SENSORS_ADM1021_DIE_CODE 90 /* R */
/* ADM1030 and ADM1031 chips */
#define SENSORS_ADM1030_PREFIX "adm1030"
#define SENSORS_ADM1031_PREFIX "adm1031"
#define SENSORS_ADM1031_TEMP1 51 /* R */
#define SENSORS_ADM1031_TEMP1_MIN 52 /* RW */
#define SENSORS_ADM1031_TEMP1_MAX 53 /* RW */
#define SENSORS_ADM1031_TEMP1_CRIT 54 /* RW */
#define SENSORS_ADM1031_TEMP2 61 /* R */
#define SENSORS_ADM1031_TEMP2_MIN 62 /* RW */
#define SENSORS_ADM1031_TEMP2_MAX 63 /* RW */
#define SENSORS_ADM1031_TEMP2_CRIT 64 /* RW */
#define SENSORS_ADM1031_TEMP3 71 /* R */
#define SENSORS_ADM1031_TEMP3_MIN 72 /* RW */
#define SENSORS_ADM1031_TEMP3_MAX 73 /* RW */
#define SENSORS_ADM1031_TEMP3_CRIT 74 /* RW */
#define SENSORS_ADM1031_FAN1 80 /* R */
#define SENSORS_ADM1031_FAN1_MIN 81 /* RW */
#define SENSORS_ADM1031_FAN1_DIV 82 /* RW */
#define SENSORS_ADM1031_FAN2 90 /* R */
#define SENSORS_ADM1031_FAN2_MIN 91 /* RW */
#define SENSORS_ADM1031_FAN2_DIV 92 /* RW */
#define SENSORS_ADM1031_ALARMS 100 /* R */
/* MAX1617 chips. */
#define SENSORS_MAX1617_PREFIX "max1617"
+93
View File
@@ -4726,6 +4726,99 @@ void print_lm90(const sensors_chip_name *name)
free_the_label(&label);
}
void print_adm1031(const sensors_chip_name *name)
{
char *label;
double cur, high, low, crit, div;
int valid, alarms, i;
int is_1031 = !strncmp("adm1031", name->prefix, 7);
if (!sensors_get_feature(*name, SENSORS_ADM1031_ALARMS, &cur))
alarms = cur;
else {
printf("ERROR: Can't get alarm data!\n");
alarms = 0;
}
for(i=0; i<(is_1031?2:1);i++) {
if (!sensors_get_label_and_valid(*name, SENSORS_ADM1031_FAN1+i*10,
&label, &valid)
&& !sensors_get_feature(*name, SENSORS_ADM1031_FAN1+i*10, &cur)
&& !sensors_get_feature(*name, SENSORS_ADM1031_FAN1_MIN+i*10, &low)
&& !sensors_get_feature(*name, SENSORS_ADM1031_FAN1_DIV+i*10, &div)) {
if (valid) {
print_label(label, 10);
printf("%4.0f RPM (min = %4.0f RPM, div = %1.0f)", cur, low, div);
printf(" %s\n",
alarms&(ADM1031_ALARM_FAN1_FLT<<(i*8))?"FAN_FAULT":
alarms&((ADM1031_ALARM_FAN1_MIN<<(i*8)))?"ALARM":"");
}
} else
printf("ERROR: Can't get fan%d data!\n", i+1);
free_the_label(&label);
}
if (!sensors_get_label_and_valid(*name, SENSORS_ADM1031_TEMP1,
&label, &valid)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP1, &cur)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP1_MIN, &low)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP1_MAX, &high)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP1_CRIT, &crit)) {
if (valid) {
print_label(label, 10);
print_temp_info(cur, high, low, MINMAX, 1, 0);
printf(" %s\n",
alarms&(ADM1031_ALARM_TEMP1_HIGH|ADM1031_ALARM_TEMP1_LOW)?"ALARM":"");
}
} else
printf("ERROR: Can't get temp1 temperature data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name, SENSORS_ADM1031_TEMP1_CRIT,
&label, &valid)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP1_CRIT, &cur)) {
if (valid) {
print_label(label, 10);
print_temp_info(cur, 0, 0, SINGLE, 0, 0);
printf(" %s\n",
alarms&ADM1031_ALARM_TEMP1_CRIT?"CRITICAL":"");
}
} else
printf("ERROR: Can't get temp1 temperature data!\n");
free_the_label(&label);
for (i=0; i < (is_1031 ? 2 : 1); i++) {
if (!sensors_get_label_and_valid(*name, SENSORS_ADM1031_TEMP2+i*10,
&label, &valid)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP2+i*10, &cur)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP2_MIN+i*10, &low)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP2_MAX+i*10, &high)) {
if (valid) {
print_label(label, 10);
print_temp_info(cur, high, low, MINMAX, 1, 0);
printf(" %s\n",
alarms&((ADM1031_ALARM_TEMP2_DIODE<<(i*8)))?"DISCONNECT":
alarms&((ADM1031_ALARM_TEMP2_HIGH<<(i*8))|
(ADM1031_ALARM_TEMP2_LOW<<(i*8)))?"ALARM":"");
}
} else
printf("ERROR: Can't get temp%d temperature data!\n", i+2);
free_the_label(&label);
if (!sensors_get_label_and_valid(*name, SENSORS_ADM1031_TEMP2_CRIT+i*10,
&label, &valid)
&& !sensors_get_feature(*name, SENSORS_ADM1031_TEMP2_CRIT+i*10, &cur)) {
if (valid) {
print_label(label, 10);
print_temp_info(cur, 0, 0, SINGLE, 0, 0);
printf(" %s\n",
alarms&((ADM1031_ALARM_TEMP2_CRIT<<(i*8)))?"CRITICAL":"");
}
} else
printf("ERROR: Can't get temp%d crit temperature data!\n", i+2);
free_the_label(&label);
}
}
void print_xeontemp(const sensors_chip_name *name)
{
char *label;
+1
View File
@@ -63,5 +63,6 @@ extern void print_lm90(const sensors_chip_name *name);
extern void print_max1619(const sensors_chip_name *name);
extern void print_xeontemp(const sensors_chip_name *name);
extern void print_max6650(const sensors_chip_name *name);
extern void print_adm1031(const sensors_chip_name *name);
#endif /* def PROG_SENSORS_CHIPS_H */
+2
View File
@@ -427,6 +427,8 @@ struct match matches[] = {
{ "xeontemp", print_xeontemp },
{ "max6650", print_max6650 },
{ "asb100", print_asb100 },
{ "adm1030", print_adm1031 },
{ "adm1031", print_adm1031 },
{ NULL, NULL }
};