Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: hwmon: (max6650) Add support for alarms hwmon: (f71882fg) Add support for the F71858F hwmon: (f71882fg) Add temp#_fault sysfs attr for f8000 hwmon: (f71882fg) Sanity check f8000 pwm settings hwmon: (f71882fg) Cleanup f8000 pwm handling hwmon: PCI quirk for hwmon access on MSI MS-7031 board hwmon: (w83627ehf) Add W83627DHG-P support hwmon: (tmp401) Add documentation hwmon: (tmp401) Add support for TI's TMP411 sensors chip hwmon: (tmp401) Add support for TI's TMP401 sensor chip hwmon: (ibmaem) Automatically load on HC10 blade hwmon: Fix more __devexit_p glitches
This commit is contained in:
Коммит
86ade88e15
|
@ -2,14 +2,18 @@ Kernel driver f71882fg
|
|||
======================
|
||||
|
||||
Supported chips:
|
||||
* Fintek F71882FG and F71883FG
|
||||
Prefix: 'f71882fg'
|
||||
* Fintek F71858FG
|
||||
Prefix: 'f71858fg'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F71862FG and F71863FG
|
||||
Prefix: 'f71862fg'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F71882FG and F71883FG
|
||||
Prefix: 'f71882fg'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
Datasheet: Available from the Fintek website
|
||||
* Fintek F8000
|
||||
Prefix: 'f8000'
|
||||
Addresses scanned: none, address read from Super I/O config space
|
||||
|
@ -66,13 +70,13 @@ printed when loading the driver.
|
|||
|
||||
Three different fan control modes are supported; the mode number is written
|
||||
to the pwm#_enable file. Note that not all modes are supported on all
|
||||
chips, and some modes may only be available in RPM / PWM mode on the F8000.
|
||||
chips, and some modes may only be available in RPM / PWM mode.
|
||||
Writing an unsupported mode will result in an invalid parameter error.
|
||||
|
||||
* 1: Manual mode
|
||||
You ask for a specific PWM duty cycle / DC voltage or a specific % of
|
||||
fan#_full_speed by writing to the pwm# file. This mode is only
|
||||
available on the F8000 if the fan channel is in RPM mode.
|
||||
available on the F71858FG / F8000 if the fan channel is in RPM mode.
|
||||
|
||||
* 2: Normal auto mode
|
||||
You can define a number of temperature/fan speed trip points, which % the
|
||||
|
|
|
@ -7,7 +7,7 @@ henceforth as AEM.
|
|||
Supported systems:
|
||||
* Any recent IBM System X server with AEM support.
|
||||
This includes the x3350, x3550, x3650, x3655, x3755, x3850 M2,
|
||||
x3950 M2, and certain HS2x/LS2x/QS2x blades. The IPMI host interface
|
||||
x3950 M2, and certain HC10/HS2x/LS2x/QS2x blades. The IPMI host interface
|
||||
driver ("ipmi-si") needs to be loaded for this driver to do anything.
|
||||
Prefix: 'ibmaem'
|
||||
Datasheet: Not available
|
||||
|
|
|
@ -70,6 +70,7 @@ are interpreted as 0! For more on how written strings are interpreted see the
|
|||
[0-*] denotes any positive number starting from 0
|
||||
[1-*] denotes any positive number starting from 1
|
||||
RO read only value
|
||||
WO write only value
|
||||
RW read/write value
|
||||
|
||||
Read/write values may be read-only for some chips, depending on the
|
||||
|
@ -295,6 +296,24 @@ temp[1-*]_label Suggested temperature channel label.
|
|||
user-space.
|
||||
RO
|
||||
|
||||
temp[1-*]_lowest
|
||||
Historical minimum temperature
|
||||
Unit: millidegree Celsius
|
||||
RO
|
||||
|
||||
temp[1-*]_highest
|
||||
Historical maximum temperature
|
||||
Unit: millidegree Celsius
|
||||
RO
|
||||
|
||||
temp[1-*]_reset_history
|
||||
Reset temp_lowest and temp_highest
|
||||
WO
|
||||
|
||||
temp_reset_history
|
||||
Reset temp_lowest and temp_highest for all sensors
|
||||
WO
|
||||
|
||||
Some chips measure temperature using external thermistors and an ADC, and
|
||||
report the temperature measurement as a voltage. Converting this voltage
|
||||
back to a temperature (or the other way around for limits) requires
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
Kernel driver tmp401
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
* Texas Instruments TMP401
|
||||
Prefix: 'tmp401'
|
||||
Addresses scanned: I2C 0x4c
|
||||
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp401.html
|
||||
* Texas Instruments TMP411
|
||||
Prefix: 'tmp411'
|
||||
Addresses scanned: I2C 0x4c
|
||||
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp411.html
|
||||
|
||||
Authors:
|
||||
Hans de Goede <hdegoede@redhat.com>
|
||||
Andre Prendel <andre.prendel@gmx.de>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for Texas Instruments TMP401 and
|
||||
TMP411 chips. These chips implements one remote and one local
|
||||
temperature sensor. Temperature is measured in degrees
|
||||
Celsius. Resolution of the remote sensor is 0.0625 degree. Local
|
||||
sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
|
||||
supported by the driver so far, so using the default resolution of 0.5
|
||||
degree).
|
||||
|
||||
The driver provides the common sysfs-interface for temperatures (see
|
||||
/Documentation/hwmon/sysfs-interface under Temperatures).
|
||||
|
||||
The TMP411 chip is compatible with TMP401. It provides some additional
|
||||
features.
|
||||
|
||||
* Minimum and Maximum temperature measured since power-on, chip-reset
|
||||
|
||||
Exported via sysfs attributes tempX_lowest and tempX_highest.
|
||||
|
||||
* Reset of historical minimum/maximum temperature measurements
|
||||
|
||||
Exported via sysfs attribute temp_reset_history. Writing 1 to this
|
||||
file triggers a reset.
|
|
@ -12,6 +12,10 @@ Supported chips:
|
|||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet:
|
||||
http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf
|
||||
* Winbond W83627DHG-P
|
||||
Prefix: 'w83627dhg'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
Datasheet: not available
|
||||
* Winbond W83667HG
|
||||
Prefix: 'w83667hg'
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
|
@ -28,8 +32,8 @@ Description
|
|||
-----------
|
||||
|
||||
This driver implements support for the Winbond W83627EHF, W83627EHG,
|
||||
W83627DHG and W83667HG super I/O chips. We will refer to them collectively
|
||||
as Winbond chips.
|
||||
W83627DHG, W83627DHG-P and W83667HG super I/O chips. We will refer to them
|
||||
collectively as Winbond chips.
|
||||
|
||||
The chips implement three temperature sensors, five fan rotation
|
||||
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
|
||||
|
@ -135,3 +139,6 @@ done in the driver for all register addresses.
|
|||
The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
|
||||
the ICH8 southbridge gets that data via PECI from the DHG, so that the
|
||||
southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.
|
||||
|
||||
The DHG-P has an additional automatic fan speed control mode named Smart Fan
|
||||
(TM) III+. This mode is not yet supported by the driver.
|
||||
|
|
|
@ -306,11 +306,11 @@ config SENSORS_F71805F
|
|||
will be called f71805f.
|
||||
|
||||
config SENSORS_F71882FG
|
||||
tristate "Fintek F71862FG, F71882FG and F8000"
|
||||
tristate "Fintek F71858FG, F71862FG, F71882FG and F8000"
|
||||
depends on EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for hardware monitoring
|
||||
features of the Fintek F71882FG/F71883FG, F71862FG/71863FG
|
||||
features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG
|
||||
and F8000 Super-I/O chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
|
@ -418,7 +418,7 @@ config SENSORS_IBMAEM
|
|||
power sensors and capping hardware in various IBM System X
|
||||
servers that support Active Energy Manager. This includes
|
||||
the x3350, x3550, x3650, x3655, x3755, x3850 M2, x3950 M2,
|
||||
and certain HS2x/LS2x/QS2x blades.
|
||||
and certain HC10/HS2x/LS2x/QS2x blades.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ibmaem.
|
||||
|
@ -787,6 +787,16 @@ config SENSORS_THMC50
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called thmc50.
|
||||
|
||||
config SENSORS_TMP401
|
||||
tristate "Texas Instruments TMP401 and compatibles"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments TMP401 and
|
||||
TMP411 temperature sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tmp401.
|
||||
|
||||
config SENSORS_VIA686A
|
||||
tristate "VIA686A"
|
||||
depends on PCI
|
||||
|
|
|
@ -82,6 +82,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
|
|||
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
|
||||
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
|
||||
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
|
||||
obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
|
||||
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
|
||||
* Copyright (C) 2007,2008 by Hans de Goede <hdegoede@redhat.com> *
|
||||
* Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
|
||||
* *
|
||||
* 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 *
|
||||
|
@ -32,6 +32,7 @@
|
|||
|
||||
#define DRVNAME "f71882fg"
|
||||
|
||||
#define SIO_F71858FG_LD_HWM 0x02 /* Hardware monitor logical device */
|
||||
#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */
|
||||
#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
|
||||
#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
|
||||
|
@ -44,6 +45,7 @@
|
|||
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
|
||||
|
||||
#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
|
||||
#define SIO_F71858_ID 0x0507 /* Chipset ID */
|
||||
#define SIO_F71862_ID 0x0601 /* Chipset ID */
|
||||
#define SIO_F71882_ID 0x0541 /* Chipset ID */
|
||||
#define SIO_F8000_ID 0x0581 /* Chipset ID */
|
||||
|
@ -70,6 +72,7 @@
|
|||
#define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr))
|
||||
#define F71882FG_REG_TEMP_STATUS 0x62
|
||||
#define F71882FG_REG_TEMP_BEEP 0x63
|
||||
#define F71882FG_REG_TEMP_CONFIG 0x69
|
||||
#define F71882FG_REG_TEMP_HYST(nr) (0x6C + (nr))
|
||||
#define F71882FG_REG_TEMP_TYPE 0x6B
|
||||
#define F71882FG_REG_TEMP_DIODE_OPEN 0x6F
|
||||
|
@ -92,9 +95,10 @@ static unsigned short force_id;
|
|||
module_param(force_id, ushort, 0);
|
||||
MODULE_PARM_DESC(force_id, "Override the detected device ID");
|
||||
|
||||
enum chips { f71862fg, f71882fg, f8000 };
|
||||
enum chips { f71858fg, f71862fg, f71882fg, f8000 };
|
||||
|
||||
static const char *f71882fg_names[] = {
|
||||
"f71858fg",
|
||||
"f71862fg",
|
||||
"f71882fg",
|
||||
"f8000",
|
||||
|
@ -119,6 +123,7 @@ struct f71882fg_data {
|
|||
struct device *hwmon_dev;
|
||||
|
||||
struct mutex update_lock;
|
||||
int temp_start; /* temp numbering start (0 or 1) */
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
unsigned long last_limits; /* In jiffies */
|
||||
|
@ -136,7 +141,7 @@ struct f71882fg_data {
|
|||
/* Note: all models have only 3 temperature channels, but on some
|
||||
they are addressed as 0-2 and on others as 1-3, so for coding
|
||||
convenience we reserve space for 4 channels */
|
||||
u8 temp[4];
|
||||
u16 temp[4];
|
||||
u8 temp_ovt[4];
|
||||
u8 temp_high[4];
|
||||
u8 temp_hyst[2]; /* 2 hysts stored per reg */
|
||||
|
@ -144,6 +149,7 @@ struct f71882fg_data {
|
|||
u8 temp_status;
|
||||
u8 temp_beep;
|
||||
u8 temp_diode_open;
|
||||
u8 temp_config;
|
||||
u8 pwm[4];
|
||||
u8 pwm_enable;
|
||||
u8 pwm_auto_point_hyst[2];
|
||||
|
@ -247,11 +253,55 @@ static struct platform_driver f71882fg_driver = {
|
|||
.name = DRVNAME,
|
||||
},
|
||||
.probe = f71882fg_probe,
|
||||
.remove = __devexit_p(f71882fg_remove),
|
||||
.remove = f71882fg_remove,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
|
||||
/* Temp and in attr for the f71858fg */
|
||||
static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
|
||||
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
|
||||
SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
|
||||
SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max,
|
||||
store_temp_max, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
|
||||
store_temp_max_hyst, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit,
|
||||
store_temp_crit, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
|
||||
0, 0),
|
||||
SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
|
||||
SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
|
||||
SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max,
|
||||
store_temp_max, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
|
||||
store_temp_max_hyst, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit,
|
||||
store_temp_crit, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
|
||||
0, 1),
|
||||
SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
|
||||
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max,
|
||||
store_temp_max, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst,
|
||||
store_temp_max_hyst, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit,
|
||||
store_temp_crit, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL,
|
||||
0, 2),
|
||||
SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
|
||||
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
|
||||
};
|
||||
|
||||
/* Temp and in attr common to both the f71862fg and f71882fg */
|
||||
static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
|
||||
SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
|
||||
|
@ -344,6 +394,7 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
|
|||
SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
|
||||
store_temp_max, 0, 0),
|
||||
SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4),
|
||||
SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0),
|
||||
SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit,
|
||||
store_temp_crit, 0, 1),
|
||||
|
@ -351,12 +402,14 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
|
|||
store_temp_max, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5),
|
||||
SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1),
|
||||
SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit,
|
||||
store_temp_crit, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max,
|
||||
store_temp_max, 0, 2),
|
||||
SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6),
|
||||
SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
|
||||
};
|
||||
|
||||
/* Fan / PWM attr common to all models */
|
||||
|
@ -395,6 +448,9 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[] = {
|
|||
show_pwm_auto_point_channel,
|
||||
store_pwm_auto_point_channel, 0, 1),
|
||||
|
||||
SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
|
||||
show_pwm_interpolate, store_pwm_interpolate, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
|
||||
|
@ -450,9 +506,6 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
|
|||
SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
|
||||
|
||||
SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
|
||||
1, 2),
|
||||
|
@ -473,22 +526,8 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
|
|||
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
|
||||
};
|
||||
|
||||
/* Fan / PWM attr for the f71882fg */
|
||||
static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
|
||||
SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 0),
|
||||
SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 1),
|
||||
SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 2),
|
||||
SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
|
||||
show_fan_full_speed,
|
||||
store_fan_full_speed, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
|
||||
|
||||
/* Fan / PWM attr common to both the f71882fg and f71858fg */
|
||||
static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
|
||||
SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
|
||||
0, 0),
|
||||
|
@ -565,9 +604,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
|
|||
SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 1),
|
||||
|
||||
SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 0, 2),
|
||||
SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
|
||||
0, 2),
|
||||
|
@ -605,6 +641,24 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
|
|||
show_pwm_auto_point_temp_hyst, NULL, 2, 2),
|
||||
SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_pwm_auto_point_temp_hyst, NULL, 3, 2),
|
||||
};
|
||||
|
||||
/* Fan / PWM attr found on the f71882fg but not on the f71858fg */
|
||||
static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
|
||||
SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 0),
|
||||
SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 1),
|
||||
SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 2),
|
||||
|
||||
SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
|
||||
show_fan_full_speed,
|
||||
store_fan_full_speed, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
|
||||
store_fan_beep, 0, 3),
|
||||
SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
|
||||
|
||||
SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
|
||||
SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
|
@ -659,8 +713,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
|
|||
static struct sensor_device_attribute_2 f8000_fan_attr[] = {
|
||||
SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
|
||||
|
||||
SENSOR_ATTR_2(pwm3, S_IRUGO, show_pwm, NULL, 0, 2),
|
||||
|
||||
SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
|
||||
show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
|
||||
0, 2),
|
||||
|
@ -857,13 +909,20 @@ static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
|
|||
outb(val & 255, data->addr + DATA_REG_OFFSET);
|
||||
}
|
||||
|
||||
static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
|
||||
{
|
||||
if (data->type == f71858fg)
|
||||
return f71882fg_read16(data, F71882FG_REG_TEMP(nr));
|
||||
else
|
||||
return f71882fg_read8(data, F71882FG_REG_TEMP(nr));
|
||||
}
|
||||
|
||||
static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
||||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr, reg = 0, reg2;
|
||||
int nr_fans = (data->type == f71882fg) ? 4 : 3;
|
||||
int nr_ins = (data->type == f8000) ? 3 : 9;
|
||||
int temp_start = (data->type == f8000) ? 0 : 1;
|
||||
int nr_ins = (data->type == f71858fg || data->type == f8000) ? 3 : 9;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
|
@ -878,7 +937,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
|||
}
|
||||
|
||||
/* Get High & boundary temps*/
|
||||
for (nr = temp_start; nr < 3 + temp_start; nr++) {
|
||||
for (nr = data->temp_start; nr < 3 + data->temp_start; nr++) {
|
||||
data->temp_ovt[nr] = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_OVT(nr));
|
||||
data->temp_high[nr] = f71882fg_read8(data,
|
||||
|
@ -886,14 +945,17 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
|||
}
|
||||
|
||||
if (data->type != f8000) {
|
||||
data->fan_beep = f71882fg_read8(data,
|
||||
F71882FG_REG_FAN_BEEP);
|
||||
data->temp_beep = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_BEEP);
|
||||
data->temp_hyst[0] = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_HYST(0));
|
||||
data->temp_hyst[1] = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_HYST(1));
|
||||
}
|
||||
|
||||
if (data->type == f71862fg || data->type == f71882fg) {
|
||||
data->fan_beep = f71882fg_read8(data,
|
||||
F71882FG_REG_FAN_BEEP);
|
||||
data->temp_beep = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_BEEP);
|
||||
/* Have to hardcode type, because temp1 is special */
|
||||
reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE);
|
||||
data->temp_type[2] = (reg & 0x04) ? 2 : 4;
|
||||
|
@ -904,10 +966,10 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
|||
data->temp_type[1] = 6 /* PECI */;
|
||||
else if ((reg2 & 0x03) == 0x02)
|
||||
data->temp_type[1] = 5 /* AMDSI */;
|
||||
else if (data->type != f8000)
|
||||
else if (data->type == f71862fg || data->type == f71882fg)
|
||||
data->temp_type[1] = (reg & 0x02) ? 2 : 4;
|
||||
else
|
||||
data->temp_type[1] = 2; /* F8000 only supports BJT */
|
||||
data->temp_type[1] = 2; /* Only supports BJT */
|
||||
|
||||
data->pwm_enable = f71882fg_read8(data,
|
||||
F71882FG_REG_PWM_ENABLE);
|
||||
|
@ -963,9 +1025,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
|
|||
F71882FG_REG_TEMP_STATUS);
|
||||
data->temp_diode_open = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP_DIODE_OPEN);
|
||||
for (nr = temp_start; nr < 3 + temp_start; nr++)
|
||||
data->temp[nr] = f71882fg_read8(data,
|
||||
F71882FG_REG_TEMP(nr));
|
||||
for (nr = data->temp_start; nr < 3 + data->temp_start; nr++)
|
||||
data->temp[nr] = f71882fg_read_temp(data, nr);
|
||||
|
||||
data->fan_status = f71882fg_read8(data,
|
||||
F71882FG_REG_FAN_STATUS);
|
||||
|
@ -1168,8 +1229,24 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
|
|||
{
|
||||
struct f71882fg_data *data = f71882fg_update_device(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
int sign, temp;
|
||||
|
||||
return sprintf(buf, "%d\n", data->temp[nr] * 1000);
|
||||
if (data->type == f71858fg) {
|
||||
/* TEMP_TABLE_SEL 1 or 3 ? */
|
||||
if (data->temp_config & 1) {
|
||||
sign = data->temp[nr] & 0x0001;
|
||||
temp = (data->temp[nr] >> 5) & 0x7ff;
|
||||
} else {
|
||||
sign = data->temp[nr] & 0x8000;
|
||||
temp = (data->temp[nr] >> 5) & 0x3ff;
|
||||
}
|
||||
temp *= 125;
|
||||
if (sign)
|
||||
temp -= 128000;
|
||||
} else
|
||||
temp = data->temp[nr] * 1000;
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t show_temp_max(struct device *dev, struct device_attribute
|
||||
|
@ -1440,6 +1517,10 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
|
|||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
/* Special case for F8000 pwm channel 3 which only does auto mode */
|
||||
if (data->type == f8000 && nr == 2 && val != 2)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
|
||||
/* Special case for F8000 auto PWM mode / Thermostat mode */
|
||||
|
@ -1458,6 +1539,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
|
|||
} else {
|
||||
switch (val) {
|
||||
case 1:
|
||||
/* The f71858fg does not support manual RPM mode */
|
||||
if (data->type == f71858fg &&
|
||||
((data->pwm_enable >> (2 * nr)) & 1)) {
|
||||
count = -EINVAL;
|
||||
goto leave;
|
||||
}
|
||||
data->pwm_enable |= 2 << (2 * nr);
|
||||
break; /* Manual */
|
||||
case 2:
|
||||
|
@ -1616,9 +1703,9 @@ static ssize_t show_pwm_auto_point_channel(struct device *dev,
|
|||
int result;
|
||||
struct f71882fg_data *data = f71882fg_update_device(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
int temp_start = (data->type == f8000) ? 0 : 1;
|
||||
|
||||
result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - temp_start);
|
||||
result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) -
|
||||
data->temp_start);
|
||||
|
||||
return sprintf(buf, "%d\n", result);
|
||||
}
|
||||
|
@ -1629,7 +1716,6 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
|
|||
{
|
||||
struct f71882fg_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr_2(devattr)->index;
|
||||
int temp_start = (data->type == f8000) ? 0 : 1;
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
|
||||
switch (val) {
|
||||
|
@ -1645,7 +1731,7 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
val += temp_start;
|
||||
val += data->temp_start;
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm_auto_point_mapping[nr] =
|
||||
f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr));
|
||||
|
@ -1721,6 +1807,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
|||
|
||||
data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
|
||||
data->type = sio_data->type;
|
||||
data->temp_start =
|
||||
(data->type == f71858fg || data->type == f8000) ? 0 : 1;
|
||||
mutex_init(&data->update_lock);
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
|
@ -1736,19 +1824,6 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
|||
goto exit_free;
|
||||
}
|
||||
|
||||
data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
|
||||
/* If it is a 71862 and the fan / pwm part is enabled sanity check
|
||||
the pwm settings */
|
||||
if (data->type == f71862fg && (start_reg & 0x02)) {
|
||||
if ((data->pwm_enable & 0x15) != 0x15) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid (reserved) pwm settings: 0x%02x\n",
|
||||
(unsigned int)data->pwm_enable);
|
||||
err = -ENODEV;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register sysfs interface files */
|
||||
err = device_create_file(&pdev->dev, &dev_attr_name);
|
||||
if (err)
|
||||
|
@ -1756,6 +1831,20 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
|||
|
||||
if (start_reg & 0x01) {
|
||||
switch (data->type) {
|
||||
case f71858fg:
|
||||
data->temp_config =
|
||||
f71882fg_read8(data, F71882FG_REG_TEMP_CONFIG);
|
||||
if (data->temp_config & 0x10)
|
||||
/* The f71858fg temperature alarms behave as
|
||||
the f8000 alarms in this mode */
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f8000_in_temp_attr,
|
||||
ARRAY_SIZE(f8000_in_temp_attr));
|
||||
else
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f71858fg_in_temp_attr,
|
||||
ARRAY_SIZE(f71858fg_in_temp_attr));
|
||||
break;
|
||||
case f71882fg:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f71882fg_in_temp_attr,
|
||||
|
@ -1779,6 +1868,35 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (start_reg & 0x02) {
|
||||
data->pwm_enable =
|
||||
f71882fg_read8(data, F71882FG_REG_PWM_ENABLE);
|
||||
|
||||
/* Sanity check the pwm settings */
|
||||
switch (data->type) {
|
||||
case f71858fg:
|
||||
err = 0;
|
||||
for (i = 0; i < nr_fans; i++)
|
||||
if (((data->pwm_enable >> (i * 2)) & 3) == 3)
|
||||
err = 1;
|
||||
break;
|
||||
case f71862fg:
|
||||
err = (data->pwm_enable & 0x15) != 0x15;
|
||||
break;
|
||||
case f71882fg:
|
||||
err = 0;
|
||||
break;
|
||||
case f8000:
|
||||
err = data->pwm_enable & 0x20;
|
||||
break;
|
||||
}
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid (reserved) pwm settings: 0x%02x\n",
|
||||
(unsigned int)data->pwm_enable);
|
||||
err = -ENODEV;
|
||||
goto exit_unregister_sysfs;
|
||||
}
|
||||
|
||||
err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr,
|
||||
ARRAY_SIZE(fxxxx_fan_attr));
|
||||
if (err)
|
||||
|
@ -1794,6 +1912,13 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
|
|||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f71882fg_fan_attr,
|
||||
ARRAY_SIZE(f71882fg_fan_attr));
|
||||
if (err)
|
||||
goto exit_unregister_sysfs;
|
||||
/* fall through! */
|
||||
case f71858fg:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
f71882fg_f71858fg_fan_attr,
|
||||
ARRAY_SIZE(f71882fg_f71858fg_fan_attr));
|
||||
break;
|
||||
case f8000:
|
||||
err = f71882fg_create_sysfs_files(pdev,
|
||||
|
@ -1878,6 +2003,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
|
|||
|
||||
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
|
||||
switch (devid) {
|
||||
case SIO_F71858_ID:
|
||||
sio_data->type = f71858fg;
|
||||
break;
|
||||
case SIO_F71862_ID:
|
||||
sio_data->type = f71862fg;
|
||||
break;
|
||||
|
@ -1892,7 +2020,11 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
superio_select(sioaddr, SIO_F71882FG_LD_HWM);
|
||||
if (sio_data->type == f71858fg)
|
||||
superio_select(sioaddr, SIO_F71858FG_LD_HWM);
|
||||
else
|
||||
superio_select(sioaddr, SIO_F71882FG_LD_HWM);
|
||||
|
||||
if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) {
|
||||
printk(KERN_WARNING DRVNAME ": Device not activated\n");
|
||||
goto exit;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/hwmon.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define HWMON_ID_PREFIX "hwmon"
|
||||
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
|
||||
|
@ -86,8 +87,36 @@ void hwmon_device_unregister(struct device *dev)
|
|||
"hwmon_device_unregister() failed: bad class ID!\n");
|
||||
}
|
||||
|
||||
static void __init hwmon_pci_quirks(void)
|
||||
{
|
||||
#if defined CONFIG_X86 && defined CONFIG_PCI
|
||||
struct pci_dev *sb;
|
||||
u16 base;
|
||||
u8 enable;
|
||||
|
||||
/* Open access to 0x295-0x296 on MSI MS-7031 */
|
||||
sb = pci_get_device(PCI_VENDOR_ID_ATI, 0x436c, NULL);
|
||||
if (sb &&
|
||||
(sb->subsystem_vendor == 0x1462 && /* MSI */
|
||||
sb->subsystem_device == 0x0031)) { /* MS-7031 */
|
||||
|
||||
pci_read_config_byte(sb, 0x48, &enable);
|
||||
pci_read_config_word(sb, 0x64, &base);
|
||||
|
||||
if (base == 0 && !(enable & BIT(2))) {
|
||||
dev_info(&sb->dev,
|
||||
"Opening wide generic port at 0x295\n");
|
||||
pci_write_config_word(sb, 0x64, 0x295);
|
||||
pci_write_config_byte(sb, 0x48, enable | BIT(2));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __init hwmon_init(void)
|
||||
{
|
||||
hwmon_pci_quirks();
|
||||
|
||||
hwmon_class = class_create(THIS_MODULE, "hwmon");
|
||||
if (IS_ERR(hwmon_class)) {
|
||||
printk(KERN_ERR "hwmon.c: couldn't create sysfs class\n");
|
||||
|
|
|
@ -1127,3 +1127,4 @@ MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*");
|
|||
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*");
|
||||
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*");
|
||||
MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*");
|
||||
MODULE_ALIAS("dmi:bvnIBM:*:pnIBMBladeHC10-*");
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* also work with the MAX6651. It does not distinguish max6650 and max6651
|
||||
* chips.
|
||||
*
|
||||
* Tha datasheet was last seen at:
|
||||
* The datasheet was last seen at:
|
||||
*
|
||||
* http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
|
||||
*
|
||||
|
@ -98,6 +98,16 @@ I2C_CLIENT_INSMOD_1(max6650);
|
|||
#define MAX6650_CFG_MODE_OPEN_LOOP 0x30
|
||||
#define MAX6650_COUNT_MASK 0x03
|
||||
|
||||
/*
|
||||
* Alarm status register bits
|
||||
*/
|
||||
|
||||
#define MAX6650_ALRM_MAX 0x01
|
||||
#define MAX6650_ALRM_MIN 0x02
|
||||
#define MAX6650_ALRM_TACH 0x04
|
||||
#define MAX6650_ALRM_GPIO1 0x08
|
||||
#define MAX6650_ALRM_GPIO2 0x10
|
||||
|
||||
/* Minimum and maximum values of the FAN-RPM */
|
||||
#define FAN_RPM_MIN 240
|
||||
#define FAN_RPM_MAX 30000
|
||||
|
@ -151,6 +161,7 @@ struct max6650_data
|
|||
u8 tach[4];
|
||||
u8 count;
|
||||
u8 dac;
|
||||
u8 alarm;
|
||||
};
|
||||
|
||||
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
|
||||
|
@ -418,6 +429,33 @@ static ssize_t set_div(struct device *dev, struct device_attribute *devattr,
|
|||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get alarm stati:
|
||||
* Possible values:
|
||||
* 0 = no alarm
|
||||
* 1 = alarm
|
||||
*/
|
||||
|
||||
static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6650_data *data = max6650_update_device(dev);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int alarm = 0;
|
||||
|
||||
if (data->alarm & attr->index) {
|
||||
mutex_lock(&data->update_lock);
|
||||
alarm = 1;
|
||||
data->alarm &= ~attr->index;
|
||||
data->alarm |= i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_ALARM);
|
||||
mutex_unlock(&data->update_lock);
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", alarm);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
|
||||
|
@ -426,7 +464,41 @@ static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target);
|
|||
static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div);
|
||||
static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable);
|
||||
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
|
||||
static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, get_alarm, NULL,
|
||||
MAX6650_ALRM_MAX);
|
||||
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, get_alarm, NULL,
|
||||
MAX6650_ALRM_MIN);
|
||||
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_alarm, NULL,
|
||||
MAX6650_ALRM_TACH);
|
||||
static SENSOR_DEVICE_ATTR(gpio1_alarm, S_IRUGO, get_alarm, NULL,
|
||||
MAX6650_ALRM_GPIO1);
|
||||
static SENSOR_DEVICE_ATTR(gpio2_alarm, S_IRUGO, get_alarm, NULL,
|
||||
MAX6650_ALRM_GPIO2);
|
||||
|
||||
static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN);
|
||||
struct device_attribute *devattr;
|
||||
|
||||
/*
|
||||
* Hide the alarms that have not been enabled by the firmware
|
||||
*/
|
||||
|
||||
devattr = container_of(a, struct device_attribute, attr);
|
||||
if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_fan1_fault.dev_attr
|
||||
|| devattr == &sensor_dev_attr_gpio1_alarm.dev_attr
|
||||
|| devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) {
|
||||
if (!(alarm_en & to_sensor_dev_attr(devattr)->index))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static struct attribute *max6650_attrs[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
|
@ -437,11 +509,17 @@ static struct attribute *max6650_attrs[] = {
|
|||
&dev_attr_fan1_div.attr,
|
||||
&dev_attr_pwm1_enable.attr,
|
||||
&dev_attr_pwm1.attr,
|
||||
&sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_gpio1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_gpio2_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group max6650_attr_grp = {
|
||||
.attrs = max6650_attrs,
|
||||
.is_visible = max6650_attrs_visible,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -659,6 +737,12 @@ static struct max6650_data *max6650_update_device(struct device *dev)
|
|||
MAX6650_REG_COUNT);
|
||||
data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
|
||||
|
||||
/* Alarms are cleared on read in case the condition that
|
||||
* caused the alarm is removed. Keep the value latched here
|
||||
* for providing the register through different alarm files. */
|
||||
data->alarm |= i2c_smbus_read_byte_data(client,
|
||||
MAX6650_REG_ALARM);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
|
|
@ -627,35 +627,35 @@ static struct platform_driver sht_drivers[] = {
|
|||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sht15_probe,
|
||||
.remove = sht15_remove,
|
||||
.remove = __devexit_p(sht15_remove),
|
||||
}, {
|
||||
.driver = {
|
||||
.name = "sht11",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sht15_probe,
|
||||
.remove = sht15_remove,
|
||||
.remove = __devexit_p(sht15_remove),
|
||||
}, {
|
||||
.driver = {
|
||||
.name = "sht15",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sht15_probe,
|
||||
.remove = sht15_remove,
|
||||
.remove = __devexit_p(sht15_remove),
|
||||
}, {
|
||||
.driver = {
|
||||
.name = "sht71",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sht15_probe,
|
||||
.remove = sht15_remove,
|
||||
.remove = __devexit_p(sht15_remove),
|
||||
}, {
|
||||
.driver = {
|
||||
.name = "sht75",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sht15_probe,
|
||||
.remove = sht15_remove,
|
||||
.remove = __devexit_p(sht15_remove),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,690 @@
|
|||
/* tmp401.c
|
||||
*
|
||||
* Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com>
|
||||
* Preliminary tmp411 support by:
|
||||
* Gabriel Konat, Sander Leget, Wouter Willems
|
||||
* Copyright (C) 2009 Andre Prendel <andre.prendel@gmx.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver for the Texas Instruments TMP401 SMBUS temperature sensor IC.
|
||||
*
|
||||
* Note this IC is in some aspect similar to the LM90, but it has quite a
|
||||
* few differences too, for example the local temp has a higher resolution
|
||||
* and thus has 16 bits registers for its value and limit instead of 8 bits.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_2(tmp401, tmp411);
|
||||
|
||||
/*
|
||||
* The TMP401 registers, note some registers have different addresses for
|
||||
* reading and writing
|
||||
*/
|
||||
#define TMP401_STATUS 0x02
|
||||
#define TMP401_CONFIG_READ 0x03
|
||||
#define TMP401_CONFIG_WRITE 0x09
|
||||
#define TMP401_CONVERSION_RATE_READ 0x04
|
||||
#define TMP401_CONVERSION_RATE_WRITE 0x0A
|
||||
#define TMP401_TEMP_CRIT_HYST 0x21
|
||||
#define TMP401_CONSECUTIVE_ALERT 0x22
|
||||
#define TMP401_MANUFACTURER_ID_REG 0xFE
|
||||
#define TMP401_DEVICE_ID_REG 0xFF
|
||||
#define TMP411_N_FACTOR_REG 0x18
|
||||
|
||||
static const u8 TMP401_TEMP_MSB[2] = { 0x00, 0x01 };
|
||||
static const u8 TMP401_TEMP_LSB[2] = { 0x15, 0x10 };
|
||||
static const u8 TMP401_TEMP_LOW_LIMIT_MSB_READ[2] = { 0x06, 0x08 };
|
||||
static const u8 TMP401_TEMP_LOW_LIMIT_MSB_WRITE[2] = { 0x0C, 0x0E };
|
||||
static const u8 TMP401_TEMP_LOW_LIMIT_LSB[2] = { 0x17, 0x14 };
|
||||
static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_READ[2] = { 0x05, 0x07 };
|
||||
static const u8 TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[2] = { 0x0B, 0x0D };
|
||||
static const u8 TMP401_TEMP_HIGH_LIMIT_LSB[2] = { 0x16, 0x13 };
|
||||
/* These are called the THERM limit / hysteresis / mask in the datasheet */
|
||||
static const u8 TMP401_TEMP_CRIT_LIMIT[2] = { 0x20, 0x19 };
|
||||
|
||||
static const u8 TMP411_TEMP_LOWEST_MSB[2] = { 0x30, 0x34 };
|
||||
static const u8 TMP411_TEMP_LOWEST_LSB[2] = { 0x31, 0x35 };
|
||||
static const u8 TMP411_TEMP_HIGHEST_MSB[2] = { 0x32, 0x36 };
|
||||
static const u8 TMP411_TEMP_HIGHEST_LSB[2] = { 0x33, 0x37 };
|
||||
|
||||
/* Flags */
|
||||
#define TMP401_CONFIG_RANGE 0x04
|
||||
#define TMP401_CONFIG_SHUTDOWN 0x40
|
||||
#define TMP401_STATUS_LOCAL_CRIT 0x01
|
||||
#define TMP401_STATUS_REMOTE_CRIT 0x02
|
||||
#define TMP401_STATUS_REMOTE_OPEN 0x04
|
||||
#define TMP401_STATUS_REMOTE_LOW 0x08
|
||||
#define TMP401_STATUS_REMOTE_HIGH 0x10
|
||||
#define TMP401_STATUS_LOCAL_LOW 0x20
|
||||
#define TMP401_STATUS_LOCAL_HIGH 0x40
|
||||
|
||||
/* Manufacturer / Device ID's */
|
||||
#define TMP401_MANUFACTURER_ID 0x55
|
||||
#define TMP401_DEVICE_ID 0x11
|
||||
#define TMP411_DEVICE_ID 0x12
|
||||
|
||||
/*
|
||||
* Functions declarations
|
||||
*/
|
||||
|
||||
static int tmp401_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id);
|
||||
static int tmp401_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info);
|
||||
static int tmp401_remove(struct i2c_client *client);
|
||||
static struct tmp401_data *tmp401_update_device(struct device *dev);
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id tmp401_id[] = {
|
||||
{ "tmp401", tmp401 },
|
||||
{ "tmp411", tmp411 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tmp401_id);
|
||||
|
||||
static struct i2c_driver tmp401_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "tmp401",
|
||||
},
|
||||
.probe = tmp401_probe,
|
||||
.remove = tmp401_remove,
|
||||
.id_table = tmp401_id,
|
||||
.detect = tmp401_detect,
|
||||
.address_data = &addr_data,
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
|
||||
struct tmp401_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
int kind;
|
||||
|
||||
/* register values */
|
||||
u8 status;
|
||||
u8 config;
|
||||
u16 temp[2];
|
||||
u16 temp_low[2];
|
||||
u16 temp_high[2];
|
||||
u8 temp_crit[2];
|
||||
u8 temp_crit_hyst;
|
||||
u16 temp_lowest[2];
|
||||
u16 temp_highest[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* Sysfs attr show / store functions
|
||||
*/
|
||||
|
||||
static int tmp401_register_to_temp(u16 reg, u8 config)
|
||||
{
|
||||
int temp = reg;
|
||||
|
||||
if (config & TMP401_CONFIG_RANGE)
|
||||
temp -= 64 * 256;
|
||||
|
||||
return (temp * 625 + 80) / 160;
|
||||
}
|
||||
|
||||
static u16 tmp401_temp_to_register(long temp, u8 config)
|
||||
{
|
||||
if (config & TMP401_CONFIG_RANGE) {
|
||||
temp = SENSORS_LIMIT(temp, -64000, 191000);
|
||||
temp += 64000;
|
||||
} else
|
||||
temp = SENSORS_LIMIT(temp, 0, 127000);
|
||||
|
||||
return (temp * 160 + 312) / 625;
|
||||
}
|
||||
|
||||
static int tmp401_crit_register_to_temp(u8 reg, u8 config)
|
||||
{
|
||||
int temp = reg;
|
||||
|
||||
if (config & TMP401_CONFIG_RANGE)
|
||||
temp -= 64;
|
||||
|
||||
return temp * 1000;
|
||||
}
|
||||
|
||||
static u8 tmp401_crit_temp_to_register(long temp, u8 config)
|
||||
{
|
||||
if (config & TMP401_CONFIG_RANGE) {
|
||||
temp = SENSORS_LIMIT(temp, -64000, 191000);
|
||||
temp += 64000;
|
||||
} else
|
||||
temp = SENSORS_LIMIT(temp, 0, 127000);
|
||||
|
||||
return (temp + 500) / 1000;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_value(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_register_to_temp(data->temp[index], data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_min(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_register_to_temp(data->temp_low[index], data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_max(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_register_to_temp(data->temp_high[index], data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_crit(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_crit_register_to_temp(data->temp_crit[index],
|
||||
data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_crit_hyst(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int temp, index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
temp = tmp401_crit_register_to_temp(data->temp_crit[index],
|
||||
data->config);
|
||||
temp -= data->temp_crit_hyst * 1000;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t show_temp_lowest(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_register_to_temp(data->temp_lowest[index],
|
||||
data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_temp_highest(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
tmp401_register_to_temp(data->temp_highest[index],
|
||||
data->config));
|
||||
}
|
||||
|
||||
static ssize_t show_status(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int mask = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
|
||||
if (data->status & mask)
|
||||
return sprintf(buf, "1\n");
|
||||
else
|
||||
return sprintf(buf, "0\n");
|
||||
}
|
||||
|
||||
static ssize_t store_temp_min(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
long val;
|
||||
u16 reg;
|
||||
|
||||
if (strict_strtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
reg = tmp401_temp_to_register(val, data->config);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_LOW_LIMIT_MSB_WRITE[index], reg >> 8);
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_LOW_LIMIT_LSB[index], reg & 0xFF);
|
||||
|
||||
data->temp_low[index] = reg;
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t store_temp_max(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
long val;
|
||||
u16 reg;
|
||||
|
||||
if (strict_strtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
reg = tmp401_temp_to_register(val, data->config);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_HIGH_LIMIT_MSB_WRITE[index], reg >> 8);
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_HIGH_LIMIT_LSB[index], reg & 0xFF);
|
||||
|
||||
data->temp_high[index] = reg;
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t store_temp_crit(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
int index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
long val;
|
||||
u8 reg;
|
||||
|
||||
if (strict_strtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
reg = tmp401_crit_temp_to_register(val, data->config);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_CRIT_LIMIT[index], reg);
|
||||
|
||||
data->temp_crit[index] = reg;
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t store_temp_crit_hyst(struct device *dev, struct device_attribute
|
||||
*devattr, const char *buf, size_t count)
|
||||
{
|
||||
int temp, index = to_sensor_dev_attr(devattr)->index;
|
||||
struct tmp401_data *data = tmp401_update_device(dev);
|
||||
long val;
|
||||
u8 reg;
|
||||
|
||||
if (strict_strtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (data->config & TMP401_CONFIG_RANGE)
|
||||
val = SENSORS_LIMIT(val, -64000, 191000);
|
||||
else
|
||||
val = SENSORS_LIMIT(val, 0, 127000);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
temp = tmp401_crit_register_to_temp(data->temp_crit[index],
|
||||
data->config);
|
||||
val = SENSORS_LIMIT(val, temp - 255000, temp);
|
||||
reg = ((temp - val) + 500) / 1000;
|
||||
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP401_TEMP_CRIT_HYST, reg);
|
||||
|
||||
data->temp_crit_hyst = reg;
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resets the historical measurements of minimum and maximum temperatures.
|
||||
* This is done by writing any value to any of the minimum/maximum registers
|
||||
* (0x30-0x37).
|
||||
*/
|
||||
static ssize_t reset_temp_history(struct device *dev,
|
||||
struct device_attribute *devattr, const char *buf, size_t count)
|
||||
{
|
||||
long val;
|
||||
|
||||
if (strict_strtol(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (val != 1) {
|
||||
dev_err(dev, "temp_reset_history value %ld not"
|
||||
" supported. Use 1 to reset the history!\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
i2c_smbus_write_byte_data(to_i2c_client(dev),
|
||||
TMP411_TEMP_LOWEST_MSB[0], val);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct sensor_device_attribute tmp401_attr[] = {
|
||||
SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
|
||||
SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
|
||||
SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
|
||||
SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0),
|
||||
SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst,
|
||||
store_temp_crit_hyst, 0),
|
||||
SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_LOCAL_LOW),
|
||||
SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_LOCAL_HIGH),
|
||||
SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_LOCAL_CRIT),
|
||||
SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
|
||||
SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1),
|
||||
SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
|
||||
SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1),
|
||||
SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1),
|
||||
SENSOR_ATTR(temp2_fault, 0444, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_OPEN),
|
||||
SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_LOW),
|
||||
SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_HIGH),
|
||||
SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
|
||||
TMP401_STATUS_REMOTE_CRIT),
|
||||
};
|
||||
|
||||
/*
|
||||
* Additional features of the TMP411 chip.
|
||||
* The TMP411 stores the minimum and maximum
|
||||
* temperature measured since power-on, chip-reset, or
|
||||
* minimum and maximum register reset for both the local
|
||||
* and remote channels.
|
||||
*/
|
||||
static struct sensor_device_attribute tmp411_attr[] = {
|
||||
SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0),
|
||||
SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0),
|
||||
SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1),
|
||||
SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1),
|
||||
SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0),
|
||||
};
|
||||
|
||||
/*
|
||||
* Begin non sysfs callback code (aka Real code)
|
||||
*/
|
||||
|
||||
static void tmp401_init_client(struct i2c_client *client)
|
||||
{
|
||||
int config, config_orig;
|
||||
|
||||
/* Set the conversion rate to 2 Hz */
|
||||
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
|
||||
|
||||
/* Start conversions (disable shutdown if necessary) */
|
||||
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
|
||||
if (config < 0) {
|
||||
dev_warn(&client->dev, "Initialization failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
config_orig = config;
|
||||
config &= ~TMP401_CONFIG_SHUTDOWN;
|
||||
|
||||
if (config != config_orig)
|
||||
i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
|
||||
}
|
||||
|
||||
static int tmp401_detect(struct i2c_client *client, int kind,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
/* Detect and identify the chip */
|
||||
if (kind <= 0) {
|
||||
u8 reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client,
|
||||
TMP401_MANUFACTURER_ID_REG);
|
||||
if (reg != TMP401_MANUFACTURER_ID)
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
|
||||
|
||||
switch (reg) {
|
||||
case TMP401_DEVICE_ID:
|
||||
kind = tmp401;
|
||||
break;
|
||||
case TMP411_DEVICE_ID:
|
||||
kind = tmp411;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
|
||||
if (reg & 0x1b)
|
||||
return -ENODEV;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client,
|
||||
TMP401_CONVERSION_RATE_READ);
|
||||
/* Datasheet says: 0x1-0x6 */
|
||||
if (reg > 15)
|
||||
return -ENODEV;
|
||||
}
|
||||
strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmp401_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int i, err = 0;
|
||||
struct tmp401_data *data;
|
||||
const char *names[] = { "TMP401", "TMP411" };
|
||||
|
||||
data = kzalloc(sizeof(struct tmp401_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->kind = id->driver_data;
|
||||
|
||||
/* Initialize the TMP401 chip */
|
||||
tmp401_init_client(client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++) {
|
||||
err = device_create_file(&client->dev,
|
||||
&tmp401_attr[i].dev_attr);
|
||||
if (err)
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
/* Register aditional tmp411 sysfs hooks */
|
||||
if (data->kind == tmp411) {
|
||||
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++) {
|
||||
err = device_create_file(&client->dev,
|
||||
&tmp411_attr[i].dev_attr);
|
||||
if (err)
|
||||
goto exit_remove;
|
||||
}
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
data->hwmon_dev = NULL;
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "Detected TI %s chip\n",
|
||||
names[data->kind - 1]);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
tmp401_remove(client); /* will also free data for us */
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tmp401_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tmp401_data *data = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
if (data->hwmon_dev)
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
|
||||
device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
|
||||
device_remove_file(&client->dev,
|
||||
&tmp411_attr[i].dev_attr);
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tmp401_data *tmp401_update_device_reg16(
|
||||
struct i2c_client *client, struct tmp401_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
/*
|
||||
* High byte must be read first immediately followed
|
||||
* by the low byte
|
||||
*/
|
||||
data->temp[i] = i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_MSB[i]) << 8;
|
||||
data->temp[i] |= i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_LSB[i]);
|
||||
data->temp_low[i] = i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
|
||||
data->temp_low[i] |= i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_LOW_LIMIT_LSB[i]);
|
||||
data->temp_high[i] = i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
|
||||
data->temp_high[i] |= i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_HIGH_LIMIT_LSB[i]);
|
||||
data->temp_crit[i] = i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_CRIT_LIMIT[i]);
|
||||
|
||||
if (data->kind == tmp411) {
|
||||
data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
|
||||
TMP411_TEMP_LOWEST_MSB[i]) << 8;
|
||||
data->temp_lowest[i] |= i2c_smbus_read_byte_data(
|
||||
client, TMP411_TEMP_LOWEST_LSB[i]);
|
||||
|
||||
data->temp_highest[i] = i2c_smbus_read_byte_data(
|
||||
client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
|
||||
data->temp_highest[i] |= i2c_smbus_read_byte_data(
|
||||
client, TMP411_TEMP_HIGHEST_LSB[i]);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static struct tmp401_data *tmp401_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct tmp401_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
|
||||
data->config = i2c_smbus_read_byte_data(client,
|
||||
TMP401_CONFIG_READ);
|
||||
tmp401_update_device_reg16(client, data);
|
||||
|
||||
data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
|
||||
TMP401_TEMP_CRIT_HYST);
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int __init tmp401_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tmp401_driver);
|
||||
}
|
||||
|
||||
static void __exit tmp401_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tmp401_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments TMP401 temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(tmp401_init);
|
||||
module_exit(tmp401_exit);
|
|
@ -36,6 +36,7 @@
|
|||
w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
|
||||
0x8860 0xa1
|
||||
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
|
||||
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
|
||||
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
|
||||
*/
|
||||
|
||||
|
@ -53,12 +54,13 @@
|
|||
#include <asm/io.h>
|
||||
#include "lm75.h"
|
||||
|
||||
enum kinds { w83627ehf, w83627dhg, w83667hg };
|
||||
enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg };
|
||||
|
||||
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
|
||||
static const char * w83627ehf_device_names[] = {
|
||||
"w83627ehf",
|
||||
"w83627dhg",
|
||||
"w83627dhg",
|
||||
"w83667hg",
|
||||
};
|
||||
|
||||
|
@ -86,6 +88,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
|
|||
#define SIO_W83627EHF_ID 0x8850
|
||||
#define SIO_W83627EHG_ID 0x8860
|
||||
#define SIO_W83627DHG_ID 0xa020
|
||||
#define SIO_W83627DHG_P_ID 0xb070
|
||||
#define SIO_W83667HG_ID 0xa510
|
||||
#define SIO_ID_MASK 0xFFF0
|
||||
|
||||
|
@ -1517,6 +1520,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
|
|||
static const char __initdata sio_name_W83627EHF[] = "W83627EHF";
|
||||
static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
|
||||
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
|
||||
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
|
||||
static const char __initdata sio_name_W83667HG[] = "W83667HG";
|
||||
|
||||
u16 val;
|
||||
|
@ -1542,6 +1546,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
|
|||
sio_data->kind = w83627dhg;
|
||||
sio_name = sio_name_W83627DHG;
|
||||
break;
|
||||
case SIO_W83627DHG_P_ID:
|
||||
sio_data->kind = w83627dhg_p;
|
||||
sio_name = sio_name_W83627DHG_P;
|
||||
break;
|
||||
case SIO_W83667HG_ID:
|
||||
sio_data->kind = w83667hg;
|
||||
sio_name = sio_name_W83667HG;
|
||||
|
|
Загрузка…
Ссылка в новой задаче