Initial checkin of it87 driver from

Christophe Gauthron <chrisg@0-in.com>


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1112 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Mark D. Studebaker committed 2001-06-03 21:58:26 +00:00
1 parent be5cdebced
commit 87c529e96d
12 files changed
+1476 -18

No files matched your search

+7 -4
View File
@@ -21,8 +21,9 @@ ask CVS about it:
NOTE: i2c-2.6.0 MUST BE be compiled AND installed first!!!
If your make fails because it can't find <linux/i2c-proc.h>, you
forgot this step!!!
File doc/FAQ: Many updates and additions
File doc/FAQ: Many, many updates and additions
File doc/busses/i2c-sis5595: new
File doc/busses/i2c-tsunami: new
File doc/chips/via686a: Claim support for 686b.
File README.thinkpad: new
File sensors.conf.eg: Adjust as99127f in5 (-12V) and temp2 calculations;
@@ -45,6 +46,7 @@ ask CVS about it:
Module i2c-sis5595: Allow force_addr=0xaddr; enable if not enabled.
Module i2c-tsunami: New
Module i2c-via: Ensure i2c bus is tristated correctly.
Module it87: new driver for IT8705, IT8712, Sis950 chips
Module lm78: Recognize chipid=0x20
Module lm87: Fix in0, in1 (2.5V and Vccp1) calculations
Module mtp008: Fix temp initializations;
@@ -62,17 +64,18 @@ ask CVS about it:
Program isadump: fix for Alpha arch. compiles
Program mkpatch.pl: Fix adm9240 typos; Add more chips to Configure.help;
Remove ltc1710; Fix i2c_sis5595 typo;
Support kernel 2.4.5 makefile change
Support kernel 2.4.5 makefile change;
Add i2c-tsunami.
Program sens_update_rrd: new
Program sensord: Add -d (debug) and -p (pid-file) options
Program sensors: Change reported version from 1.3 to the lm_sensors version;
Fix swapped limit and hysteresis on 9240, 5595, 686a;
Change mtp008 temps from max/min to limit/hyst;
Add maxilife-nba and adm1024 support.
Add maxilife-nba, adm1024, it87xx support.
Program sensors-detect: Recognize lm78 with chipid=0x20;
Recognize SMSC Victory66 South Bridge;
Add devfs /dev/i2c/x support;
Add adm1024 support; Add thinkpad warning.
Add adm1024, it87xx support; Add thinkpad warning.
Program tellerstats: new
2.5.5 (20010115)
+2
View File
@@ -60,3 +60,5 @@ problems.
Author of the adm1024 driver
* Oleg I. Vdovikin <vdovikin@jscc.ru>
Author of the DEC Tsunami bus driver.
* Christophe Gauthron <chrisg@0-in.com>
Author of the IT87 driver.
+4 -3
View File
@@ -52,11 +52,12 @@ At least the following hardware sensor chips are supported:
Hewlett Packard Maxilife (several revisions including '99 NBA)
Genesys Logic GL518SM (rev 00, 80), GL520SM, GL523SM
Intel Xeon processor embedded sensors
ITE IT8705F, IT8712F embedded sensors
Maxim MAX1617 and MAX1617A
Myson MTP008
National Semicoductor LM75, LM77, LM78, LM78-J, LM79,
LM80, LM81, LM84, and LM87
SiS 5595 embedded sensors
National Semiconductor LM75, LM77, LM78, LM78-J, LM79,
LM80, LM81, LM84, and LM87
SiS 5595, 950 embedded sensors
TelCom TCN75
TI THMC10 and THMC50
VIA Technologies VT82C686A/B embedded sensors
+184
View File
@@ -0,0 +1,184 @@
Kernel driver `it87.o'
======================
Status: Beta
Supported chips:
* IT8705F
ISA 0x290
Datasheet: Publicly available at the ITE website
* IT8712F (untested)
Addresses scanned: I2C 0x20 - 0x2f (inclusive), ISA 0x290 (8 I/O ports)
Datasheet: Publicly available at the ITE website
* Sis950 [clone of IT8705F]
ISA 0x290
Datasheet: Publicly available at the SIS website
Author: Christophe Gauthron <chrisg@0-in.com>
Module Parameters
-----------------
* force: short array (min = 1, max = 48)
List of adapter,address pairs to boldly assume to be present
* 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
* 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
Description
-----------
This driver implements support for the IT8705F chip.
This chip is also known as SIS 950.
This driver also supports IT8712F, which adds SmBus access, and a Vid
input, used to report the Vcore voltage of the Pentium processor.
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
joysticks and other miscellaneous stuff. For hardware monitoring,
they include an 'environmnent controller' with 3 temperature sensors,
3 FAN rotation speed sensors, 8 voltage sensors, and associated alarms.
Temperatures are measured in degrees Celcius. An alarm is triggered once
when the Overtemperature Shutdown limit is crossed; it is triggered again
as soon as it drops below the Hysteresis value.
FAN rotation speeds are reported in RPM (rotations per minute). An alarm is
triggered if the rotation speed has dropped below a programmable limit. FAN
readings can be divided by a programmable divider (1, 2, 4 or 8) to give
the readings more range or accuracy. This is important because some FANs
report only one 'tick' each rotation, while others report two - making
all readings twice as high. Not all RPM values can accurately be represented,
so some rounding is done. With a divider of 2, the lowest representable
value is around 2600 RPM.
Voltage sensors (also known as IN sensors) report their values in volts.
An alarm is triggered if the voltage has crossed a programmable minimum
or maximum limit. Note that minimum in this case always means 'closest to
zero'; this is important for negative voltage measurements. All voltage
inputs can measure voltages between 0 and 4.08 volts, with a resolution
of 0.016 volt.
The VID lines (IT8712F only) encode the core voltage value: the voltage level
your processor should work with. This is hardcoded by the mainboard and/or
processor itself. It is a value in volts.
If an alarm triggers, it will remain triggered until the hardware register
is read at least once. This means that the cause for the alarm may
already have disappeared! Note that in the current implementation, all
hardware registers are read whenever any data is read (unless it is less
than 1.5 seconds since the last update). This means that you can easily
miss once-only alarms.
The IT87xx only updates its values each 1.5 seconds; reading it more often
will do no harm, but will return 'old' values.
Chip features
-------------
Chip `it87`
LABEL LABEL CLASS COMPUTE CLASS ACCESS MAGNITUDE
in0 NONE NONE R 2
in1 NONE NONE R 2
in2 NONE NONE R 2
in3 NONE NONE R 2
in4 NONE NONE R 2
in5 NONE NONE R 2
in6 NONE NONE R 2
in7 NONE NONE R 2
in0_min in0 in0 RW 2
in1_min in1 in1 RW 2
in2_min in2 in2 RW 2
in3_min in3 in3 RW 2
in4_min in4 in4 RW 2
in5_min in5 in5 RW 2
in6_min in6 in6 RW 2
in7_min in6 in6 RW 2
in0_max in0 in0 RW 2
in1_max in1 in1 RW 2
in2_max in2 in2 RW 2
in3_max in3 in3 RW 2
in4_max in4 in4 RW 2
in5_max in5 in5 RW 2
in6_max in6 in6 RW 2
in7_max in6 in6 RW 2
fan1 NONE NONE R 0
fan2 NONE NONE R 0
fan3 NONE NONE R 0
fan1_min fan1 fan1 RW 0
fan2_min fan2 fan2 RW 0
fan3_min fan3 fan3 RW 0
temp1 NONE NONE R 1
temp2 NONE NONE R 1
temp3 NONE NONE R 1
temp_hyst1 temp temp RW 1
temp_over1 temp temp RW 1
temp_hyst2 temp temp RW 1
temp_over2 temp temp RW 1
temp_hyst3 temp temp RW 1
temp_over3 temp temp RW 1
vid NONE NONE R 2
fan1_div fan1 NONE RW 0
fan2_div fan2 NONE RW 0
fan3_div fan3 NONE R 0
alarms_vin NONE NONE R 0
alarms_fan NONE NONE R 0
alarms_temp NONE NONE R 0
LABEL FEATURE SYMBOL SYSCTL FILE:OFFSET
in0 SENSORS_IT87_IN0 in0:3
in1 SENSORS_IT87_IN1 in1:3
in2 SENSORS_IT87_IN2 in2:3
in3 SENSORS_IT87_IN3 in3:3
in4 SENSORS_IT87_IN4 in4:3
in5 SENSORS_IT87_IN5 in5:3
in6 SENSORS_IT87_IN6 in6:3
in7 SENSORS_IT87_IN7 in7:3
in0_min SENSORS_IT87_IN0_MIN in0:1
in1_min SENSORS_IT87_IN1_MIN in1:1
in2_min SENSORS_IT87_IN2_MIN in2:1
in3_min SENSORS_IT87_IN3_MIN in3:1
in4_min SENSORS_IT87_IN4_MIN in4:1
in5_min SENSORS_IT87_IN5_MIN in5:1
in6_min SENSORS_IT87_IN6_MIN in6:1
in7_min SENSORS_IT87_IN7_MIN in7:1
in0_max SENSORS_IT87_IN0_MAX in0:2
in1_max SENSORS_IT87_IN1_MAX in1:2
in2_max SENSORS_IT87_IN2_MAX in2:2
in3_max SENSORS_IT87_IN3_MAX in3:2
in4_max SENSORS_IT87_IN4_MAX in4:2
in5_max SENSORS_IT87_IN5_MAX in5:2
in6_max SENSORS_IT87_IN6_MAX in6:2
in7_max SENSORS_IT87_IN7_MAX in7:2
fan1 SENSORS_IT87_FAN1 fan1:2
fan2 SENSORS_IT87_FAN2 fan2:2
fan3 SENSORS_IT87_FAN3 fan3:2
fan1_min SENSORS_IT87_FAN1_MIN fan1:1
fan2_min SENSORS_IT87_FAN2_MIN fan2:1
fan3_min SENSORS_IT87_FAN3_MIN fan3:1
temp1 SENSORS_IT87_TEMP1 temp1:3
temp2 SENSORS_IT87_TEMP2 temp2:3
temp3 SENSORS_IT87_TEMP3 temp3:3
temp1_hyst SENSORS_IT87_TEMP1_HYST temp1:2
temp1_over SENSORS_IT87_TEMP1_OVER temp1:1
temp2_hyst SENSORS_IT87_TEMP2_HYST temp2:2
temp2_over SENSORS_IT87_TEMP2_OVER temp2:1
temp3_hyst SENSORS_IT87_TEMP3_HYST temp3:2
temp3_over SENSORS_IT87_TEMP3_OVER temp3:1
vid SENSORS_IT87_VID vid:1
fan1_div SENSORS_IT87_FAN1_DIV fan_div:1
fan2_div SENSORS_IT87_FAN2_DIV fan_div:2
fan3_div SENSORS_IT87_FAN3_DIV fan_div:3
alarms_fan SENSORS_IT87_ALARMS_FAN alarms_fan:1
alarms_vin SENSORS_IT87_ALARMS_VIN alarms_vin:1
alarms_temp SENSORS_IT87_ALARMS_TEMP alarms_temp:1
+108 -5
View File
@@ -207,7 +207,7 @@
#### Here begins the real configuration file
chip "lm78-*" "lm78-j-*" "lm79-*" "w83781d-*" "sis5595-*"
chip "lm78-*" "lm78-j-*" "lm79-*" "w83781d-*"
# These are as advised in the LM78 and LM79 data sheets, and used on almost
# any mainboard we have seen.
@@ -272,8 +272,7 @@ chip "lm78-*" "lm78-j-*" "lm79-*" "w83781d-*" "sis5595-*"
set in6_min -5 * 0.95
set in6_max -5 * 1.05
# examples for lm78, lm78j, lm79, sis5595 temperature limits;
# for sis5595, temp_hist is really the low limit, not a hysteresis value
# examples for lm78, lm78j, lm79 temperature limits;
# set temp_over 40
# set temp_hyst 37
@@ -286,6 +285,48 @@ chip "lm78-*" "lm78-j-*" "lm79-*" "w83781d-*" "sis5595-*"
# set temp3_hyst 47
chip "sis5595-*"
label in0 "VCore 1"
label in1 "VCore 2"
label in2 "+3.3V"
label in3 "+5V"
label in4 "+12V"
compute in3 ((6.8/10)+1)*@ , @/((6.8/10)+1)
compute in4 ((28/10)+1)*@ , @/((28/10)+1)
set in0_min 2.0 * 0.95
set in0_max 2.0 * 1.05
set in1_min 2.0 * 0.95
set in1_max 2.0 * 1.05
set in2_min 3.3 * 0.95
set in2_max 3.3 * 1.05
set in3_min 5.0 * 0.95
set in3_max 5.0 * 1.05
set in4_min 12 * 0.95
set in4_max 12 * 1.05
#
# SiS5595 temperature calculation
# The driver does NOT include a calculation due to the wide
# variation in thermistor types on SiS5595 motherboards.
# Look in your 'Vendor.ini' file to see which one is present
# on your motherboard. Look for the line like:
# [Temp1]
# ThermistorType = NTC-10KC15-1608-1P
# Uncomment the correct 'compute' line below to match your
# thermistor type.
# ThermistorType = NTC-10KC15-1608-1P (10K at 25C; Beta = 3435)
# ThermistorType = NTC-103KC15-1608-1P
# ThermistorType = NTC-103AT-2 (10K at 25C; Beta = 3435)
# ThermistorType = NTC-103JT (10K at 25C; Beta = 3435)
# examples for sis5595 temperature limits;
# for sis5595, temp_hist is really the low limit, not a hysteresis value
# set temp_over 40
# set temp_hyst 37
chip "w83782d-*" "w83783s-*" "w83627hf-*"
# Same as above for w83781d except that in5 and in6 are computed differently.
@@ -873,9 +914,9 @@ chip "lm87-*"
chip "adm9240-*" "ds1780-*" "lm81-*"
#
# This chip has non-standard entries in lib/chips.c so
# These chips have non-standard entries in lib/chips.c so
# the feature names are quite different from other chips.
# For this chip, libsensors anticipates the correct labeling.
# For these chips, libsensors anticipates the correct labeling.
# This is great if it's correct but makes it a little more
# difficult if you want to change it.
#
@@ -937,3 +978,65 @@ chip "adm1024-*"
ignore "2.5V" # This register is also used for temp2
ignore "Vccp1"
ignore "Vccp2"
chip "it87-*"
# The values below have been tested on Asus CUSI, CUM motherboards.
# Voltage monitors as advised in the It8705 data sheet
label in0 "VCore 1"
label in1 "VCore 2"
label in2 "+3.3V"
label in3 "+5V"
label in4 "+12V"
label in5 "-12V"
label in6 "-5V"
label in7 "Stdby"
set in0_min 1.5 * 0.95
set in0_max 1.5 * 1.05
set in1_min 2.4
set in1_max 2.6
set in2_min 3.3 * 0.95
set in2_max 3.3 * 1.05
set in3_min 5.0 * 0.95
set in3_max 5.0 * 1.05
set in4_min 12 * 0.95
set in4_max 12 * 1.05
set in5_min -12 * 0.95
set in5_max -12 * 1.05
set in6_min -5 * 0.95
set in6_max -5 * 1.05
set in6_min -5 * 0.95
set in6_max -5 * 1.05
# vid not monitored by IT8705F
ignore vid
compute in2 (1 + 1)*@ , @/(1 + 1)
compute in3 ((6.8/10)+1)*@ , @/((6.8/10)+1)
compute in4 ((30/10) +1)*@ , @/((30/10) +1)
compute in5 -(72/10)*@ , -@/(72/10)
compute in6 -(56/10)*@ , -@/(56/10)
compute in7 ((6.8/10)+1)*@ , @/((6.8/10)+1)
# Temperature
label temp1 "Temp1/MB"
set temp1_over 40
set temp1_hyst 20
label temp2 "Temp2/CPU"
set temp2_over 45
set temp2_hyst 25
ignore temp3
# Fans
set fan1_min 0
set fan2_min 3000
ignore fan3
+5 -1
View File
@@ -25,8 +25,9 @@ KERNELCHIPSDIR := $(MODULE_DIR)
# defined value verbatim into the command-list of rules...
# These targets are NOT included in 'mkpatch' ...
KERNELCHIPSTARGETS := \
$(MODULE_DIR)/maxilife.o \
$(MODULE_DIR)/adm1024.o \
$(MODULE_DIR)/it87.o \
$(MODULE_DIR)/maxilife.o \
$(MODULE_DIR)/mtp008.o
# These targets ARE included in 'mkpatch', except for LTC1710, which we
@@ -58,6 +59,9 @@ endif
ifneq ($(shell if grep -q '^CONFIG_SENSORS_GL520SM=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELCHIPSTARGETS += $(MODULE_DIR)/gl520sm.o
endif
ifneq ($(shell if grep -q '^CONFIG_SENSORS_IT87=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELCHIPSTARGETS += $(MODULE_DIR)/it87.o
endif
ifneq ($(shell if grep -q '^CONFIG_SENSORS_LM75=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELCHIPSTARGETS += $(MODULE_DIR)/lm75.o
endif
+941
View File
@@ -0,0 +1,941 @@
/*
it87.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring.
Supports: IT8705F Super I/O chip w/LPC interface
IT8712F Super I/O chup w/LPC interface & SMbus
Sis950 A clone of the IT8705F
Copyright (c) 2001 Chris Gauthron <chrisg@0-in.com>
Largely inspired by lm78.c of the same package
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/version.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include "version.h"
#include "i2c-isa.h"
#include "sensors.h"
#include <linux/init.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \
(LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0))
#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)
#endif
#ifndef THIS_MODULE
#define THIS_MODULE NULL
#endif
/* Addresses to scan */
static unsigned short normal_i2c[] = { SENSORS_I2C_END };
static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(it87);
/* Many IT87 constants specified below */
/* Length of ISA address segment */
#define IT87_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define IT87_ADDR_REG_OFFSET 5
#define IT87_DATA_REG_OFFSET 6
/*----- The IT87 registers -----*/
#define IT87_REG_CONFIG 0x00
#define IT87_REG_ALARM1 0x01
#define IT87_REG_ALARM2 0x02
#define IT87_REG_ALARM3 0x03
#define IT87_REG_VID 0x0a
#define IT87_REG_FAN_DIV 0x0b
/* Monitors: 8 voltage (0 to 7), 3 temp (1 to 3), 3 fan (1 to 3) */
#define IT87_REG_FAN(nr) (0x0c + (nr))
#define IT87_REG_FAN_MIN(nr) (0x0f + (nr))
#define IT87_REG_FAN_CTRL 0x13
#define IT87_REG_VIN(nr) (0x20 + (nr))
#define IT87_REG_TEMP(nr) (0x28 + (nr))
#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2)
#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2)
#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2)
#define IT87_REG_TEMP_LOW(nr) (0x3f + (nr) * 2)
#define IT87_REG_I2C_ADDR 0x48
#define IT87_REG_VIN_ENABLE 0x50
#define IT87_REG_TEMP_ENABLE 0x51
#define IT87_REG_CHIPID 0x58
/* 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. */
#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
#define IN_FROM_REG(val) (((val) * 16) / 10)
extern inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm == 0)
return 255;
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1,
254);
}
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\
((val)+5)/10),0,255))
#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
205-(val)*5)
#define ALARMS_FROM_REG(val) (val)
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
#define DIV_FROM_REG(val) (1 << (val))
/* Initial limits. Use the config file to set better limits. */
#define IT87_INIT_IN_0 170
#define IT87_INIT_IN_1 250
#define IT87_INIT_IN_2 (330 / 2)
#define IT87_INIT_IN_3 (((500) * 100)/168)
#define IT87_INIT_IN_4 (((1200) * 10)/38)
#define IT87_INIT_IN_5 (((1200) * 10)/72)
#define IT87_INIT_IN_6 (((500) * 10)/56)
#define IT87_INIT_IN_7 (((500) * 100)/168)
#define IT87_INIT_IN_PERCENTAGE 10
#define IT87_INIT_IN_MIN_0 \
(IT87_INIT_IN_0 - IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_0 \
(IT87_INIT_IN_0 + IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_1 \
(IT87_INIT_IN_1 - IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_1 \
(IT87_INIT_IN_1 + IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_2 \
(IT87_INIT_IN_2 - IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_2 \
(IT87_INIT_IN_2 + IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_3 \
(IT87_INIT_IN_3 - IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_3 \
(IT87_INIT_IN_3 + IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_4 \
(IT87_INIT_IN_4 - IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_4 \
(IT87_INIT_IN_4 + IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_5 \
(IT87_INIT_IN_5 - IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_5 \
(IT87_INIT_IN_5 + IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_6 \
(IT87_INIT_IN_6 - IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_6 \
(IT87_INIT_IN_6 + IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MIN_7 \
(IT87_INIT_IN_7 - IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_IN_MAX_7 \
(IT87_INIT_IN_7 + IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100)
#define IT87_INIT_FAN_MIN_1 3000
#define IT87_INIT_FAN_MIN_2 3000
#define IT87_INIT_FAN_MIN_3 3000
#define IT87_INIT_TEMP_HIGH_1 600
#define IT87_INIT_TEMP_LOW_1 200
#define IT87_INIT_TEMP_HIGH_2 600
#define IT87_INIT_TEMP_LOW_2 200
#define IT87_INIT_TEMP_HIGH_3 600
#define IT87_INIT_TEMP_LOW_3 200
#ifdef MODULE
extern int init_module(void);
extern int cleanup_module(void);
#endif /* MODULE */
/* For each registered IT87, we need to keep some data in memory. That
data is pointed to by it87_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new it87 client is
allocated. */
struct it87_data {
struct semaphore lock;
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 in[8]; /* Register value */
u8 in_max[8]; /* Register value */
u8 in_min[8]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp[3]; /* Register value */
u8 temp_high[3]; /* Register value */
u8 temp_low[3]; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u8 alarms_fan; /* Register value */
u8 alarms_vin; /* Register value */
u8 alarms_temp; /* Register value */
};
#ifdef MODULE
static
#else
extern
#endif
int __init sensors_it87_init(void);
static int __init it87_cleanup(void);
static int it87_attach_adapter(struct i2c_adapter *adapter);
static int it87_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
static int it87_detach_client(struct i2c_client *client);
static int it87_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void it87_inc_use(struct i2c_client *client);
static void it87_dec_use(struct i2c_client *client);
static int it87_read_value(struct i2c_client *client, u8 register);
static int it87_write_value(struct i2c_client *client, u8 register,
u8 value);
static void it87_update_client(struct i2c_client *client);
static void it87_init_client(struct i2c_client *client);
static void it87_in(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results);
static void it87_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_vid(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_alarms_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_alarms_vin(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_alarms_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void it87_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static struct i2c_driver it87_driver = {
/* name */ "IT87xx sensor driver",
/* id */ I2C_DRIVERID_IT87,
/* flags */ I2C_DF_NOTIFY,
/* attach_adapter */ &it87_attach_adapter,
/* detach_client */ &it87_detach_client,
/* command */ &it87_command,
/* inc_use */ &it87_inc_use,
/* dec_use */ &it87_dec_use
};
/* Used by it87_init/cleanup */
static int __initdata it87_initialized = 0;
static int it87_id = 0;
/* The /proc/sys entries */
/* These files are created for each detected IT87. 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 it87_dir_table_template[] = {
{IT87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_in},
{IT87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_fan},
{IT87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_fan},
{IT87_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_fan},
{IT87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_temp},
{IT87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_temp},
{IT87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_temp},
{IT87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_vid},
{IT87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_fan_div},
{IT87_SYSCTL_ALARMS_FAN, "alarms_fan", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_alarms_fan},
{IT87_SYSCTL_ALARMS_VIN, "alarms_vin", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_alarms_vin},
{IT87_SYSCTL_ALARMS_TEMP, "alarms_temp", NULL, 0, 0444, NULL, &i2c_proc_real,
&i2c_sysctl_real, NULL, &it87_alarms_temp},
{0}
};
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and it87_driver is still present) */
int it87_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, it87_detect);
}
/* This function is called by i2c_detect */
int it87_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind)
{
int i;
struct i2c_client *new_client;
struct it87_data *data;
int err = 0;
const char *type_name = "";
const char *client_name = "";
int is_isa = i2c_is_isa_adapter(adapter);
if (!is_isa
&& !i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) goto
ERROR0;
if (is_isa) {
if (check_region(address, IT87_EXTENT))
goto ERROR0;
}
/* Probe whether there is anything available on this address. Already
done for SMBus clients */
if (kind < 0) {
if (is_isa) {
#define REALLY_SLOW_IO
/* We need the timeouts for at least some IT87-like chips. But only
if we read 'undefined' registers. */
i = inb_p(address + 1);
if (inb_p(address + 2) != i)
goto ERROR0;
if (inb_p(address + 3) != i)
goto ERROR0;
if (inb_p(address + 7) != i)
goto ERROR0;
#undef REALLY_SLOW_IO
/* Let's just hope nothing breaks here */
i = inb_p(address + 5) & 0x7f;
outb_p(~i & 0x7f, address + 5);
if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
outb_p(i, address + 5);
return 0;
}
}
}
/* 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 it87_{read,write}_value. */
if (!(new_client = kmalloc((sizeof(struct i2c_client)) +
sizeof(struct it87_data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
data = (struct it87_data *) (new_client + 1);
if (is_isa)
init_MUTEX(&data->lock);
new_client->addr = address;
new_client->data = data;
new_client->adapter = adapter;
new_client->driver = &it87_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if (it87_read_value(new_client, IT87_REG_CONFIG) & 0x80)
goto ERROR1;
if (!is_isa
&& (it87_read_value(new_client, IT87_REG_I2C_ADDR) !=
address)) goto ERROR1;
}
/* Determine the chip type. */
if (kind <= 0) {
i = it87_read_value(new_client, IT87_REG_CHIPID);
if (i == 0x90) {
kind = it87;
}
else {
if (kind == 0)
printk
("it87.o: Ignoring 'force' parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto ERROR1;
}
}
if (kind == it87) {
type_name = "it87";
client_name = "IT87 chip";
} /* else if (kind == it8712) {
type_name = "it8712";
client_name = "IT87-J chip";
} */ else {
#ifdef DEBUG
printk("it87.o: Internal error: unknown kind (%d)?!?",
kind);
#endif
goto ERROR1;
}
/* Reserve the ISA region */
if (is_isa)
request_region(address, IT87_EXTENT, type_name);
/* 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 = it87_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,
it87_dir_table_template,
THIS_MODULE)) < 0) {
err = i;
goto ERROR4;
}
data->sysctl_id = i;
/* Initialize the IT87 chip */
it87_init_client(new_client);
return 0;
/* 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:
if (is_isa)
release_region(address, IT87_EXTENT);
ERROR1:
kfree(new_client);
ERROR0:
return err;
}
int it87_detach_client(struct i2c_client *client)
{
int err;
i2c_deregister_entry(((struct it87_data *) (client->data))->
sysctl_id);
if ((err = i2c_detach_client(client))) {
printk
("it87.o: Client deregistration failed, client not detached.\n");
return err;
}
if i2c_is_isa_client
(client)
release_region(client->addr, IT87_EXTENT);
kfree(client);
return 0;
}
/* No commands defined yet */
int it87_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
return 0;
}
/* Nothing here yet */
void it87_inc_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
/* Nothing here yet */
void it87_dec_use(struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* The SMBus locks itself, but ISA access must be locked explicitely!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary.
There are some ugly typecasts here, but the good new is - they should
nowhere else be necessary! */
int it87_read_value(struct i2c_client *client, u8 reg)
{
int res;
if (i2c_is_isa_client(client)) {
down(&(((struct it87_data *) (client->data))->lock));
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
res = inb_p(client->addr + IT87_DATA_REG_OFFSET);
up(&(((struct it87_data *) (client->data))->lock));
return res;
} else
return i2c_smbus_read_byte_data(client, reg);
}
/* The SMBus locks itself, but ISA access muse be locked explicitely!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary.
There are some ugly typecasts here, but the good new is - they should
nowhere else be necessary! */
int it87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
if (i2c_is_isa_client(client)) {
down(&(((struct it87_data *) (client->data))->lock));
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
up(&(((struct it87_data *) (client->data))->lock));
return 0;
} else
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new IT87. It should set limits, etc. */
void it87_init_client(struct i2c_client *client)
{
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
it87_write_value(client, IT87_REG_CONFIG, 0x80);
it87_write_value(client, IT87_REG_VIN_MIN(0),
IN_TO_REG(IT87_INIT_IN_MIN_0));
it87_write_value(client, IT87_REG_VIN_MAX(0),
IN_TO_REG(IT87_INIT_IN_MAX_0));
it87_write_value(client, IT87_REG_VIN_MIN(1),
IN_TO_REG(IT87_INIT_IN_MIN_1));
it87_write_value(client, IT87_REG_VIN_MAX(1),
IN_TO_REG(IT87_INIT_IN_MAX_1));
it87_write_value(client, IT87_REG_VIN_MIN(2),
IN_TO_REG(IT87_INIT_IN_MIN_2));
it87_write_value(client, IT87_REG_VIN_MAX(2),
IN_TO_REG(IT87_INIT_IN_MAX_2));
it87_write_value(client, IT87_REG_VIN_MIN(3),
IN_TO_REG(IT87_INIT_IN_MIN_3));
it87_write_value(client, IT87_REG_VIN_MAX(3),
IN_TO_REG(IT87_INIT_IN_MAX_3));
it87_write_value(client, IT87_REG_VIN_MIN(4),
IN_TO_REG(IT87_INIT_IN_MIN_4));
it87_write_value(client, IT87_REG_VIN_MAX(4),
IN_TO_REG(IT87_INIT_IN_MAX_4));
it87_write_value(client, IT87_REG_VIN_MIN(5),
IN_TO_REG(IT87_INIT_IN_MIN_5));
it87_write_value(client, IT87_REG_VIN_MAX(5),
IN_TO_REG(IT87_INIT_IN_MAX_5));
it87_write_value(client, IT87_REG_VIN_MIN(6),
IN_TO_REG(IT87_INIT_IN_MIN_6));
it87_write_value(client, IT87_REG_VIN_MAX(6),
IN_TO_REG(IT87_INIT_IN_MAX_6));
it87_write_value(client, IT87_REG_VIN_MIN(7),
IN_TO_REG(IT87_INIT_IN_MIN_7));
it87_write_value(client, IT87_REG_VIN_MAX(7),
IN_TO_REG(IT87_INIT_IN_MAX_7));
it87_write_value(client, IT87_REG_FAN_MIN(1),
FAN_TO_REG(IT87_INIT_FAN_MIN_1, 2));
it87_write_value(client, IT87_REG_FAN_MIN(2),
FAN_TO_REG(IT87_INIT_FAN_MIN_2, 2));
it87_write_value(client, IT87_REG_FAN_MIN(3),
FAN_TO_REG(IT87_INIT_FAN_MIN_3, 2));
it87_write_value(client, IT87_REG_TEMP_HIGH(1),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_1));
it87_write_value(client, IT87_REG_TEMP_LOW(1),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_1));
it87_write_value(client, IT87_REG_TEMP_HIGH(2),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_2));
it87_write_value(client, IT87_REG_TEMP_LOW(2),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_2));
it87_write_value(client, IT87_REG_TEMP_HIGH(3),
TEMP_TO_REG(IT87_INIT_TEMP_HIGH_3));
it87_write_value(client, IT87_REG_TEMP_LOW(3),
TEMP_TO_REG(IT87_INIT_TEMP_LOW_3));
/* Enable voltage monitors */
it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff);
/* Enable Temo1 as thermal resistor */
/* Enable Temp2 as thermal diode */
it87_write_value(client, IT87_REG_TEMP_ENABLE,
(it87_read_value(client, IT87_REG_TEMP_ENABLE) & 0xc0)
| 0x0a);
/* Enable fans */
it87_write_value(client, IT87_REG_FAN_CTRL,
(it87_read_value(client, IT87_REG_FAN_CTRL) & 0x8f)
| 0x70);
/* Start monitoring */
it87_write_value(client, IT87_REG_CONFIG,
(it87_read_value(client, IT87_REG_CONFIG) & 0xb7)
| 0x01);
}
void it87_update_client(struct i2c_client *client)
{
struct it87_data *data = client->data;
int i;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
for (i = 0; i <= 7; i++) {
data->in[i] =
it87_read_value(client, IT87_REG_VIN(i));
data->in_min[i] =
it87_read_value(client, IT87_REG_VIN_MIN(i));
data->in_max[i] =
it87_read_value(client, IT87_REG_VIN_MAX(i));
}
for (i = 1; i <= 3; i++) {
data->fan[i - 1] =
it87_read_value(client, IT87_REG_FAN(i));
data->fan_min[i - 1] =
it87_read_value(client, IT87_REG_FAN_MIN(i));
}
for (i = 1; i <= 3; i++) {
data->temp[i - 1] =
it87_read_value(client, IT87_REG_TEMP(i));
data->temp_high[i - 1] =
it87_read_value(client, IT87_REG_TEMP_HIGH(i));
data->temp_low[i - 1] =
it87_read_value(client, IT87_REG_TEMP_LOW(i));
}
/* The 8705 does not have VID capability */
/*if (data->type == it8712) {
data->vid = it87_read_value(client, IT87_REG_VID);
data->vid &= 0x1f;
}
else */ {
data->vid = 0x1f;
}
i = it87_read_value(client, IT87_REG_FAN_DIV);
data->fan_div[0] = i & 0x07;
data->fan_div[1] = (i >> 3) & 0x07;
data->fan_div[2] = 1;
data->alarms_fan =
it87_read_value(client, IT87_REG_ALARM1) & 0x7;
data->alarms_vin =
it87_read_value(client, IT87_REG_ALARM2) & 0xf;
data->alarms_temp =
it87_read_value(client, IT87_REG_ALARM3) & 0x7;
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 it87_in(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
int nr = ctl_name - IT87_SYSCTL_IN0;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 2;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = IN_FROM_REG(data->in_min[nr]);
results[1] = IN_FROM_REG(data->in_max[nr]);
results[2] = IN_FROM_REG(data->in[nr]);
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->in_min[nr] = IN_TO_REG(results[0]);
it87_write_value(client, IT87_REG_VIN_MIN(nr),
data->in_min[nr]);
}
if (*nrels_mag >= 2) {
data->in_max[nr] = IN_TO_REG(results[1]);
it87_write_value(client, IT87_REG_VIN_MAX(nr),
data->in_max[nr]);
}
}
}
void it87_fan(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
int nr = ctl_name - IT87_SYSCTL_FAN1 + 1;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
DIV_FROM_REG(data->
fan_div[nr - 1]));
results[1] =
FAN_FROM_REG(data->fan[nr - 1],
DIV_FROM_REG(data->fan_div[nr - 1]));
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->fan_min[nr - 1] = FAN_TO_REG(results[0],
DIV_FROM_REG
(data->
fan_div[nr -
1]));
it87_write_value(client, IT87_REG_FAN_MIN(nr),
data->fan_min[nr - 1]);
}
}
}
void it87_temp(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
int nr = ctl_name - IT87_SYSCTL_TEMP1 + 1;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 1;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = TEMP_FROM_REG(data->temp_high[nr - 1]);
results[1] = TEMP_FROM_REG(data->temp_low[nr - 1]);
results[2] = TEMP_FROM_REG(data->temp[nr - 1]);
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->temp_high[nr - 1] = TEMP_TO_REG(results[0]);
it87_write_value(client, IT87_REG_TEMP_HIGH(nr),
data->temp_high[nr - 1]);
}
if (*nrels_mag >= 2) {
data->temp_low[nr - 1] = TEMP_TO_REG(results[1]);
it87_write_value(client, IT87_REG_TEMP_LOW(nr),
data->temp_low[nr - 1]);
}
}
}
void it87_vid(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 2;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = VID_FROM_REG(data->vid);
*nrels_mag = 1;
}
}
void it87_alarms_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = ALARMS_FROM_REG(data->alarms_fan);
*nrels_mag = 1;
}
}
void it87_alarms_vin(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = ALARMS_FROM_REG(data->alarms_vin);
*nrels_mag = 1;
}
}
void it87_alarms_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = ALARMS_FROM_REG(data->alarms_temp);
*nrels_mag = 1;
}
}
void it87_fan_div(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct it87_data *data = client->data;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
it87_update_client(client);
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
results[2] = 2;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
old = it87_read_value(client, IT87_REG_FAN_DIV);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
old = (old & 0xc3) | (data->fan_div[1] << 3);
}
if (*nrels_mag >= 1) {
data->fan_div[0] = DIV_TO_REG(results[0]);
old = (old & 0xf8) | data->fan_div[0];
it87_write_value(client, IT87_REG_FAN_DIV, old);
}
}
}
int __init sensors_it87_init(void)
{
int res;
printk("it87.o version %s (%s)\n", LM_VERSION, LM_DATE);
it87_initialized = 0;
if ((res = i2c_add_driver(&it87_driver))) {
printk
("it87.o: Driver registration failed, module not inserted.\n");
it87_cleanup();
return res;
}
it87_initialized++;
return 0;
}
int __init it87_cleanup(void)
{
int res;
if (it87_initialized >= 1) {
if ((res = i2c_del_driver(&it87_driver))) {
printk
("it87.o: Driver deregistration failed, module not removed.\n");
return res;
}
it87_initialized--;
}
return 0;
}
EXPORT_NO_SYMBOLS;
#ifdef MODULE
MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver");
int init_module(void)
{
return sensors_it87_init();
}
int cleanup_module(void)
{
return it87_cleanup();
}
#endif /* MODULE */
+7
View File
@@ -18,6 +18,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#ifdef MODULE
@@ -97,6 +98,9 @@ extern int sensors_eeprom_init(void);
#ifdef CONFIG_SENSORS_LTC1710
extern int sensors_ltc1710_init(void);
#endif
#ifdef CONFIG_SENSORS_IT87
extern int sensors_it87_init(void);
#endif
int __init sensors_init_all(void)
{
@@ -157,6 +161,9 @@ int __init sensors_init_all(void)
#endif
#ifdef CONFIG_SENSORS_LTC1710
sensors_ltc1710_init();
#endif
#ifdef CONFIG_SENSORS_IT87
sensors_it87_init();
#endif
return 0;
}
+1 -5
View File
@@ -628,13 +628,9 @@ use subs qw(mtp008_detect lm78_detect lm78_isa_detect lm78_alias_detect
},
{
name => "ITE IT8705F / IT8712F / SiS 950",
driver => "Unwritten (IT87xxF)",
i2c_addrs => [0x00..0x68,0x6a..0x7f],
i2c_driver_addrs => [0x20..0x2f],
i2c_detect => sub { ite_detect 0, @_},
driver => "it87",
isa_addrs => [0x290],
isa_detect => sub { ite_isa_detect 0, @_ },
alias_detect => sub { ite_alias_detect 0, @_ },
} ,
{
name => "Serial EEPROM (PC-100 DIMM)",
+214
View File
@@ -2426,6 +2426,220 @@ printf("%d %d %d %d\n", (int) a, (int) b, (int) c, (int) d);
}
void print_it87(const sensors_chip_name *name)
{
char *label = NULL;
double cur, min, max, fdiv;
int alarms_fan, alarms_vin, alarms_temp, valid;
if (!sensors_get_feature(*name,SENSORS_IT87_ALARMS_FAN, &cur)) {
alarms_fan = cur + 0.5;
}
else {
printf("ERROR: Can't get fan alarm data!\n");
alarms_fan = 0;
}
if (!sensors_get_feature(*name,SENSORS_IT87_ALARMS_VIN, &cur)) {
alarms_vin = cur + 0.5;
}
else {
printf("ERROR: Can't get vin alarm data!\n");
alarms_vin = 0;
}
if (!sensors_get_feature(*name,SENSORS_IT87_ALARMS_TEMP, &cur)) {
alarms_temp = cur + 0.5;
}
else {
printf("ERROR: Can't get temp alarm data!\n");
alarms_temp = 0;
}
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN0,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN0,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN0_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN0_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN0?"ALARM":"");
}
} else
printf("ERROR: Can't get IN0 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN1,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN1,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN1_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN1_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN1?"ALARM":"");
}
} else
printf("ERROR: Can't get IN1 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN2,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN2,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN2_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN2_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN2?"ALARM":"");
}
} else
printf("ERROR: Can't get IN2 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN3,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN3,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN3_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN3_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN3?"ALARM":"");
}
} else
printf("ERROR: Can't get IN3 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN4,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN4,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN4_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN4_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN4?"ALARM":"");
}
} else
printf("ERROR: Can't get IN4 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN5,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN5,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN5_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN5_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN5?"ALARM":"");
}
} else
printf("ERROR: Can't get IN5 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN6,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN6,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN6_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN6_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN6?"ALARM":"");
}
} else
printf("ERROR: Can't get IN6 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_IN7,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_IN7,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_IN7_MIN,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_IN7_MAX,&max)) {
if (valid) {
print_label(label,10);
printf("%+6.2f V (min = %+6.2f V, max = %+6.2f V) %s\n",
cur,min,max,alarms_vin&IT87_ALARM_IN7?"ALARM":"");
}
} else
printf("ERROR: Can't get IN7 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_FAN1,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN1,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN1_DIV,&fdiv) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN1_MIN,&min)) {
if (valid) {
print_label(label,10);
printf("%4.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n",
cur,min,fdiv, alarms_fan&IT87_ALARM_FAN1?"ALARM":"");
}
} else
printf("ERROR: Can't get FAN1 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_FAN2,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN2,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN2_DIV,&fdiv) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN2_MIN,&min)) {
if (valid) {
print_label(label,10);
printf("%4.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n",
cur,min,fdiv, alarms_fan&IT87_ALARM_FAN2?"ALARM":"");
}
} else
printf("ERROR: Can't get FAN2 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_FAN3,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN3,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN3_DIV,&fdiv) &&
!sensors_get_feature(*name,SENSORS_IT87_FAN3_MIN,&min)) {
if (valid) {
print_label(label,10);
printf("%4.0f RPM (min = %4.0f RPM, div = %1.0f) %s\n",
cur,min,fdiv, alarms_fan&IT87_ALARM_FAN3?"ALARM":"");
}
} else
printf("ERROR: Can't get FAN3 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_TEMP1,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP1,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP1_LOW,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP1_HIGH,&max)) {
if (valid) {
print_label(label,10);
print_temp_info( cur, max, min, MINMAX );
printf( " %s\n", alarms_temp & IT87_ALARM_TEMP1 ? "ALARM" : "" );
}
} else
printf("ERROR: Can't get TEMP1 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_TEMP2,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP2,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP2_LOW,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP2_HIGH,&max)) {
if (valid) {
print_label(label,10);
print_temp_info( cur, max, min, MINMAX );
printf( " %s\n", alarms_temp & IT87_ALARM_TEMP2 ? "ALARM" : "" );
}
} else
printf("ERROR: Can't get TEMP2 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_TEMP3,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP3,&cur) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP3_LOW,&min) &&
!sensors_get_feature(*name,SENSORS_IT87_TEMP3_HIGH,&max)) {
if (valid) {
print_label(label,10);
print_temp_info( cur, max, min, MINMAX );
printf( " %s\n", alarms_temp & IT87_ALARM_TEMP3 ? "ALARM" : "" );
}
} else
printf("ERROR: Can't get TEMP3 data!\n");
free_the_label(&label);
if (!sensors_get_label_and_valid(*name,SENSORS_IT87_VID,&label,&valid) &&
!sensors_get_feature(*name,SENSORS_IT87_VID,&cur)) {
if (valid) {
print_label(label,10);
printf("%+5.2f V\n",cur);
}
}
free_the_label(&label);
}
void print_unknown_chip(const sensors_chip_name *name)
{
int a,b,valid;
+1
View File
@@ -41,5 +41,6 @@ extern void print_maxilife(const sensors_chip_name *name);
extern void print_ddcmon(const sensors_chip_name *name);
extern void print_eeprom(const sensors_chip_name *name);
extern void print_lm87(const sensors_chip_name *name);
extern void print_it87(const sensors_chip_name *name);
#endif /* def PROG_SENSORS_CHIPS_H */
+2
View File
@@ -324,6 +324,8 @@ void do_a_print(sensors_chip_name name)
print_ddcmon(&name);
else if (!strcmp(name.prefix,"eeprom"))
print_eeprom(&name);
else if (!strcmp(name.prefix,"it87"))
print_it87(&name);
else
print_unknown_chip(&name);
printf("\n");