From 1ac215f6a107ecfb1efd6dbb239677c01107e2ef Mon Sep 17 00:00:00 2001 From: Philip Edelbrock Date: Fri, 19 Mar 1999 21:11:33 +0000 Subject: [PATCH] (Phil) Added Fons Rademakers' MaxiLife driver. I've reviewed and applied the patch, but I would like some others to review these commits too, to be safe. git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@327 7894878c-1315-0410-8ee3-d5d059ff63e0 --- etc/sensors.conf.eg | 49 +++ kernel/chips/Module.mk | 2 +- kernel/chips/maxilife.c | 747 +++++++++++++++++++++++++++++++++++++++ kernel/include/sensors.h | 29 ++ lib/chips.c | 98 +++++ lib/chips.h | 48 +++ prog/sensors/chips.c | 195 ++++++++++ prog/sensors/chips.h | 1 + prog/sensors/main.c | 4 + 9 files changed, 1172 insertions(+), 1 deletion(-) create mode 100644 kernel/chips/maxilife.c diff --git a/etc/sensors.conf.eg b/etc/sensors.conf.eg index a99208d0..11883a02 100644 --- a/etc/sensors.conf.eg +++ b/etc/sensors.conf.eg @@ -322,3 +322,52 @@ chip "lm80-*" set in5_max -12 * 1.05 set in6_min -5 * 0.95 set in6_max -5 * 1.05 + + +chip "maxilife-cg-*" "maxilife-co-*" "maxilife-as-*" + + label fan1 "HDD Fan" + label fan2 "PCI Fan" + label fan3 "CPU Fan" + + label temp2 "PCI Temp" + label temp4 "HDD Temp" + label temp5 "CPU Temp" + + label vid1 "V+12" + +# vid1 need to be scaled by 6.337 other voltages +# require no scaling + + compute vid1 6.337*@ , @/6.337 + + +chip "maxilife-cg-*" + +# invalid temp1 + label temp3 "BX Temp" + + label vid2 "Vcpu1" + label vid3 "Vcpu2" +# invalid vid4 + + +chip "maxilife-co-*" + + label temp1 "CPU 1 Temp" + label temp3 "CPU 2 Temp" + + label vid2 "Vcpu1" + label vid3 "Vcpu2" + label vid4 "VcacheL2" + + +chip "maxilife-as-*" + +# invalid temp1 +# invalid temp3 + + label vid2 "Vcpu" +# invalid vid3 +# invalid vid4 + diff --git a/kernel/chips/Module.mk b/kernel/chips/Module.mk index a7378b64..7e27b701 100644 --- a/kernel/chips/Module.mk +++ b/kernel/chips/Module.mk @@ -27,7 +27,7 @@ KERNELCHIPSTARGETS := $(MODULE_DIR)/adm1021.o $(MODULE_DIR)/adm9240.o \ $(MODULE_DIR)/lm75.o $(MODULE_DIR)/lm78.o \ $(MODULE_DIR)/lm80.o $(MODULE_DIR)/ltc1710.o \ $(MODULE_DIR)/w83781d.o $(MODULE_DIR)/sis5595.o \ - $(MODULE_DIR)/icspll.o + $(MODULE_DIR)/icspll.o $(MODULE_DIR)/maxilife.o # Include all dependency files INCLUDEFILES += $(KERNELCHIPSTARGETS:.o=.d) diff --git a/kernel/chips/maxilife.c b/kernel/chips/maxilife.c new file mode 100644 index 00000000..1a65b157 --- /dev/null +++ b/kernel/chips/maxilife.c @@ -0,0 +1,747 @@ +/* + maxilife.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Fons Rademakers + + 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. +*/ + +/* The is the driver for the HP MaxiLife Health monitoring system + as used in the line of HP Kayak Workstation PC's. + + The driver supports the following MaxiLife firmware versions: + + 0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath) + 1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + 2) HP KAYAK XA (Pentium II Slot 1, monoprocessor) + + Currently firmware auto detection is not implemented. To use the + driver load it with the correct option for you Kayak. For example: + + insmod maxilife.o maxi_version=0 | 1 | 2 + + maxi_version=0 is the default +*/ + +static const char *version = "1.00 25/2/99 Fons Rademakers"; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "smbus.h" +#include "version.h" +#include "sensors.h" +#include "i2c.h" +#include "compat.h" + + +/*#define AUTODETECT /* try to autodetect MaxiLife version */ +#define NOWRITE /* don't allow writing to MaxiLife registers */ + + +/* The MaxiLife registers */ +#define MAXI_REG_TEMP(nr) (0x60 + (nr)) + +#define MAXI_REG_FAN(nr) (0x65 + (nr)) +#define MAXI_REG_FAN_MIN(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab) +#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3) +#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9) + +#define MAXI_REG_PLL 0xb9 +#define MAXI_REG_PLL_MIN 0xba +#define MAXI_REG_PLL_MAX 0xbb + +#define MAXI_REG_VID(nr) ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \ + (nr)==2 ? 0xd4 : 0xc5) +#define MAXI_REG_VID_MIN(nr) MAXI_REG_VID(nr)+1 +#define MAXI_REG_VID_MAX(nr) MAXI_REG_VID(nr)+2 + +#define MAXI_REG_DIAG_RT1 0x2c +#define MAXI_REG_DIAG_RT2 0x2d + +#define MAXI_REG_BIOS_CTRL 0x2a +#define MAXI_REG_LED_STATE 0x96 + +/* Conversions */ + /* 0xfe: fan off, 0xff: stopped (alarm) */ + /* 19531 / val * 60 == 1171860 / val */ +#define FAN_FROM_REG(val) ((val)==0xfe ? -1 : (val)==0xff ? 0 : \ + (1171860 / (val))) +#define FAN_TO_REG(val) (1171860 / (val)) + +#define TEMP_FROM_REG(val) ((val) * 5) +#define TEMP_TO_REG(val) ((val) / 5) +#define PLL_FROM_REG(val) (((val) * 1000) / 32) +#define PLL_TO_REG(val) (((val) * 32) / 1000) +#define VID_FROM_REG(val) ((val) ? (((val) * 27390) / 256) + 3208 : 0) +#define VID_TO_REG(val) ((((val) - 3208) * 256) / 27390) +#define ALARMS_FROM_REG(val) (val) + + + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* The following product codenames apply: + Cristal/Geronimo: HP KAYAK XU/XAs + (Dual Pentium II Slot 1, Deschutes/Klamath) + Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor) */ + +enum maxi_type { cristal, cognac, ashaki }; + +/* For each registered MaxiLife controller, we need to keep some data in + memory. That data is pointed to by maxi_list[NR]->data. The structure + itself is dynamically allocated, at the same time when a new MaxiLife + client is allocated. We assume MaxiLife will only be present on the + SMBus and not on the ISA bus. */ +struct maxi_data { + struct semaphore lock; + int sysctl_id; + enum maxi_type type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 fan_speed[3]; /* Register value */ + u8 fan_div[3]; /* Static value */ + u8 temp[5]; /* Register value */ + u8 temp_max[5]; /* Static value */ + u8 temp_hyst[5]; /* Static value */ + u8 pll; /* Register value */ + u8 pll_min; /* Register value */ + u8 pll_max; /* register value */ + u8 vid[4]; /* Register value */ + u8 vid_min[4]; /* Register value */ + u8 vid_max[4]; /* Register value */ + u16 alarms; /* Register encoding, combined */ +}; + + +static int maxi_init(void); +static int maxi_cleanup(void); + +static int maxi_attach_adapter(struct i2c_adapter *adapter); +static int maxi_detect_smbus(struct i2c_adapter *adapter); +static int maxi_detach_client(struct i2c_client *client); +static int maxi_detach_smbus(struct i2c_client *client); +static int maxi_new_client(struct i2c_adapter *adapter, + struct i2c_client *new_client); +static void maxi_remove_client(struct i2c_client *client); +static int maxi_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void maxi_inc_use (struct i2c_client *client); +static void maxi_dec_use (struct i2c_client *client); + +static int maxi_read_value(struct i2c_client *client, u8 register); +static int maxi_write_value(struct i2c_client *client, u8 register, u8 value); +static void maxi_update_client(struct i2c_client *client); +static void maxi_init_client(struct i2c_client *client); + +static void maxi_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void maxi_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void maxi_pll(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void maxi_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void maxi_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); + + +/* I choose here for semi-static MaxiLife allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +#define MAX_MAXI_NR 4 +static struct i2c_client *maxi_list[MAX_MAXI_NR]; + +/* The driver. I choose to use type i2c_driver, as at is identical to + the smbus_driver. */ +static struct i2c_driver maxi_driver = { + /* name */ "HP MaxiLife driver", + /* id */ I2C_DRIVERID_MAXILIFE, + /* flags */ DF_NOTIFY, + /* attach_adapter */ &maxi_attach_adapter, + /* detach_client */ &maxi_detach_client, + /* command */ &maxi_command, + /* inc_use */ &maxi_inc_use, + /* dec_use */ &maxi_dec_use +}; + +/* Used by maxi_init/cleanup */ +static int maxi_initialized = 0; + +/* Default firmware version. Use module option "maxi_version" + to set desired version. Auto detect is not yet working */ +static int maxi_version = cristal; + +/* The /proc/sys entries */ +/* These files are created for each detected MaxiLife processor. + 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 maxi_dir_table_template[] = { + { MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_fan }, + { MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_fan }, + { MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_fan }, + { MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_temp }, + { MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_temp }, + { MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_temp }, + { MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_temp }, + { MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_temp }, + { MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_pll }, + { MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_vid }, + { MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_vid }, + { MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_vid }, + { MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_vid }, + { MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real, NULL, &maxi_alarms }, + { 0 } +}; + +/* This function is called when: + - maxi_driver is inserted (when this module is loaded), for each + available adapter + - when a new adapter is inserted (and maxi_driver is still present) */ +int maxi_attach_adapter(struct i2c_adapter *adapter) +{ + return maxi_detect_smbus(adapter); +} + +/* This function is called whenever a client should be removed: + - maxi_driver is removed (when this module is unloaded) + - when an adapter is removed which has a maxi client (and maxi_driver + is still present). */ +int maxi_detach_client(struct i2c_client *client) +{ + return maxi_detach_smbus(client); +} + +int maxi_detect_smbus(struct i2c_adapter *adapter) +{ + u8 biosctl; + int address, err; + struct i2c_client *new_client; + enum maxi_type type; + const char *type_name, *client_name; + + /* OK, this is no detection. I know. It will do for now, though. */ + err = 0; + for (address = 0x10; (! err) && (address <= 0x14); address++) { + + /* Later on, we will keep a list of registered addresses for each + adapter, and check whether they are used here */ + + if (smbus_read_byte_data(adapter, address, MAXI_REG_LED_STATE) < 0) + continue; + +#ifdef AUTODETECT + /* The right way to get the platform info is to read the firmware + revision from serial EEPROM (addr=0x54), at offset 0x0045. + This is a string as: + "CG 00.04" -> Cristal [XU] / Geronimo [XAs] + "CO 00.03" -> Cognac [XU] + "AS 00.01" -> Ashaki [XA] */ + biosctl = smbus_read_byte_data(adapter, address, MAXI_REG_BIOS_CTRL); + smbus_write_byte_data(adapter, address, MAXI_REG_BIOS_CTRL, biosctl|4); + err = eeprom_read_byte_data(adapter, 0x54, 0x45); + smbus_write_byte_data(adapter, address, MAXI_REG_BIOS_CTRL, biosctl); +#endif + + if (maxi_version == cristal) { + type = cristal; + type_name = "maxilife-cg"; + client_name = "HP MaxiLife Rev CG 00.04"; + printk("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n"); + } else if (maxi_version == cognac) { + type = cognac; + type_name = "maxilife-co"; + client_name = "HP MaxiLife Rev CO 00.03"; + printk("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n"); + } else if (maxi_version == ashaki) { + type = ashaki; + type_name = "maxilife-as"; + client_name = "HP MaxiLife Rev AS 00.01"; + printk("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n"); + } else { +#if AUTODETECT + printk("maxilife: Warning: probed non-maxilife chip?!? (%x)\n", err); +#else + printk("maxilife: Error: specified wrong maxi_version (%d)\n", + maxi_version); +#endif + continue; + } + + /* Allocate space for a new client structure. To counter memory + fragmentation somewhat, we only do one kmalloc. */ + if (! (new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct maxi_data), + GFP_KERNEL))) { + err = -ENOMEM; + continue; + } + + /* Fill the new client structure with data */ + new_client->data = (struct maxi_data *) (new_client + 1); + new_client->addr = address; + strcpy(new_client->name, client_name); + ((struct maxi_data *) (new_client->data))->type = type; + if ((err = maxi_new_client(adapter, new_client))) + goto ERROR2; + + /* Tell i2c-core a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((err = sensors_register_entry(new_client, type_name, + maxi_dir_table_template)) < 0) + goto ERROR4; + ((struct maxi_data *) (new_client->data))->sysctl_id = err; + err = 0; + + /* Initialize the MaxiLife chip */ + maxi_init_client(new_client); + continue; + + /* OK, this is not exactly good programming practice, usually. + But it is very code-efficient in this case. */ +ERROR4: + i2c_detach_client(new_client); +ERROR3: + maxi_remove_client((struct i2c_client *) new_client); +ERROR2: + kfree(new_client); + } + return err; +} + +int maxi_detach_smbus(struct i2c_client *client) +{ + int err,i; + for (i = 0; i < MAX_MAXI_NR; i++) + if (client == maxi_list[i]) + break; + if ((i == MAX_MAXI_NR)) { + printk("maxilife: Client to detach not found.\n"); + return -ENOENT; + } + + sensors_deregister_entry(((struct maxi_data *)(client->data))->sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk("maxilife: Client deregistration failed, client not detached.\n"); + return err; + } + maxi_remove_client(client); + kfree(client); + return 0; +} + +/* Find a free slot, and initialize most of the fields */ +int maxi_new_client(struct i2c_adapter *adapter, + struct i2c_client *new_client) +{ + int i; + struct maxi_data *data; + + /* First, seek out an empty slot */ + for (i = 0; i < MAX_MAXI_NR; i++) + if (!maxi_list[i]) + break; + if (i == MAX_MAXI_NR) { + printk("maxilife: No empty slots left, recompile and heighten " + "MAX_MAXI_NR!\n"); + return -ENOMEM; + } + + maxi_list[i] = new_client; + new_client->id = i; + new_client->adapter = adapter; + new_client->driver = &maxi_driver; + data = new_client->data; + data->valid = 0; + data->lock = MUTEX; + data->update_lock = MUTEX; + return 0; +} + +/* Inverse of maxi_new_client */ +void maxi_remove_client(struct i2c_client *client) +{ + int i; + for (i = 0; i < MAX_MAXI_NR; i++) + if (client == maxi_list[i]) + maxi_list[i] = NULL; +} + +/* No commands defined yet */ +int maxi_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void maxi_inc_use (struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void maxi_dec_use (struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Read byte from specified register. */ +int maxi_read_value(struct i2c_client *client, u8 reg) +{ + return smbus_read_byte_data(client->adapter, client->addr, reg); +} + +/* Write byte to specified register. */ +int maxi_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return smbus_write_byte_data(client->adapter, client->addr, reg, value); +} + +/* Called when we have found a new MaxiLife. It should set limits, etc. */ +void maxi_init_client(struct i2c_client *client) +{ + /* start with default settings */ +} + +void maxi_update_client(struct i2c_client *client) +{ + struct maxi_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ+HZ/2 ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("maxilife: Starting MaxiLife update\n"); +#endif + for (i = 0; i < 5; i++) + data->temp[i] = maxi_read_value(client, MAXI_REG_TEMP(i)); + switch (data->type) { + case cristal: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max BX chipset temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + case cognac: + data->temp_max[0] = 120; /* max CPU1 temp */ + data->temp_hyst[0] = 110; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max CPU2 temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max reference CPU temp */ + data->temp_hyst[4] = 110; + break; + + case ashaki: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp[2] = 0; /* not valid */ + data->temp_max[2] = 0; + data->temp_hyst[2] = 0; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + + for (i = 0; i < 3; i++) { + data->fan[i] = maxi_read_value(client, MAXI_REG_FAN(i)); + data->fan_speed[i] = maxi_read_value(client, MAXI_REG_FAN_SPEED(i)); + data->fan_div[i] = 4; + if (data->type == ashaki) + data->fan_min[i] = maxi_read_value(client, MAXI_REG_FAN_MINAS(i)); + else + data->fan_min[i] = maxi_read_value(client, MAXI_REG_FAN_MIN(i)); + } + + data->pll = maxi_read_value(client, MAXI_REG_PLL); + data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN); + data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX); + + for (i = 0; i < 4; i++) { + data->vid[i] = maxi_read_value(client, MAXI_REG_VID(i)); + data->vid_min[i] = maxi_read_value(client, MAXI_REG_VID_MIN(i)); + data->vid_max[i] = maxi_read_value(client, MAXI_REG_VID_MAX(i)); + } + switch (data->type) { + case cristal: + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + case cognac: + break; + + case ashaki: + data->vid[1] = 0; /* no voltage CPU 2 */ + data->vid_min[1] = 0; + data->vid_max[1] = 0; + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + + data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) + + (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +/* 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 data + 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 maxi_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr-1]); + results[1] = data->fan_div[nr-1]; + results[2] = FAN_FROM_REG(data->fan[nr-1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->fan_min[nr-1] = FAN_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_FAN_MIN(nr), data->fan_min[nr-1]); + } +#endif + } +} + +void maxi_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_max[nr-1]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr-1]); + results[2] = TEMP_FROM_REG(data->temp[nr-1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* temperature range can not be changed */ + } +} + +void maxi_pll(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = PLL_FROM_REG(data->pll_min); + results[1] = PLL_FROM_REG(data->pll_max); + results[2] = PLL_FROM_REG(data->pll); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->pll_min = PLL_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_PLL_MIN, data->pll_min); + } + if (*nrels_mag >= 2) { + data->pll_max = PLL_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_PLL_MAX, data->pll_max); + } +#endif + } +} + +void maxi_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr = ctl_name - MAXI_SYSCTL_VID1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 4; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = VID_FROM_REG(data->vid_min[nr-1]); + results[1] = VID_FROM_REG(data->vid_max[nr-1]); + results[2] = VID_FROM_REG(data->vid[nr-1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->vid_min[nr-1] = VID_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_VID_MIN(nr), data->vid_min[nr-1]); + } + if (*nrels_mag >= 2) { + data->vid_max[nr-1] = VID_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_VID_MAX(nr), data->vid_max[nr-1]); + } +#endif + } +} + +void maxi_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +int maxi_init(void) +{ + int res; + + printk("maxilife: Version %s (lm_sensors %s (%s))\n", version, + LM_VERSION, LM_DATE); + maxi_initialized = 0; + + if ((res = i2c_add_driver(&maxi_driver))) { + printk("maxilife: Driver registration failed, module not inserted.\n"); + maxi_cleanup(); + return res; + } + maxi_initialized++; + return 0; +} + +int maxi_cleanup(void) +{ + int res; + + if (maxi_initialized >= 1) { + if ((res = i2c_del_driver(&maxi_driver))) { + printk("maxilife: Driver deregistration failed, module not removed.\n"); + return res; + } + maxi_initialized--; + } + return 0; +} + + +#ifdef MODULE + +MODULE_AUTHOR("Fons Rademakers "); +MODULE_DESCRIPTION("HP MaxiLife driver"); +MODULE_PARM(maxi_version, "i"); +MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version"); + +int init_module(void) +{ + return maxi_init(); +} + +int cleanup_module(void) +{ + return maxi_cleanup(); +} + +#endif /* MODULE */ + + diff --git a/kernel/include/sensors.h b/kernel/include/sensors.h index 0f88b9e3..61620453 100644 --- a/kernel/include/sensors.h +++ b/kernel/include/sensors.h @@ -285,6 +285,7 @@ extern int sensors_detect(struct i2c_adapter *adapter, #define I2C_DRIVERID_LTC1710 1010 #define I2C_DRIVERID_SIS5595 1011 #define I2C_DRIVERID_ICSPLL 1012 +#define I2C_DRIVERID_MAXILIFE 1012 /* Sysctl IDs */ #ifdef DEV_HWMON @@ -452,5 +453,33 @@ struct sensors_chips_data { #define LM80_ALARM_BTI 0x0200 #define LM80_ALARM_INT_IN 0x0080 +#define MAXI_SYSCTL_FAN1 1101 /* Rotations/min */ +#define MAXI_SYSCTL_FAN2 1102 /* Rotations/min */ +#define MAXI_SYSCTL_FAN3 1103 /* Rotations/min */ +#define MAXI_SYSCTL_TEMP1 1201 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP2 1202 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP3 1203 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP4 1204 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP5 1205 /* Degrees Celcius */ +#define MAXI_SYSCTL_PLL 1301 /* MHz */ +#define MAXI_SYSCTL_VID1 1401 /* Volts / 6.337 */ +#define MAXI_SYSCTL_VID2 1402 /* Volts */ +#define MAXI_SYSCTL_VID3 1403 /* Volts */ +#define MAXI_SYSCTL_VID4 1404 /* Volts */ +#define MAXI_SYSCTL_ALARMS 2001 /* Bitvector (see below) */ + +#define MAXI_ALARM_VID4 0x0001 +#define MAXI_ALARM_TEMP2 0x0002 +#define MAXI_ALARM_VID1 0x0004 +#define MAXI_ALARM_VID2 0x0008 +#define MAXI_ALARM_VID3 0x0010 +#define MAXI_ALARM_PLL 0x0080 +#define MAXI_ALARM_TEMP4 0x0100 +#define MAXI_ALARM_TEMP5 0x0200 +#define MAXI_ALARM_FAN1 0x1000 +#define MAXI_ALARM_FAN2 0x2000 +#define MAXI_ALARM_FAN3 0x4000 + + #define ICSPLL_SYSCTL1 1000 #endif /* def SENSORS_SENSORS_H */ diff --git a/lib/chips.c b/lib/chips.c index 98748955..47a3f3a4 100644 --- a/lib/chips.c +++ b/lib/chips.c @@ -1077,6 +1077,101 @@ static sensors_chip_feature sis5595_features[] = { 0 } }; +static sensors_chip_feature maxi_features[] = + { + { SENSORS_MAXI_FAN1, "fan1", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_FAN1, VALUE(3), 0 }, + { SENSORS_MAXI_FAN2, "fan2", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_FAN2, VALUE(3), 0 }, + { SENSORS_MAXI_FAN3, "fan3", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_FAN3, VALUE(3), 0 }, + { SENSORS_MAXI_FAN1_MIN, "fan1_min", SENSORS_MAXI_FAN1, SENSORS_MAXI_FAN1, + SENSORS_MODE_RW, MAXI_SYSCTL_FAN1, VALUE(1), 0 }, + { SENSORS_MAXI_FAN2_MIN, "fan2_min", SENSORS_MAXI_FAN2, SENSORS_MAXI_FAN2, + SENSORS_MODE_RW, MAXI_SYSCTL_FAN2, VALUE(1), 0 }, + { SENSORS_MAXI_FAN3_MIN, "fan3_min", SENSORS_MAXI_FAN3, SENSORS_MAXI_FAN3, + SENSORS_MODE_RW, MAXI_SYSCTL_FAN3, VALUE(1), 0 }, + { SENSORS_MAXI_FAN1_DIV, "fan1_div", SENSORS_MAXI_FAN1, SENSORS_MAXI_FAN1, + SENSORS_MODE_R, MAXI_SYSCTL_FAN1, VALUE(2), 0 }, + { SENSORS_MAXI_FAN2_DIV, "fan2_div", SENSORS_MAXI_FAN2, SENSORS_MAXI_FAN2, + SENSORS_MODE_R, MAXI_SYSCTL_FAN2, VALUE(2), 0 }, + { SENSORS_MAXI_FAN3_DIV, "fan3_div", SENSORS_MAXI_FAN3, SENSORS_MAXI_FAN3, + SENSORS_MODE_R, MAXI_SYSCTL_FAN3, VALUE(2), 0 }, + { SENSORS_MAXI_TEMP1, "temp1", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_TEMP1, VALUE(3), 1 }, + { SENSORS_MAXI_TEMP2, "temp2", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_TEMP2, VALUE(3), 1 }, + { SENSORS_MAXI_TEMP3, "temp3", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_TEMP3, VALUE(3), 1 }, + { SENSORS_MAXI_TEMP4, "temp4", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_TEMP4, VALUE(3), 1 }, + { SENSORS_MAXI_TEMP5, "temp5", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_TEMP5, VALUE(3), 1 }, + { SENSORS_MAXI_TEMP1_MAX, "temp1_max", SENSORS_MAXI_TEMP1, + SENSORS_MAXI_TEMP1, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP1, VALUE(1), 1 }, + { SENSORS_MAXI_TEMP2_MAX, "temp2_max", SENSORS_MAXI_TEMP2, + SENSORS_MAXI_TEMP2, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP2, VALUE(1), 1 }, + { SENSORS_MAXI_TEMP3_MAX, "temp3_max", SENSORS_MAXI_TEMP3, + SENSORS_MAXI_TEMP3, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP3, VALUE(1), 1 }, + { SENSORS_MAXI_TEMP4_MAX, "temp4_max", SENSORS_MAXI_TEMP4, + SENSORS_MAXI_TEMP4, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP4, VALUE(1), 1 }, + { SENSORS_MAXI_TEMP5_MAX, "temp5_max", SENSORS_MAXI_TEMP5, + SENSORS_MAXI_TEMP5, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP5, VALUE(1), 1 }, + { SENSORS_MAXI_TEMP1_HYST, "temp1_hyst", SENSORS_MAXI_TEMP1, + SENSORS_MAXI_TEMP1, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP1, VALUE(2), 1 }, + { SENSORS_MAXI_TEMP2_HYST, "temp2_hyst", SENSORS_MAXI_TEMP2, + SENSORS_MAXI_TEMP2, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP2, VALUE(2), 1 }, + { SENSORS_MAXI_TEMP3_HYST, "temp3_hyst", SENSORS_MAXI_TEMP3, + SENSORS_MAXI_TEMP3, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP3, VALUE(2), 1 }, + { SENSORS_MAXI_TEMP4_HYST, "temp4_hyst", SENSORS_MAXI_TEMP4, + SENSORS_MAXI_TEMP4, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP4, VALUE(2), 1 }, + { SENSORS_MAXI_TEMP5_HYST, "temp5_hyst", SENSORS_MAXI_TEMP5, + SENSORS_MAXI_TEMP5, SENSORS_MODE_R, + MAXI_SYSCTL_TEMP5, VALUE(2), 1 }, + { SENSORS_MAXI_PLL, "pll", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_PLL, VALUE(3), 2 }, + { SENSORS_MAXI_PLL_MIN, "pll_min", SENSORS_MAXI_PLL, SENSORS_MAXI_PLL, + SENSORS_MODE_RW, MAXI_SYSCTL_PLL, VALUE(1), 2 }, + { SENSORS_MAXI_PLL_MAX, "pll_max", SENSORS_MAXI_PLL, SENSORS_MAXI_PLL, + SENSORS_MODE_RW, MAXI_SYSCTL_PLL, VALUE(2), 2 }, + { SENSORS_MAXI_VID1, "vid1", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_VID1, VALUE(3), 4 }, + { SENSORS_MAXI_VID2, "vid2", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_VID2, VALUE(3), 4 }, + { SENSORS_MAXI_VID3, "vid3", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_VID3, VALUE(3), 4 }, + { SENSORS_MAXI_VID4, "vid4", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_VID4, VALUE(3), 4 }, + { SENSORS_MAXI_VID1_MIN, "vid1_min", SENSORS_MAXI_VID1, SENSORS_MAXI_VID1, + SENSORS_MODE_RW, MAXI_SYSCTL_VID1, VALUE(1), 4 }, + { SENSORS_MAXI_VID2_MIN, "vid2_min", SENSORS_MAXI_VID2, SENSORS_MAXI_VID2, + SENSORS_MODE_RW, MAXI_SYSCTL_VID2, VALUE(1), 4 }, + { SENSORS_MAXI_VID3_MIN, "vid3_min", SENSORS_MAXI_VID3, SENSORS_MAXI_VID3, + SENSORS_MODE_RW, MAXI_SYSCTL_VID3, VALUE(1), 4 }, + { SENSORS_MAXI_VID4_MIN, "vid4_min", SENSORS_MAXI_VID4, SENSORS_MAXI_VID4, + SENSORS_MODE_RW, MAXI_SYSCTL_VID4, VALUE(1), 4 }, + { SENSORS_MAXI_VID1_MAX, "vid1_max", SENSORS_MAXI_VID1, SENSORS_MAXI_VID1, + SENSORS_MODE_RW, MAXI_SYSCTL_VID1, VALUE(2), 4 }, + { SENSORS_MAXI_VID2_MAX, "vid2_max", SENSORS_MAXI_VID2, SENSORS_MAXI_VID2, + SENSORS_MODE_RW, MAXI_SYSCTL_VID2, VALUE(2), 4 }, + { SENSORS_MAXI_VID3_MAX, "vid3_max", SENSORS_MAXI_VID3, SENSORS_MAXI_VID3, + SENSORS_MODE_RW, MAXI_SYSCTL_VID3, VALUE(2), 4 }, + { SENSORS_MAXI_VID4_MAX, "vid4_max", SENSORS_MAXI_VID4, SENSORS_MAXI_VID4, + SENSORS_MODE_RW, MAXI_SYSCTL_VID4, VALUE(2), 4 }, + { SENSORS_MAXI_ALARMS, "alarms", SENSORS_NO_MAPPING, SENSORS_NO_MAPPING, + SENSORS_MODE_R, MAXI_SYSCTL_ALARMS, VALUE(1), 0 }, + { 0 } + }; + sensors_chip_features sensors_chip_features_list[] = { { SENSORS_LM78_PREFIX, lm78_features }, @@ -1092,6 +1187,9 @@ sensors_chip_features sensors_chip_features_list[] = { SENSORS_ADM9240_PREFIX, adm9240_features }, { SENSORS_ADM1021_PREFIX, adm1021_features }, { SENSORS_SIS5595_PREFIX, sis5595_features }, + { SENSORS_MAXI_CG_PREFIX, maxi_features }, + { SENSORS_MAXI_CO_PREFIX, maxi_features }, + { SENSORS_MAXI_AS_PREFIX, maxi_features }, { 0 } }; diff --git a/lib/chips.h b/lib/chips.h index 4bf4d932..0dee19f2 100644 --- a/lib/chips.h +++ b/lib/chips.h @@ -505,4 +505,52 @@ #define SENSORS_SIS5595_FAN3_DIV 73 /* R (yes, really! */ #define SENSORS_SIS5595_ALARMS 81 /* R */ + +/* HP MaxiLife chips */ + +#define SENSORS_MAXI_CG_PREFIX "maxilife-cg" +#define SENSORS_MAXI_CO_PREFIX "maxilife-co" +#define SENSORS_MAXI_AS_PREFIX "maxilife-as" + +#define SENSORS_MAXI_FAN1 1 /* R */ +#define SENSORS_MAXI_FAN2 2 /* R */ +#define SENSORS_MAXI_FAN3 3 /* R */ +#define SENSORS_MAXI_FAN1_MIN 11 /* RW */ +#define SENSORS_MAXI_FAN2_MIN 12 /* RW */ +#define SENSORS_MAXI_FAN3_MIN 13 /* RW */ +#define SENSORS_MAXI_FAN1_DIV 14 /* R */ +#define SENSORS_MAXI_FAN2_DIV 15 /* R */ +#define SENSORS_MAXI_FAN3_DIV 16 /* R */ +#define SENSORS_MAXI_TEMP1 21 /* R */ +#define SENSORS_MAXI_TEMP2 22 /* R */ +#define SENSORS_MAXI_TEMP3 23 /* R */ +#define SENSORS_MAXI_TEMP4 24 /* R */ +#define SENSORS_MAXI_TEMP5 25 /* R */ +#define SENSORS_MAXI_TEMP1_MAX 31 /* R */ +#define SENSORS_MAXI_TEMP2_MAX 32 /* R */ +#define SENSORS_MAXI_TEMP3_MAX 33 /* R */ +#define SENSORS_MAXI_TEMP4_MAX 34 /* R */ +#define SENSORS_MAXI_TEMP5_MAX 35 /* R */ +#define SENSORS_MAXI_TEMP1_HYST 41 /* R */ +#define SENSORS_MAXI_TEMP2_HYST 42 /* R */ +#define SENSORS_MAXI_TEMP3_HYST 43 /* R */ +#define SENSORS_MAXI_TEMP4_HYST 44 /* R */ +#define SENSORS_MAXI_TEMP5_HYST 45 /* R */ +#define SENSORS_MAXI_PLL 51 /* R */ +#define SENSORS_MAXI_PLL_MIN 52 /* RW */ +#define SENSORS_MAXI_PLL_MAX 53 /* RW */ +#define SENSORS_MAXI_VID1 61 /* R */ +#define SENSORS_MAXI_VID2 62 /* R */ +#define SENSORS_MAXI_VID3 63 /* R */ +#define SENSORS_MAXI_VID4 64 /* R */ +#define SENSORS_MAXI_VID1_MIN 71 /* RW */ +#define SENSORS_MAXI_VID2_MIN 72 /* RW */ +#define SENSORS_MAXI_VID3_MIN 73 /* RW */ +#define SENSORS_MAXI_VID4_MIN 74 /* RW */ +#define SENSORS_MAXI_VID1_MAX 81 /* RW */ +#define SENSORS_MAXI_VID2_MAX 82 /* RW */ +#define SENSORS_MAXI_VID3_MAX 83 /* RW */ +#define SENSORS_MAXI_VID4_MAX 84 /* RW */ +#define SENSORS_MAXI_ALARMS 91 /* R */ + #endif /* def LIB_SENSORS_CHIPS_H */ diff --git a/prog/sensors/chips.c b/prog/sensors/chips.c index f026a54f..b5fa88e9 100644 --- a/prog/sensors/chips.c +++ b/prog/sensors/chips.c @@ -752,3 +752,198 @@ void print_w83781d(const sensors_chip_name *name) printf("ERROR: Can't get BEEP data!\n"); free_the_label(&label); } + +void print_maxilife(const sensors_chip_name *name) +{ + char *label = NULL; + double cur, min, max; + int alarms; + + if (!sensors_get_feature(*name, SENSORS_MAXI_ALARMS, &cur)) + alarms = cur + 0.5; + else { + printf("ERROR: Can't get alarm data!\n"); + alarms = 0; + } + + if (!sensors_get_label(*name, SENSORS_MAXI_TEMP1, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP1, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP1_MAX, &max) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP1_HYST, &min)) { + if (cur || max || min) { + print_label(label, 12); + printf("%+3.1f C (limit = %+3.1f C, hysteresis = %+3.1f C)\n", + cur, max, min); + } + } else + printf("ERROR: Can't get TEMP1 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_TEMP2, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP2, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP2_MAX, &max) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP2_HYST, &min)) { + if (cur || max || min) { + print_label(label, 12); + printf("%+3.1f C (limit = %+3.1f C, hysteresis = %+3.1f C) %s\n", + cur, max, min, alarms&MAXI_ALARM_TEMP2 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get TEMP2 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_TEMP3, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP3, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP3_MAX, &max) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP3_HYST, &min)) { + if (cur || max || min) { + print_label(label, 12); + printf("%+3.1f C (limit = %+3.1f C, hysteresis = %+3.1f C)\n", + cur, max, min); + } + } else + printf("ERROR: Can't get TEMP3 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_TEMP4, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP4, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP4_MAX, &max) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP4_HYST, &min)) { + if (cur || max || min) { + print_label(label, 12); + printf("%+3.1f C (limit = %+3.1f C, hysteresis = %+3.1f C) %s\n", + cur, max, min, alarms&MAXI_ALARM_TEMP4 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get TEMP4 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_TEMP5, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP5, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP5_MAX, &max) && + !sensors_get_feature(*name, SENSORS_MAXI_TEMP5_HYST, &min)) { + if (cur || max || min) { + print_label(label, 12); + printf("%+3.1f C (limit = %+3.1f C, hysteresis = %+3.1f C) %s\n", + cur, max, min, alarms&MAXI_ALARM_TEMP5 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get TEMP5 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_FAN1, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN1, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN1_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN1_DIV, &max)) { + if (cur || min || max) { + print_label(label, 12); + if (cur < 0) + printf(" OFF (min = %4.0f RPM, div = %1.0f) %s\n", + min/max, max, alarms&MAXI_ALARM_FAN1 ? "ALARM" : ""); + else + printf("%5.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n", + cur/max, min/max, max, alarms&MAXI_ALARM_FAN1 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get FAN1 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_FAN2, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN2, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN2_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN2_DIV, &max)) { + if (cur || min || max) { + print_label(label, 12); + if (cur < 0) + printf(" OFF (min = %4.0f RPM, div = %1.0f) %s\n", + min/max, max, alarms&MAXI_ALARM_FAN2 ? "ALARM" : ""); + else + printf("%5.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n", + cur/max, min/max, max, alarms&MAXI_ALARM_FAN2 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get FAN2 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_FAN3, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN3, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN3_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_FAN3_DIV, &max)) { + if (cur || min || max) { + print_label(label, 12); + if (cur < 0) + printf(" OFF (min = %4.0f RPM, div = %1.0f) %s\n", + min/max, max, alarms&MAXI_ALARM_FAN3 ? "ALARM" : ""); + else + printf("%5.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n", + cur/max, min/max, max, alarms&MAXI_ALARM_FAN3 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get FAN3 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_PLL, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_PLL, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_PLL_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_PLL_MAX, &max)) { + if (cur || min || max) { + print_label(label, 12); + printf("%4.2f MHz (min = %4.2f MHz, max = %4.2f MHz) %s\n", + cur, min, max, alarms&MAXI_ALARM_PLL ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get PLL data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_VID1, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_VID1, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_VID1_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_VID1_MAX, &max)) { + if (cur || min || max) { + print_label(label, 12); + printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n", + cur, min, max, alarms&MAXI_ALARM_VID1 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get VID1 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_VID2, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_VID2, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_VID2_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_VID2_MAX, &max)) { + if (cur || min || max) { + print_label(label, 12); + printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n", + cur, min, max, alarms&MAXI_ALARM_VID2 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get VID2 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_VID3, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_VID3, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_VID3_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_VID3_MAX, &max)) { + if (cur || min || max) { + print_label(label, 12); + printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n", + cur, min, max, alarms&MAXI_ALARM_VID3 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get VID3 data!\n"); + free_the_label(&label); + + if (!sensors_get_label(*name, SENSORS_MAXI_VID4, &label) && + !sensors_get_feature(*name, SENSORS_MAXI_VID4, &cur) && + !sensors_get_feature(*name, SENSORS_MAXI_VID4_MIN, &min) && + !sensors_get_feature(*name, SENSORS_MAXI_VID4_MAX, &max)) { + if (cur || min || max) { + print_label(label, 12); + printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n", + cur, min, max, alarms&MAXI_ALARM_VID4 ? "ALARM" : ""); + } + } else + printf("ERROR: Can't get VID4 data!\n"); + free_the_label(&label); +} diff --git a/prog/sensors/chips.h b/prog/sensors/chips.h index 160ac760..d066bfde 100644 --- a/prog/sensors/chips.h +++ b/prog/sensors/chips.h @@ -29,5 +29,6 @@ extern void print_lm78(const sensors_chip_name *name); extern void print_gl518(const sensors_chip_name *name); extern void print_lm80(const sensors_chip_name *name); extern void print_w83781d(const sensors_chip_name *name); +extern void print_maxilife(const sensors_chip_name *name); #endif /* def PROG_SENSORS_CHIPS_H */ diff --git a/prog/sensors/main.c b/prog/sensors/main.c index 0411745d..28c24045 100644 --- a/prog/sensors/main.c +++ b/prog/sensors/main.c @@ -258,6 +258,10 @@ void do_a_print(sensors_chip_name name) (!strcmp(name.prefix,"w83782d")) || (!strcmp(name.prefix,"w83783s"))) print_w83781d(&name); + else if (!strcmp(name.prefix,"maxilife-cg") || + !strcmp(name.prefix,"maxilife-co") || + !strcmp(name.prefix,"maxilife-as")) + print_maxilife(&name); else print_unknown_chip(&name); printf("\n");