From 103344795a56942a471a316d3a7b7aa0293637f9 Mon Sep 17 00:00:00 2001 From: "Mark D. Studebaker" Date: Mon, 25 Aug 2003 00:11:02 +0000 Subject: [PATCH] add xeontemp driver git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1978 7894878c-1315-0410-8ee3-d5d059ff63e0 --- CHANGES | 1 + doc/chips/xeontemp | 51 ++++++ kernel/chips/Module.mk | 7 +- kernel/chips/xeontemp.c | 384 ++++++++++++++++++++++++++++++++++++++++ lib/chips.c | 20 +++ lib/chips.h | 7 + prog/sensors/chips.c | 44 +++++ prog/sensors/chips.h | 1 + prog/sensors/main.c | 4 + 9 files changed, 516 insertions(+), 3 deletions(-) create mode 100644 doc/chips/xeontemp create mode 100644 kernel/chips/xeontemp.c diff --git a/CHANGES b/CHANGES index 69ee4e26..b2aa841a 100644 --- a/CHANGES +++ b/CHANGES @@ -60,6 +60,7 @@ ask CVS about it: Module lm90: New Module saa1064: New Module w83781d: Enhance chip detection + Module xeontemp: New Program decode-dimms.pl: Add Direct Rambus (DRDRAM) Mark Rambus as unsupported Program decode-edid.pl: New diff --git a/doc/chips/xeontemp b/doc/chips/xeontemp new file mode 100644 index 00000000..0752f0a6 --- /dev/null +++ b/doc/chips/xeontemp @@ -0,0 +1,51 @@ +Kernel driver `adm1021.o' +========================= + +Status: Complete; tested for most devices except Xeon. + +Supported chips: + * Intel Xeon and other processors + Prefix `xeontemp' + Addresses scanned: I2C 0x18, 0x1a, 0x29, 0x2b, 0x4c, 0x4e + Datasheet: Publicly available at the Intel website + +Authors: Frodo Looijaard ,Philip Edelbrock + , and Mark D. Studebaker (mdsxyz123@yahoo.com) + +License: GPL + + +Module Parameters +----------------- + +* force: short array (min = 1, max = 48) + List of adapter,address pairs to boldly assume to be present +* force_xeontemp: short array (min = 1, max = 48) + List of adapter,address pairs which are unquestionably assumed to contain + a `xeontemp' 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 +* read_only: int + Don't set any values, read only mode + + +Description +----------- + +Xeon and other Intel processors with an embedded i2c temperature +sensor look very similar to an adm1021. +The Xeon has only one sensor where the adm1021 has two. +Not all Xeon processors have this feature. +If you have an Intel processor, and sensors-detect recommended +the adm1021 driver, and the address is one of +0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e, and when using the +adm1021 driver, temp1 is invalid but temp2 looks correct, then +it is likely you should use this driver instead. + +See the documentation for adm1021 for more information. diff --git a/kernel/chips/Module.mk b/kernel/chips/Module.mk index 9707a867..06b04210 100644 --- a/kernel/chips/Module.mk +++ b/kernel/chips/Module.mk @@ -29,13 +29,14 @@ ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; KERNELCHIPSTARGETS += $(MODULE_DIR)/bmcsensors.o endif KERNELCHIPSTARGETS += $(MODULE_DIR)/ds1307.o +KERNELCHIPSTARGETS += $(MODULE_DIR)/lm83.o +KERNELCHIPSTARGETS += $(MODULE_DIR)/lm90.o +KERNELCHIPSTARGETS += $(MODULE_DIR)/saa1064.o KERNELCHIPSTARGETS += $(MODULE_DIR)/smartbatt.o KERNELCHIPSTARGETS += $(MODULE_DIR)/smbus-arp.o KERNELCHIPSTARGETS += $(MODULE_DIR)/vt8231.o KERNELCHIPSTARGETS += $(MODULE_DIR)/w83627hf.o -KERNELCHIPSTARGETS += $(MODULE_DIR)/lm83.o -KERNELCHIPSTARGETS += $(MODULE_DIR)/saa1064.o -KERNELCHIPSTARGETS += $(MODULE_DIR)/lm90.o +KERNELCHIPSTARGETS += $(MODULE_DIR)/xeontemp.o # These targets ARE included in 'mkpatch', except for LTC1710, which we diff --git a/kernel/chips/xeontemp.c b/kernel/chips/xeontemp.c new file mode 100644 index 00000000..5bfd0354 --- /dev/null +++ b/kernel/chips/xeontemp.c @@ -0,0 +1,384 @@ +/* + xeontemp.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999,2003 Frodo Looijaard , + Philip Edelbrock , and + Mark D. Studebaker + + 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 Xeon temperature sensor looks just like an ADM1021 with the remote + sensor only. There is are no ID registers so detection is difficult. */ + +#include +#include +#include +#include +#include +#include "version.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x18, 0x1a, 0x29, 0x2b, + 0x4c, 0x4e, SENSORS_I2C_END +}; +static unsigned short normal_i2c_range[] = { 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(xeontemp); + +/* xeontemp constants specified below */ + +/* The registers */ +/* Read-only */ +#define XEONTEMP_REG_REMOTE_TEMP 0x01 +#define XEONTEMP_REG_STATUS 0x02 +/* These use different addresses for reading/writing */ +#define XEONTEMP_REG_CONFIG_R 0x03 +#define XEONTEMP_REG_CONFIG_W 0x09 +#define XEONTEMP_REG_CONV_RATE_R 0x04 +#define XEONTEMP_REG_CONV_RATE_W 0x0A +/* limits */ +#define XEONTEMP_REG_REMOTE_TOS_R 0x07 +#define XEONTEMP_REG_REMOTE_TOS_W 0x0D +#define XEONTEMP_REG_REMOTE_THYST_R 0x08 +#define XEONTEMP_REG_REMOTE_THYST_W 0x0E +/* write-only */ +#define XEONTEMP_REG_ONESHOT 0x0F + +#define XEONTEMP_ALARM_RTEMP (XEONTEMP_ALARM_RTEMP_HIGH | XEONTEMP_ALARM_RTEMP_LOW\ + | XEONTEMP_ALARM_RTEMP_NA) +#define XEONTEMP_ALARM_ALL XEONTEMP_ALARM_RTEMP + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +/* Conversions note: 1021 uses normal integer signed-byte format*/ +#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) + +/* Each client has this additional data */ +struct xeontemp_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms; + u8 fail; +}; + +static int xeontemp_attach_adapter(struct i2c_adapter *adapter); +static int xeontemp_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void xeontemp_init_client(struct i2c_client *client); +static int xeontemp_detach_client(struct i2c_client *client); +static int xeontemp_read_value(struct i2c_client *client, u8 reg); +static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask); +static int xeontemp_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void xeontemp_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void xeontemp_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void xeontemp_update_client(struct i2c_client *client); + +/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ +static int read_only = 0; + + +/* This is the driver that will be inserted */ +static struct i2c_driver xeontemp_driver = { + .owner = THIS_MODULE, + .name = "Xeon temp sensor driver", + .id = I2C_DRIVERID_XEONTEMP, + .flags = I2C_DF_NOTIFY, + .attach_adapter = xeontemp_attach_adapter, + .detach_client = xeontemp_detach_client, +}; + +/* -- SENSORS SYSCTL START -- */ + +#define XEONTEMP_SYSCTL_REMOTE_TEMP 1201 +#define XEONTEMP_SYSCTL_ALARMS 1203 + +#define XEONTEMP_ALARM_RTEMP_HIGH 0x10 +#define XEONTEMP_ALARM_RTEMP_LOW 0x08 +#define XEONTEMP_ALARM_RTEMP_NA 0x04 + +/* -- SENSORS SYSCTL END -- */ + +/* These files are created for each detected xeontemp. 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 xeontemp_dir_table_template[] = { + {XEONTEMP_SYSCTL_REMOTE_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &xeontemp_remote_temp}, + {XEONTEMP_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &xeontemp_alarms}, + {0} +}; + +static int xeontemp_id = 0; + +static int xeontemp_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, xeontemp_detect); +} + +static int xeontemp_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct xeontemp_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto error0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access xeontemp_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct xeontemp_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto error0; + } + + data = (struct xeontemp_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &xeontemp_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + (xeontemp_read_value(new_client, XEONTEMP_REG_STATUS) & + 0x03) != 0x00) + goto error1; + } + + /* Determine the chip type. */ + + if (kind <= 0) { + kind = xeontemp; + } + + type_name = "xeontemp"; + client_name = "xeon sensors"; + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = xeontemp_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 error3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + xeontemp_dir_table_template)) < 0) { + err = i; + goto error4; + } + data->sysctl_id = i; + + xeontemp_init_client(new_client); + return 0; + + error4: + i2c_detach_client(new_client); + error3: + error1: + kfree(new_client); + error0: + return err; +} + +static void xeontemp_init_client(struct i2c_client *client) +{ + /* Enable ADC and disable suspend mode */ + xeontemp_write_value(client, XEONTEMP_REG_CONFIG_W, 0); + /* Set Conversion rate to 1/sec (this can be tinkered with) */ + xeontemp_write_value(client, XEONTEMP_REG_CONV_RATE_W, 0x04); +} + +static int xeontemp_detach_client(struct i2c_client *client) +{ + + int err; + + i2c_deregister_entry(((struct xeontemp_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("xeontemp.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + + +/* All registers are byte-sized */ +static int xeontemp_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* only update value if read succeeded; set fail bit if failed */ +static int xeontemp_rd_good(u8 *val, struct i2c_client *client, u8 reg, u8 mask) +{ + int i; + struct xeontemp_data *data = client->data; + + i = i2c_smbus_read_byte_data(client, reg); + if (i < 0) { + data->fail |= mask; + return i; + } + *val = i; + return 0; +} + +static int xeontemp_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (read_only > 0) + return 0; + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static void xeontemp_update_client(struct i2c_client *client) +{ + struct xeontemp_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 xeontemp update\n"); +#endif + + data->fail = 0; + xeontemp_rd_good(&(data->remote_temp), client, + XEONTEMP_REG_REMOTE_TEMP, XEONTEMP_ALARM_RTEMP); + xeontemp_rd_good(&(data->remote_temp_os), client, + XEONTEMP_REG_REMOTE_TOS_R, XEONTEMP_ALARM_RTEMP); + xeontemp_rd_good(&(data->remote_temp_hyst), client, + XEONTEMP_REG_REMOTE_THYST_R, + XEONTEMP_ALARM_RTEMP); + data->alarms = XEONTEMP_ALARM_ALL; + if (!xeontemp_rd_good(&(data->alarms), client, + XEONTEMP_REG_STATUS, 0)) + data->alarms &= XEONTEMP_ALARM_ALL; + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +void xeontemp_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct xeontemp_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + xeontemp_update_client(client); + results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[1] = TEMP_FROM_REG(data->remote_temp_hyst); + results[2] = TEMP_FROM_REG(data->remote_temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->remote_temp_os = TEMP_TO_REG(results[0]); + xeontemp_write_value(client, + XEONTEMP_REG_REMOTE_TOS_W, + data->remote_temp_os); + } + if (*nrels_mag >= 2) { + data->remote_temp_hyst = TEMP_TO_REG(results[1]); + xeontemp_write_value(client, + XEONTEMP_REG_REMOTE_THYST_W, + data->remote_temp_hyst); + } + } +} + +void xeontemp_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct xeontemp_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + xeontemp_update_client(client); + results[0] = data->alarms | data->fail; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* Can't write to it */ + } +} + +static int __init sm_xeontemp_init(void) +{ + printk(KERN_INFO "xeontemp.o version %s (%s)\n", LM_VERSION, LM_DATE); + return i2c_add_driver(&xeontemp_driver); +} + +static void __exit sm_xeontemp_exit(void) +{ + i2c_del_driver(&xeontemp_driver); +} + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("xeontemp driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(read_only, "i"); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +module_init(sm_xeontemp_init) +module_exit(sm_xeontemp_exit) diff --git a/lib/chips.c b/lib/chips.c index d01a7868..bab2a0a0 100644 --- a/lib/chips.c +++ b/lib/chips.c @@ -4424,6 +4424,25 @@ static sensors_chip_feature bmc_features[] = { 0 } }; +static sensors_chip_feature xeontemp_features[] = + { + { SENSORS_XEONTEMP_REMOTE_TEMP, "temp", SENSORS_NO_MAPPING, + SENSORS_NO_MAPPING, SENSORS_MODE_R, + XEONTEMP_SYSCTL_REMOTE_TEMP, VALUE(3), 0 }, + { SENSORS_XEONTEMP_REMOTE_TEMP_HYST, "temp_low", + SENSORS_XEONTEMP_REMOTE_TEMP, + SENSORS_XEONTEMP_REMOTE_TEMP, SENSORS_MODE_RW, + XEONTEMP_SYSCTL_REMOTE_TEMP, VALUE(2), 0 }, + { SENSORS_XEONTEMP_REMOTE_TEMP_OVER, "temp_over", + SENSORS_XEONTEMP_REMOTE_TEMP, + SENSORS_XEONTEMP_REMOTE_TEMP, SENSORS_MODE_RW, + XEONTEMP_SYSCTL_REMOTE_TEMP, VALUE(1), 0 }, + { SENSORS_XEONTEMP_ALARMS, "alarms", SENSORS_NO_MAPPING, + SENSORS_NO_MAPPING, SENSORS_MODE_R, + XEONTEMP_SYSCTL_ALARMS, VALUE(1), 0 }, + { 0 } + }; + sensors_chip_features sensors_chip_features_list[] = { { SENSORS_LM78_PREFIX, lm78_features }, @@ -4491,5 +4510,6 @@ sensors_chip_features sensors_chip_features_list[] = { SENSORS_ADT7463_PREFIX, adm1027_features }, { SENSORS_LM83_PREFIX, lm83_features }, { SENSORS_LM90_PREFIX, lm90_features }, + { SENSORS_XEONTEMP_PREFIX, xeontemp_features }, { 0 } }; diff --git a/lib/chips.h b/lib/chips.h index 45c824e0..857b88c4 100644 --- a/lib/chips.h +++ b/lib/chips.h @@ -1648,4 +1648,11 @@ #define SENSORS_BMC_CURR1_MIN 3101 /* RW */ #define SENSORS_BMC_CURR1_MAX 3201 /* RW */ +#define SENSORS_XEONTEMP_PREFIX "xeontemp" + +#define SENSORS_XEONTEMP_REMOTE_TEMP 54 /* R */ +#define SENSORS_XEONTEMP_REMOTE_TEMP_HYST 55 /* RW */ +#define SENSORS_XEONTEMP_REMOTE_TEMP_OVER 56 /* RW */ +#define SENSORS_XEONTEMP_ALARMS 81 /* R */ + #endif /* def LIB_SENSORS_CHIPS_H */ diff --git a/prog/sensors/chips.c b/prog/sensors/chips.c index 3abca8de..2b078fa5 100644 --- a/prog/sensors/chips.c +++ b/prog/sensors/chips.c @@ -4467,6 +4467,50 @@ void print_lm90(const sensors_chip_name *name) free_the_label(&label); } +void print_xeontemp(const sensors_chip_name *name) +{ + char *label; + double cur,hyst,over; + int alarms,i,valid; + + if (!sensors_get_feature(*name,SENSORS_XEONTEMP_ALARMS,&cur)) + alarms = cur + 0.5; + else { + printf("ERROR: Can't get alarm data!\n"); + alarms = 0; + } + + if (!sensors_get_label_and_valid(*name,SENSORS_XEONTEMP_REMOTE_TEMP, + &label,&valid) && + !sensors_get_feature(*name,SENSORS_XEONTEMP_REMOTE_TEMP,&cur) && + !sensors_get_feature(*name,SENSORS_XEONTEMP_REMOTE_TEMP_HYST,&hyst) && + !sensors_get_feature(*name,SENSORS_XEONTEMP_REMOTE_TEMP_OVER,&over)) { + if (valid) { + print_label(label,10); + print_temp_info( cur, over, hyst, MINMAX, 0, 0); + if (alarms & (XEONTEMP_ALARM_RTEMP_HIGH | XEONTEMP_ALARM_RTEMP_LOW | + XEONTEMP_ALARM_RTEMP_NA)) { + printf("ALARM ("); + i = 0; + if (alarms & XEONTEMP_ALARM_RTEMP_NA) { + printf("N/A"); + i++; + } + if (alarms & XEONTEMP_ALARM_RTEMP_LOW) { + printf("%sLOW",i?",":""); + i++; + } + if (alarms & XEONTEMP_ALARM_RTEMP_HIGH) + printf("%sHIGH",i?",":""); + printf(")"); + } + printf("\n"); + } + } else + printf("ERROR: Can't get temperature data!\n"); + free_the_label(&label); +} + void print_unknown_chip(const sensors_chip_name *name) { int a,b,valid; diff --git a/prog/sensors/chips.h b/prog/sensors/chips.h index 949d2a77..4b8baa16 100644 --- a/prog/sensors/chips.h +++ b/prog/sensors/chips.h @@ -54,5 +54,6 @@ extern void print_vt8231(const sensors_chip_name *name); extern void print_bmc(const sensors_chip_name *name); extern void print_lm83(const sensors_chip_name *name); extern void print_lm90(const sensors_chip_name *name); +extern void print_xeontemp(const sensors_chip_name *name); #endif /* def PROG_SENSORS_CHIPS_H */ diff --git a/prog/sensors/main.c b/prog/sensors/main.c index 6d20bd84..37fc89a3 100644 --- a/prog/sensors/main.c +++ b/prog/sensors/main.c @@ -376,6 +376,10 @@ void do_a_print(sensors_chip_name name) print_adm1026(&name); else if (!strcmp(name.prefix,"lm83")) print_lm83(&name); + else if (!strcmp(name.prefix,"lm90")) + print_lm90(&name); + else if (!strcmp(name.prefix,"xeontemp")) + print_xeontemp(&name); else print_unknown_chip(&name); printf("\n");