forked from Minki/linux
Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6
* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6: hwmon: (it87) Support for 16-bit fan reading in it8705 >= rev 0x03 hwmon: (it87) Support for 16-bit fan reading in it8712 >= rev 0x07 hwmon: (hwmon-vid) Add 6-bit vid codes for AMD NPT 0Fh cpus hwmon: (hwmon-vid) Trivial format multi-line comments per CodingStyle hwmon: ad7414 driver hwmon: (thmc50) Add support for critical temperature limits hwmon: (adm9240) Remove EXPERIMENTAL dependency hwmon: (w83627hf) Drop reset module parameter hwmon: (w83627hf) Add pwm_enable sysfs interface hwmon: (w83791d) Use fan divisor bits from vbat register hwmon: (f71882fg) Delete needless forward declarations hwmon: (dme1737) Add support for the SMSC SCH5027 hwmon: (dme1737) Skip detection if forced hwmon: (dme1737) Cleanups
This commit is contained in:
commit
a06dee41a3
@ -10,6 +10,10 @@ Supported chips:
|
||||
Prefix: 'sch311x'
|
||||
Addresses scanned: none, address read from Super-I/O config space
|
||||
Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
|
||||
* SMSC SCH5027
|
||||
Prefix: 'sch5027'
|
||||
Addresses scanned: I2C 0x2c, 0x2d, 0x2e
|
||||
Datasheet: Provided by SMSC upon request and under NDA
|
||||
|
||||
Authors:
|
||||
Juerg Haefliger <juergh@gmail.com>
|
||||
@ -27,33 +31,31 @@ Module Parameters
|
||||
following boards:
|
||||
- VIA EPIA SN18000
|
||||
|
||||
Note that there is no need to use this parameter if the driver loads without
|
||||
complaining. The driver will say so if it is necessary.
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the hardware monitoring capabilities of the
|
||||
SMSC DME1737 and Asus A8000 (which are the same) and SMSC SCH311x Super-I/O
|
||||
chips. These chips feature monitoring of 3 temp sensors temp[1-3] (2 remote
|
||||
diodes and 1 internal), 7 voltages in[0-6] (6 external and 1 internal) and up
|
||||
to 6 fan speeds fan[1-6]. Additionally, the chips implement up to 5 PWM
|
||||
outputs pwm[1-3,5-6] for controlling fan speeds both manually and
|
||||
SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, and SMSC
|
||||
SCH311x Super-I/O chips. These chips feature monitoring of 3 temp sensors
|
||||
temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
|
||||
1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
|
||||
up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
|
||||
automatically.
|
||||
|
||||
For the DME1737 and A8000, fan[1-2] and pwm[1-2] are always present. Fan[3-6]
|
||||
and pwm[3,5-6] are optional features and their availability depends on the
|
||||
configuration of the chip. The driver will detect which features are present
|
||||
during initialization and create the sysfs attributes accordingly.
|
||||
For the DME1737, A8000 and SCH5027, fan[1-2] and pwm[1-2] are always present.
|
||||
Fan[3-6] and pwm[3,5-6] are optional features and their availability depends on
|
||||
the configuration of the chip. The driver will detect which features are
|
||||
present during initialization and create the sysfs attributes accordingly.
|
||||
|
||||
For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
|
||||
pwm[5-6] don't exist.
|
||||
|
||||
The hardware monitoring features of the DME1737 and A8000 are only accessible
|
||||
via SMBus, while the SCH311x only provides access via the ISA bus. The driver
|
||||
will therefore register itself as an I2C client driver if it detects a DME1737
|
||||
or A8000 and as a platform driver if it detects a SCH311x chip.
|
||||
The hardware monitoring features of the DME1737, A8000, and SCH5027 are only
|
||||
accessible via SMBus, while the SCH311x only provides access via the ISA bus.
|
||||
The driver will therefore register itself as an I2C client driver if it detects
|
||||
a DME1737, A8000, or SCH5027 and as a platform driver if it detects a SCH311x
|
||||
chip.
|
||||
|
||||
|
||||
Voltage Monitoring
|
||||
@ -64,6 +66,7 @@ scaling resistors. The values returned by the driver therefore reflect true
|
||||
millivolts and don't need scaling. The voltage inputs are mapped as follows
|
||||
(the last column indicates the input ranges):
|
||||
|
||||
DME1737, A8000:
|
||||
in0: +5VTR (+5V standby) 0V - 6.64V
|
||||
in1: Vccp (processor core) 0V - 3V
|
||||
in2: VCC (internal +3.3V) 0V - 4.38V
|
||||
@ -72,6 +75,24 @@ millivolts and don't need scaling. The voltage inputs are mapped as follows
|
||||
in5: VTR (+3.3V standby) 0V - 4.38V
|
||||
in6: Vbat (+3.0V) 0V - 4.38V
|
||||
|
||||
SCH311x:
|
||||
in0: +2.5V 0V - 6.64V
|
||||
in1: Vccp (processor core) 0V - 2V
|
||||
in2: VCC (internal +3.3V) 0V - 4.38V
|
||||
in3: +5V 0V - 6.64V
|
||||
in4: +12V 0V - 16V
|
||||
in5: VTR (+3.3V standby) 0V - 4.38V
|
||||
in6: Vbat (+3.0V) 0V - 4.38V
|
||||
|
||||
SCH5027:
|
||||
in0: +5VTR (+5V standby) 0V - 6.64V
|
||||
in1: Vccp (processor core) 0V - 3V
|
||||
in2: VCC (internal +3.3V) 0V - 4.38V
|
||||
in3: V2_IN 0V - 1.5V
|
||||
in4: V1_IN 0V - 1.5V
|
||||
in5: VTR (+3.3V standby) 0V - 4.38V
|
||||
in6: Vbat (+3.0V) 0V - 4.38V
|
||||
|
||||
Each voltage input has associated min and max limits which trigger an alarm
|
||||
when crossed.
|
||||
|
||||
|
@ -6,12 +6,14 @@ Supported chips:
|
||||
Prefix: 'it87'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Publicly available at the ITE website
|
||||
http://www.ite.com.tw/
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8705F_V.0.4.1.pdf
|
||||
* IT8712F
|
||||
Prefix: 'it8712'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Publicly available at the ITE website
|
||||
http://www.ite.com.tw/
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8712F_V0.9.1.pdf
|
||||
http://www.ite.com.tw/product_info/file/pc/Errata%20V0.1%20for%20IT8712F%20V0.9.1.pdf
|
||||
http://www.ite.com.tw/product_info/file/pc/IT8712F_V0.9.3.pdf
|
||||
* IT8716F/IT8726F
|
||||
Prefix: 'it8716'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
@ -90,14 +92,13 @@ upper VID bits share their pins with voltage inputs (in5 and in6) so you
|
||||
can't have both on a given board.
|
||||
|
||||
The IT8716F, IT8718F and later IT8712F revisions have support for
|
||||
2 additional fans. They are supported by the driver for the IT8716F and
|
||||
IT8718F but not for the IT8712F
|
||||
2 additional fans. The additional fans are supported by the driver.
|
||||
|
||||
The IT8716F and IT8718F, and late IT8712F and IT8705F also have optional
|
||||
16-bit tachometer counters for fans 1 to 3. This is better (no more fan
|
||||
clock divider mess) but not compatible with the older chips and
|
||||
revisions. For now, the driver only uses the 16-bit mode on the
|
||||
IT8716F and IT8718F.
|
||||
revisions. The 16-bit tachometer mode is enabled by the driver when one
|
||||
of the above chips is detected.
|
||||
|
||||
The IT8726F is just bit enhanced IT8716F with additional hardware
|
||||
for AMD power sequencing. Therefore the chip will appear as IT8716F
|
||||
|
@ -40,10 +40,6 @@ Module Parameters
|
||||
(default is 1)
|
||||
Use 'init=0' to bypass initializing the chip.
|
||||
Try this if your computer crashes when you load the module.
|
||||
* reset: int
|
||||
(default is 0)
|
||||
The driver used to reset the chip on load, but does no more. Use
|
||||
'reset=1' to restore the old behavior. Report if you need to do this.
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
@ -22,6 +22,7 @@ Credits:
|
||||
|
||||
Additional contributors:
|
||||
Sven Anders <anders@anduras.de>
|
||||
Marc Hulsman <m.hulsman@tudelft.nl>
|
||||
|
||||
Module Parameters
|
||||
-----------------
|
||||
@ -67,9 +68,8 @@ on until the temperature falls 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, 8 for fan 1/2/3
|
||||
and 1, 2, 4, 8, 16, 32, 64 or 128 for fan 4/5) to give the readings more
|
||||
range or accuracy.
|
||||
readings can be divided by a programmable divider (1, 2, 4, 8, 16,
|
||||
32, 64 or 128 for all fans) to give the readings more range or accuracy.
|
||||
|
||||
Voltage sensors (also known as IN sensors) report their values in millivolts.
|
||||
An alarm is triggered if the voltage has crossed a programmable minimum
|
||||
|
@ -57,6 +57,16 @@ config SENSORS_ABITUGURU3
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abituguru3.
|
||||
|
||||
config SENSORS_AD7414
|
||||
tristate "Analog Devices AD7414"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
help
|
||||
If you say yes here you get support for the Analog Devices
|
||||
AD7414 temperature monitoring chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called ad7414.
|
||||
|
||||
config SENSORS_AD7418
|
||||
tristate "Analog Devices AD7416, AD7417 and AD7418"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
@ -124,7 +134,7 @@ config SENSORS_ADM1031
|
||||
|
||||
config SENSORS_ADM9240
|
||||
tristate "Analog Devices ADM9240 and compatibles"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
depends on I2C
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for Analog Devices ADM9240,
|
||||
@ -575,8 +585,8 @@ config SENSORS_DME1737
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for the hardware monitoring
|
||||
and fan control features of the SMSC DME1737 (and compatibles
|
||||
like the Asus A8000) and SCH311x Super-I/O chips.
|
||||
and fan control features of the SMSC DME1737, SCH311x, SCH5027, and
|
||||
Asus A8000 Super-I/O chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called dme1737.
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
|
||||
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
|
||||
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
|
||||
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
|
||||
|
268
drivers/hwmon/ad7414.c
Normal file
268
drivers/hwmon/ad7414.c
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* An hwmon driver for the Analog Devices AD7414
|
||||
*
|
||||
* Copyright 2006 Stefan Roese <sr at denx.de>, DENX Software Engineering
|
||||
*
|
||||
* Copyright (c) 2008 PIKA Technologies
|
||||
* Sean MacLennan <smaclennan@pikatech.com>
|
||||
*
|
||||
* Copyright (c) 2008 Spansion Inc.
|
||||
* Frank Edelhaeuser <frank.edelhaeuser at spansion.com>
|
||||
* (converted to "new style" I2C driver model, removed checkpatch.pl warnings)
|
||||
*
|
||||
* Based on ad7418.c
|
||||
* Copyright 2006 Tower Technologies, Alessandro Zummo <a.zummo at towertech.it>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.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>
|
||||
|
||||
|
||||
/* AD7414 registers */
|
||||
#define AD7414_REG_TEMP 0x00
|
||||
#define AD7414_REG_CONF 0x01
|
||||
#define AD7414_REG_T_HIGH 0x02
|
||||
#define AD7414_REG_T_LOW 0x03
|
||||
|
||||
static u8 AD7414_REG_LIMIT[] = { AD7414_REG_T_HIGH, AD7414_REG_T_LOW };
|
||||
|
||||
struct ad7414_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex lock; /* atomic read data updates */
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long next_update; /* In jiffies */
|
||||
s16 temp_input; /* Register values */
|
||||
s8 temps[ARRAY_SIZE(AD7414_REG_LIMIT)];
|
||||
};
|
||||
|
||||
/* REG: (0.25C/bit, two's complement) << 6 */
|
||||
static inline int ad7414_temp_from_reg(s16 reg)
|
||||
{
|
||||
/* use integer division instead of equivalent right shift to
|
||||
* guarantee arithmetic shift and preserve the sign
|
||||
*/
|
||||
return ((int)reg / 64) * 250;
|
||||
}
|
||||
|
||||
static inline int ad7414_read(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if (reg == AD7414_REG_TEMP) {
|
||||
int value = i2c_smbus_read_word_data(client, reg);
|
||||
return (value < 0) ? value : swab16(value);
|
||||
} else
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static inline int ad7414_write(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
|
||||
struct ad7414_data *ad7414_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7414_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
if (time_after(jiffies, data->next_update) || !data->valid) {
|
||||
int value, i;
|
||||
|
||||
dev_dbg(&client->dev, "starting ad7414 update\n");
|
||||
|
||||
value = ad7414_read(client, AD7414_REG_TEMP);
|
||||
if (value < 0)
|
||||
dev_dbg(&client->dev, "AD7414_REG_TEMP err %d\n",
|
||||
value);
|
||||
else
|
||||
data->temp_input = value;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(AD7414_REG_LIMIT); ++i) {
|
||||
value = ad7414_read(client, AD7414_REG_LIMIT[i]);
|
||||
if (value < 0)
|
||||
dev_dbg(&client->dev, "AD7414 reg %d err %d\n",
|
||||
AD7414_REG_LIMIT[i], value);
|
||||
else
|
||||
data->temps[i] = value;
|
||||
}
|
||||
|
||||
data->next_update = jiffies + HZ + HZ / 2;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_input(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7414_data *data = ad7414_update_device(dev);
|
||||
return sprintf(buf, "%d\n", ad7414_temp_from_reg(data->temp_input));
|
||||
}
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
|
||||
|
||||
static ssize_t show_max_min(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
struct ad7414_data *data = ad7414_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->temps[index] * 1000);
|
||||
}
|
||||
|
||||
static ssize_t set_max_min(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7414_data *data = i2c_get_clientdata(client);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
u8 reg = AD7414_REG_LIMIT[index];
|
||||
long temp = simple_strtol(buf, NULL, 10);
|
||||
|
||||
temp = SENSORS_LIMIT(temp, -40000, 85000);
|
||||
temp = (temp + (temp < 0 ? -500 : 500)) / 1000;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
data->temps[index] = temp;
|
||||
ad7414_write(client, reg, temp);
|
||||
mutex_unlock(&data->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
|
||||
show_max_min, set_max_min, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
|
||||
show_max_min, set_max_min, 1);
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int bitnr = to_sensor_dev_attr(attr)->index;
|
||||
struct ad7414_data *data = ad7414_update_device(dev);
|
||||
int value = (data->temp_input >> bitnr) & 1;
|
||||
return sprintf(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 4);
|
||||
|
||||
static struct attribute *ad7414_attributes[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7414_group = {
|
||||
.attrs = ad7414_attributes,
|
||||
};
|
||||
|
||||
static int ad7414_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
struct ad7414_data *data;
|
||||
int conf;
|
||||
int err = 0;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
goto exit;
|
||||
|
||||
data = kzalloc(sizeof(struct ad7414_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
dev_info(&client->dev, "chip found\n");
|
||||
|
||||
/* Make sure the chip is powered up. */
|
||||
conf = i2c_smbus_read_byte_data(client, AD7414_REG_CONF);
|
||||
if (conf < 0)
|
||||
dev_warn(&client->dev,
|
||||
"ad7414_probe unable to read config register.\n");
|
||||
else {
|
||||
conf &= ~(1 << 7);
|
||||
i2c_smbus_write_byte_data(client, AD7414_REG_CONF, conf);
|
||||
}
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = sysfs_create_group(&client->dev.kobj, &ad7414_group);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_group(&client->dev.kobj, &ad7414_group);
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ad7414_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ad7414_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &ad7414_group);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7414_id[] = {
|
||||
{ "ad7414", 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver ad7414_driver = {
|
||||
.driver = {
|
||||
.name = "ad7414",
|
||||
},
|
||||
.probe = ad7414_probe,
|
||||
.remove = __devexit_p(ad7414_remove),
|
||||
.id_table = ad7414_id,
|
||||
};
|
||||
|
||||
static int __init ad7414_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ad7414_driver);
|
||||
}
|
||||
module_init(ad7414_init);
|
||||
|
||||
static void __exit ad7414_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ad7414_driver);
|
||||
}
|
||||
module_exit(ad7414_exit);
|
||||
|
||||
MODULE_AUTHOR("Stefan Roese <sr at denx.de>, "
|
||||
"Frank Edelhaeuser <frank.edelhaeuser at spansion.com>");
|
||||
|
||||
MODULE_DESCRIPTION("AD7414 driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,11 +1,11 @@
|
||||
/*
|
||||
* dme1737.c - Driver for the SMSC DME1737, Asus A8000, and SMSC SCH311x
|
||||
* Super-I/O chips integrated hardware monitoring features.
|
||||
* Copyright (c) 2007 Juerg Haefliger <juergh@gmail.com>
|
||||
* dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and
|
||||
* SCH5027 Super-I/O chips integrated hardware monitoring features.
|
||||
* Copyright (c) 2007, 2008 Juerg Haefliger <juergh@gmail.com>
|
||||
*
|
||||
* This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
|
||||
* the chip registers if a DME1737 (or A8000) is found and the ISA bus if a
|
||||
* SCH311x chip is found. Both types of chips have very similar hardware
|
||||
* the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
|
||||
* if a SCH311x chip is found. Both types of chips have very similar hardware
|
||||
* monitoring capabilities but differ in the way they can be accessed.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@ -57,7 +57,10 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
|
||||
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
|
||||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(dme1737);
|
||||
I2C_CLIENT_INSMOD_2(dme1737, sch5027);
|
||||
|
||||
/* ISA chip types */
|
||||
enum isa_chips { sch311x = sch5027 + 1 };
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Registers
|
||||
@ -163,6 +166,7 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
|
||||
#define DME1737_VERSTEP 0x88
|
||||
#define DME1737_VERSTEP_MASK 0xf8
|
||||
#define SCH311X_DEVICE 0x8c
|
||||
#define SCH5027_VERSTEP 0x69
|
||||
|
||||
/* Length of ISA address segment */
|
||||
#define DME1737_EXTENT 2
|
||||
@ -182,6 +186,7 @@ struct dme1737_data {
|
||||
unsigned long last_update; /* in jiffies */
|
||||
unsigned long last_vbat; /* in jiffies */
|
||||
enum chips type;
|
||||
const int *in_nominal; /* pointer to IN_NOMINAL array */
|
||||
|
||||
u8 vid;
|
||||
u8 pwm_rr_en;
|
||||
@ -220,23 +225,23 @@ static const int IN_NOMINAL_DME1737[] = {5000, 2250, 3300, 5000, 12000, 3300,
|
||||
3300};
|
||||
static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
|
||||
3300};
|
||||
#define IN_NOMINAL(ix, type) (((type) == dme1737) ? \
|
||||
IN_NOMINAL_DME1737[(ix)] : \
|
||||
IN_NOMINAL_SCH311x[(ix)])
|
||||
static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
|
||||
3300};
|
||||
#define IN_NOMINAL(type) ((type) == sch311x ? IN_NOMINAL_SCH311x : \
|
||||
(type) == sch5027 ? IN_NOMINAL_SCH5027 : \
|
||||
IN_NOMINAL_DME1737)
|
||||
|
||||
/* Voltage input
|
||||
* Voltage inputs have 16 bits resolution, limit values have 8 bits
|
||||
* resolution. */
|
||||
static inline int IN_FROM_REG(int reg, int ix, int res, int type)
|
||||
static inline int IN_FROM_REG(int reg, int nominal, int res)
|
||||
{
|
||||
return (reg * IN_NOMINAL(ix, type) + (3 << (res - 3))) /
|
||||
(3 << (res - 2));
|
||||
return (reg * nominal + (3 << (res - 3))) / (3 << (res - 2));
|
||||
}
|
||||
|
||||
static inline int IN_TO_REG(int val, int ix, int type)
|
||||
static inline int IN_TO_REG(int val, int nominal)
|
||||
{
|
||||
return SENSORS_LIMIT((val * 192 + IN_NOMINAL(ix, type) / 2) /
|
||||
IN_NOMINAL(ix, type), 0, 255);
|
||||
return SENSORS_LIMIT((val * 192 + nominal / 2) / nominal, 0, 255);
|
||||
}
|
||||
|
||||
/* Temperature input
|
||||
@ -565,7 +570,10 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
|
||||
|
||||
/* Sample register contents every 1 sec */
|
||||
if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
|
||||
data->vid = dme1737_read(client, DME1737_REG_VID) & 0x3f;
|
||||
if (data->type != sch5027) {
|
||||
data->vid = dme1737_read(client, DME1737_REG_VID) &
|
||||
0x3f;
|
||||
}
|
||||
|
||||
/* In (voltage) registers */
|
||||
for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
|
||||
@ -593,8 +601,10 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
|
||||
DME1737_REG_TEMP_MIN(ix));
|
||||
data->temp_max[ix] = dme1737_read(client,
|
||||
DME1737_REG_TEMP_MAX(ix));
|
||||
data->temp_offset[ix] = dme1737_read(client,
|
||||
DME1737_REG_TEMP_OFFSET(ix));
|
||||
if (data->type != sch5027) {
|
||||
data->temp_offset[ix] = dme1737_read(client,
|
||||
DME1737_REG_TEMP_OFFSET(ix));
|
||||
}
|
||||
}
|
||||
|
||||
/* In and temp LSB registers
|
||||
@ -669,9 +679,11 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
|
||||
data->zone_abs[ix] = dme1737_read(client,
|
||||
DME1737_REG_ZONE_ABS(ix));
|
||||
}
|
||||
for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
|
||||
data->zone_hyst[ix] = dme1737_read(client,
|
||||
if (data->type != sch5027) {
|
||||
for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
|
||||
data->zone_hyst[ix] = dme1737_read(client,
|
||||
DME1737_REG_ZONE_HYST(ix));
|
||||
}
|
||||
}
|
||||
|
||||
/* Alarm registers */
|
||||
@ -735,13 +747,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
switch (fn) {
|
||||
case SYS_IN_INPUT:
|
||||
res = IN_FROM_REG(data->in[ix], ix, 16, data->type);
|
||||
res = IN_FROM_REG(data->in[ix], data->in_nominal[ix], 16);
|
||||
break;
|
||||
case SYS_IN_MIN:
|
||||
res = IN_FROM_REG(data->in_min[ix], ix, 8, data->type);
|
||||
res = IN_FROM_REG(data->in_min[ix], data->in_nominal[ix], 8);
|
||||
break;
|
||||
case SYS_IN_MAX:
|
||||
res = IN_FROM_REG(data->in_max[ix], ix, 8, data->type);
|
||||
res = IN_FROM_REG(data->in_max[ix], data->in_nominal[ix], 8);
|
||||
break;
|
||||
case SYS_IN_ALARM:
|
||||
res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
|
||||
@ -768,12 +780,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
|
||||
mutex_lock(&data->update_lock);
|
||||
switch (fn) {
|
||||
case SYS_IN_MIN:
|
||||
data->in_min[ix] = IN_TO_REG(val, ix, data->type);
|
||||
data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]);
|
||||
dme1737_write(client, DME1737_REG_IN_MIN(ix),
|
||||
data->in_min[ix]);
|
||||
break;
|
||||
case SYS_IN_MAX:
|
||||
data->in_max[ix] = IN_TO_REG(val, ix, data->type);
|
||||
data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]);
|
||||
dme1737_write(client, DME1737_REG_IN_MAX(ix),
|
||||
data->in_max[ix]);
|
||||
break;
|
||||
@ -1166,7 +1178,7 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
|
||||
return sprintf(buf, "%d\n", res);
|
||||
}
|
||||
|
||||
static struct attribute *dme1737_attr_pwm[];
|
||||
static struct attribute *dme1737_pwm_chmod_attr[];
|
||||
static void dme1737_chmod_file(struct device*, struct attribute*, mode_t);
|
||||
|
||||
static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
@ -1230,7 +1242,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
switch (val) {
|
||||
case 0:
|
||||
/* Change permissions of pwm[ix] to read-only */
|
||||
dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
|
||||
dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
|
||||
S_IRUGO);
|
||||
/* Turn fan fully on */
|
||||
data->pwm_config[ix] = PWM_EN_TO_REG(0,
|
||||
@ -1245,12 +1257,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
|
||||
data->pwm_config[ix]);
|
||||
/* Change permissions of pwm[ix] to read-writeable */
|
||||
dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
|
||||
dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
|
||||
S_IRUGO | S_IWUSR);
|
||||
break;
|
||||
case 2:
|
||||
/* Change permissions of pwm[ix] to read-only */
|
||||
dme1737_chmod_file(dev, dme1737_attr_pwm[ix],
|
||||
dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
|
||||
S_IRUGO);
|
||||
/* Turn on auto mode using the saved zone channel
|
||||
* assignment */
|
||||
@ -1570,38 +1582,29 @@ static struct attribute *dme1737_attr[] ={
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr.attr,
|
||||
/* Zones */
|
||||
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
|
||||
/* Misc */
|
||||
&dev_attr_vrm.attr,
|
||||
&dev_attr_cpu0_vid.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1609,49 +1612,68 @@ static const struct attribute_group dme1737_group = {
|
||||
.attrs = dme1737_attr,
|
||||
};
|
||||
|
||||
/* The following struct holds misc attributes, which are not available in all
|
||||
* chips. Their creation depends on the chip type which is determined during
|
||||
* module load. */
|
||||
static struct attribute *dme1737_misc_attr[] = {
|
||||
/* Temperatures */
|
||||
&sensor_dev_attr_temp1_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr.attr,
|
||||
/* Zones */
|
||||
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
|
||||
/* Misc */
|
||||
&dev_attr_vrm.attr,
|
||||
&dev_attr_cpu0_vid.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group dme1737_misc_group = {
|
||||
.attrs = dme1737_misc_attr,
|
||||
};
|
||||
|
||||
/* The following structs hold the PWM attributes, some of which are optional.
|
||||
* Their creation depends on the chip configuration which is determined during
|
||||
* module load. */
|
||||
static struct attribute *dme1737_attr_pwm1[] = {
|
||||
static struct attribute *dme1737_pwm1_attr[] = {
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm2[] = {
|
||||
static struct attribute *dme1737_pwm2_attr[] = {
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm3[] = {
|
||||
static struct attribute *dme1737_pwm3_attr[] = {
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm5[] = {
|
||||
static struct attribute *dme1737_pwm5_attr[] = {
|
||||
&sensor_dev_attr_pwm5.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5_enable.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm6[] = {
|
||||
static struct attribute *dme1737_pwm6_attr[] = {
|
||||
&sensor_dev_attr_pwm6.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm6_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm6_enable.dev_attr.attr,
|
||||
@ -1659,53 +1681,62 @@ static struct attribute *dme1737_attr_pwm6[] = {
|
||||
};
|
||||
|
||||
static const struct attribute_group dme1737_pwm_group[] = {
|
||||
{ .attrs = dme1737_attr_pwm1 },
|
||||
{ .attrs = dme1737_attr_pwm2 },
|
||||
{ .attrs = dme1737_attr_pwm3 },
|
||||
{ .attrs = dme1737_pwm1_attr },
|
||||
{ .attrs = dme1737_pwm2_attr },
|
||||
{ .attrs = dme1737_pwm3_attr },
|
||||
{ .attrs = NULL },
|
||||
{ .attrs = dme1737_attr_pwm5 },
|
||||
{ .attrs = dme1737_attr_pwm6 },
|
||||
{ .attrs = dme1737_pwm5_attr },
|
||||
{ .attrs = dme1737_pwm6_attr },
|
||||
};
|
||||
|
||||
/* The following struct holds misc PWM attributes, which are not available in
|
||||
* all chips. Their creation depends on the chip type which is determined
|
||||
* during module load. */
|
||||
static struct attribute *dme1737_pwm_misc_attr[] = {
|
||||
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
|
||||
};
|
||||
|
||||
/* The following structs hold the fan attributes, some of which are optional.
|
||||
* Their creation depends on the chip configuration which is determined during
|
||||
* module load. */
|
||||
static struct attribute *dme1737_attr_fan1[] = {
|
||||
static struct attribute *dme1737_fan1_attr[] = {
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_type.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_fan2[] = {
|
||||
static struct attribute *dme1737_fan2_attr[] = {
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_type.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_fan3[] = {
|
||||
static struct attribute *dme1737_fan3_attr[] = {
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_type.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_fan4[] = {
|
||||
static struct attribute *dme1737_fan4_attr[] = {
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_type.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_fan5[] = {
|
||||
static struct attribute *dme1737_fan5_attr[] = {
|
||||
&sensor_dev_attr_fan5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_fan5_max.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_fan6[] = {
|
||||
static struct attribute *dme1737_fan6_attr[] = {
|
||||
&sensor_dev_attr_fan6_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan6_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan6_alarm.dev_attr.attr,
|
||||
@ -1714,94 +1745,83 @@ static struct attribute *dme1737_attr_fan6[] = {
|
||||
};
|
||||
|
||||
static const struct attribute_group dme1737_fan_group[] = {
|
||||
{ .attrs = dme1737_attr_fan1 },
|
||||
{ .attrs = dme1737_attr_fan2 },
|
||||
{ .attrs = dme1737_attr_fan3 },
|
||||
{ .attrs = dme1737_attr_fan4 },
|
||||
{ .attrs = dme1737_attr_fan5 },
|
||||
{ .attrs = dme1737_attr_fan6 },
|
||||
{ .attrs = dme1737_fan1_attr },
|
||||
{ .attrs = dme1737_fan2_attr },
|
||||
{ .attrs = dme1737_fan3_attr },
|
||||
{ .attrs = dme1737_fan4_attr },
|
||||
{ .attrs = dme1737_fan5_attr },
|
||||
{ .attrs = dme1737_fan6_attr },
|
||||
};
|
||||
|
||||
/* The permissions of all of the following attributes are changed to read-
|
||||
/* The permissions of the following zone attributes are changed to read-
|
||||
* writeable if the chip is *not* locked. Otherwise they stay read-only. */
|
||||
static struct attribute *dme1737_attr_lock[] = {
|
||||
/* Temperatures */
|
||||
&sensor_dev_attr_temp1_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_offset.dev_attr.attr,
|
||||
/* Zones */
|
||||
&sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
|
||||
static struct attribute *dme1737_zone_chmod_attr[] = {
|
||||
&sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group dme1737_lock_group = {
|
||||
.attrs = dme1737_attr_lock,
|
||||
static const struct attribute_group dme1737_zone_chmod_group = {
|
||||
.attrs = dme1737_zone_chmod_attr,
|
||||
};
|
||||
|
||||
/* The permissions of the following PWM attributes are changed to read-
|
||||
* writeable if the chip is *not* locked and the respective PWM is available.
|
||||
* Otherwise they stay read-only. */
|
||||
static struct attribute *dme1737_attr_pwm1_lock[] = {
|
||||
static struct attribute *dme1737_pwm1_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm1_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm2_lock[] = {
|
||||
static struct attribute *dme1737_pwm2_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm2_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm3_lock[] = {
|
||||
static struct attribute *dme1737_pwm3_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm5_lock[] = {
|
||||
static struct attribute *dme1737_pwm5_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm5.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm5_freq.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute *dme1737_attr_pwm6_lock[] = {
|
||||
static struct attribute *dme1737_pwm6_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm6.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm6_freq.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group dme1737_pwm_lock_group[] = {
|
||||
{ .attrs = dme1737_attr_pwm1_lock },
|
||||
{ .attrs = dme1737_attr_pwm2_lock },
|
||||
{ .attrs = dme1737_attr_pwm3_lock },
|
||||
static const struct attribute_group dme1737_pwm_chmod_group[] = {
|
||||
{ .attrs = dme1737_pwm1_chmod_attr },
|
||||
{ .attrs = dme1737_pwm2_chmod_attr },
|
||||
{ .attrs = dme1737_pwm3_chmod_attr },
|
||||
{ .attrs = NULL },
|
||||
{ .attrs = dme1737_attr_pwm5_lock },
|
||||
{ .attrs = dme1737_attr_pwm6_lock },
|
||||
{ .attrs = dme1737_pwm5_chmod_attr },
|
||||
{ .attrs = dme1737_pwm6_chmod_attr },
|
||||
};
|
||||
|
||||
/* Pwm[1-3] are read-writeable if the associated pwm is in manual mode and the
|
||||
* chip is not locked. Otherwise they are read-only. */
|
||||
static struct attribute *dme1737_attr_pwm[] = {
|
||||
static struct attribute *dme1737_pwm_chmod_attr[] = {
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
@ -1875,9 +1895,17 @@ static void dme1737_remove_files(struct device *dev)
|
||||
if (data->has_pwm & (1 << ix)) {
|
||||
sysfs_remove_group(&dev->kobj,
|
||||
&dme1737_pwm_group[ix]);
|
||||
if (data->type != sch5027 && ix < 3) {
|
||||
sysfs_remove_file(&dev->kobj,
|
||||
dme1737_pwm_misc_attr[ix]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->type != sch5027) {
|
||||
sysfs_remove_group(&dev->kobj, &dme1737_misc_group);
|
||||
}
|
||||
|
||||
sysfs_remove_group(&dev->kobj, &dme1737_group);
|
||||
|
||||
if (!data->client.driver) {
|
||||
@ -1901,6 +1929,13 @@ static int dme1737_create_files(struct device *dev)
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
/* Create misc sysfs attributes */
|
||||
if ((data->type != sch5027) &&
|
||||
(err = sysfs_create_group(&dev->kobj,
|
||||
&dme1737_misc_group))) {
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
/* Create fan sysfs attributes */
|
||||
for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
|
||||
if (data->has_fan & (1 << ix)) {
|
||||
@ -1918,6 +1953,11 @@ static int dme1737_create_files(struct device *dev)
|
||||
&dme1737_pwm_group[ix]))) {
|
||||
goto exit_remove;
|
||||
}
|
||||
if (data->type != sch5027 && ix < 3 &&
|
||||
(err = sysfs_create_file(&dev->kobj,
|
||||
dme1737_pwm_misc_attr[ix]))) {
|
||||
goto exit_remove;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1927,16 +1967,27 @@ static int dme1737_create_files(struct device *dev)
|
||||
dev_info(dev, "Device is locked. Some attributes "
|
||||
"will be read-only.\n");
|
||||
} else {
|
||||
/* Change permissions of standard attributes */
|
||||
dme1737_chmod_group(dev, &dme1737_lock_group,
|
||||
/* Change permissions of zone sysfs attributes */
|
||||
dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
|
||||
S_IRUGO | S_IWUSR);
|
||||
|
||||
/* Change permissions of PWM attributes */
|
||||
for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_lock_group); ix++) {
|
||||
/* Change permissions of misc sysfs attributes */
|
||||
if (data->type != sch5027) {
|
||||
dme1737_chmod_group(dev, &dme1737_misc_group,
|
||||
S_IRUGO | S_IWUSR);
|
||||
}
|
||||
|
||||
/* Change permissions of PWM sysfs attributes */
|
||||
for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
|
||||
if (data->has_pwm & (1 << ix)) {
|
||||
dme1737_chmod_group(dev,
|
||||
&dme1737_pwm_lock_group[ix],
|
||||
&dme1737_pwm_chmod_group[ix],
|
||||
S_IRUGO | S_IWUSR);
|
||||
if (data->type != sch5027 && ix < 3) {
|
||||
dme1737_chmod_file(dev,
|
||||
dme1737_pwm_misc_attr[ix],
|
||||
S_IRUGO | S_IWUSR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1945,7 +1996,7 @@ static int dme1737_create_files(struct device *dev)
|
||||
if ((data->has_pwm & (1 << ix)) &&
|
||||
(PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
|
||||
dme1737_chmod_file(dev,
|
||||
dme1737_attr_pwm[ix],
|
||||
dme1737_pwm_chmod_attr[ix],
|
||||
S_IRUGO | S_IWUSR);
|
||||
}
|
||||
}
|
||||
@ -1966,6 +2017,9 @@ static int dme1737_init_device(struct device *dev)
|
||||
int ix;
|
||||
u8 reg;
|
||||
|
||||
/* Point to the right nominal voltages array */
|
||||
data->in_nominal = IN_NOMINAL(data->type);
|
||||
|
||||
data->config = dme1737_read(client, DME1737_REG_CONFIG);
|
||||
/* Inform if part is not monitoring/started */
|
||||
if (!(data->config & 0x01)) {
|
||||
@ -2076,7 +2130,9 @@ static int dme1737_init_device(struct device *dev)
|
||||
data->pwm_acz[2] = 4; /* pwm3 -> zone3 */
|
||||
|
||||
/* Set VRM */
|
||||
data->vrm = vid_which_vrm();
|
||||
if (data->type != sch5027) {
|
||||
data->vrm = vid_which_vrm();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2095,9 +2151,10 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
|
||||
dme1737_sio_enter(sio_cip);
|
||||
|
||||
/* Check device ID
|
||||
* The DME1737 can return either 0x78 or 0x77 as its device ID. */
|
||||
* The DME1737 can return either 0x78 or 0x77 as its device ID.
|
||||
* The SCH5027 returns 0x89 as its device ID. */
|
||||
reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
|
||||
if (!(reg == 0x77 || reg == 0x78)) {
|
||||
if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) {
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
@ -2166,15 +2223,24 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
|
||||
company = dme1737_read(client, DME1737_REG_COMPANY);
|
||||
verstep = dme1737_read(client, DME1737_REG_VERSTEP);
|
||||
|
||||
if (!((company == DME1737_COMPANY_SMSC) &&
|
||||
((verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP))) {
|
||||
if (company == DME1737_COMPANY_SMSC &&
|
||||
(verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
|
||||
kind = dme1737;
|
||||
} else if (company == DME1737_COMPANY_SMSC &&
|
||||
verstep == SCH5027_VERSTEP) {
|
||||
kind = sch5027;
|
||||
} else {
|
||||
err = -ENODEV;
|
||||
goto exit_kfree;
|
||||
}
|
||||
}
|
||||
|
||||
kind = dme1737;
|
||||
name = "dme1737";
|
||||
if (kind == sch5027) {
|
||||
name = "sch5027";
|
||||
} else {
|
||||
kind = dme1737;
|
||||
name = "dme1737";
|
||||
}
|
||||
data->type = kind;
|
||||
|
||||
/* Fill in the remaining client fields and put it into the global
|
||||
@ -2187,8 +2253,9 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
|
||||
goto exit_kfree;
|
||||
}
|
||||
|
||||
dev_info(dev, "Found a DME1737 chip at 0x%02x (rev 0x%02x).\n",
|
||||
client->addr, verstep);
|
||||
dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
|
||||
kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
|
||||
verstep);
|
||||
|
||||
/* Initialize the DME1737 chip */
|
||||
if ((err = dme1737_init_device(dev))) {
|
||||
@ -2360,15 +2427,18 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
|
||||
client->addr = res->start;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
company = dme1737_read(client, DME1737_REG_COMPANY);
|
||||
device = dme1737_read(client, DME1737_REG_DEVICE);
|
||||
/* Skip chip detection if module is loaded with force_id parameter */
|
||||
if (!force_id) {
|
||||
company = dme1737_read(client, DME1737_REG_COMPANY);
|
||||
device = dme1737_read(client, DME1737_REG_DEVICE);
|
||||
|
||||
if (!((company == DME1737_COMPANY_SMSC) &&
|
||||
(device == SCH311X_DEVICE))) {
|
||||
err = -ENODEV;
|
||||
goto exit_kfree;
|
||||
if (!((company == DME1737_COMPANY_SMSC) &&
|
||||
(device == SCH311X_DEVICE))) {
|
||||
err = -ENODEV;
|
||||
goto exit_kfree;
|
||||
}
|
||||
}
|
||||
data->type = -1;
|
||||
data->type = sch311x;
|
||||
|
||||
/* Fill in the remaining client fields and initialize the mutex */
|
||||
strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
|
||||
|
@ -87,8 +87,6 @@ static inline void superio_enter(int base);
|
||||
static inline void superio_select(int base, int ld);
|
||||
static inline void superio_exit(int base);
|
||||
|
||||
static inline u16 fan_from_reg ( u16 reg );
|
||||
|
||||
struct f71882fg_data {
|
||||
unsigned short addr;
|
||||
struct device *hwmon_dev;
|
||||
@ -116,10 +114,6 @@ struct f71882fg_data {
|
||||
u8 temp_diode_open;
|
||||
};
|
||||
|
||||
static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg);
|
||||
static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg);
|
||||
static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val);
|
||||
|
||||
/* Sysfs in*/
|
||||
static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf);
|
||||
|
@ -1,76 +1,74 @@
|
||||
/*
|
||||
hwmon-vid.c - VID/VRM/VRD voltage conversions
|
||||
|
||||
Copyright (c) 2004 Rudolf Marek <r.marek@assembler.cz>
|
||||
|
||||
Partly imported from i2c-vid.h of the lm_sensors project
|
||||
Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
With assistance from Trent Piepho <xyzzy@speakeasy.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
* hwmon-vid.c - VID/VRM/VRD voltage conversions
|
||||
*
|
||||
* Copyright (c) 2004 Rudolf Marek <r.marek@assembler.cz>
|
||||
*
|
||||
* Partly imported from i2c-vid.h of the lm_sensors project
|
||||
* Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
* With assistance from Trent Piepho <xyzzy@speakeasy.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/hwmon-vid.h>
|
||||
|
||||
/*
|
||||
Common code for decoding VID pins.
|
||||
* Common code for decoding VID pins.
|
||||
*
|
||||
* References:
|
||||
*
|
||||
* For VRM 8.4 to 9.1, "VRM x.y DC-DC Converter Design Guidelines",
|
||||
* available at http://developer.intel.com/.
|
||||
*
|
||||
* For VRD 10.0 and up, "VRD x.y Design Guide",
|
||||
* available at http://developer.intel.com/.
|
||||
*
|
||||
* AMD NPT 0Fh (Athlon64 & Opteron), AMD Publication 32559,
|
||||
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
|
||||
* Table 71. VID Code Voltages
|
||||
* AMD Opteron processors don't follow the Intel specifications.
|
||||
* I'm going to "make up" 2.4 as the spec number for the Opterons.
|
||||
* No good reason just a mnemonic for the 24x Opteron processor
|
||||
* series.
|
||||
*
|
||||
* The 17 specification is in fact Intel Mobile Voltage Positioning -
|
||||
* (IMVP-II). You can find more information in the datasheet of Max1718
|
||||
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2452
|
||||
*
|
||||
* The 13 specification corresponds to the Intel Pentium M series. There
|
||||
* doesn't seem to be any named specification for these. The conversion
|
||||
* tables are detailed directly in the various Pentium M datasheets:
|
||||
* http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm
|
||||
*
|
||||
* The 14 specification corresponds to Intel Core series. There
|
||||
* doesn't seem to be any named specification for these. The conversion
|
||||
* tables are detailed directly in the various Pentium Core datasheets:
|
||||
* http://www.intel.com/design/mobile/datashts/309221.htm
|
||||
*
|
||||
* The 110 (VRM 11) specification corresponds to Intel Conroe based series.
|
||||
* http://www.intel.com/design/processor/applnots/313214.htm
|
||||
*/
|
||||
|
||||
References:
|
||||
|
||||
For VRM 8.4 to 9.1, "VRM x.y DC-DC Converter Design Guidelines",
|
||||
available at http://developer.intel.com/.
|
||||
|
||||
For VRD 10.0 and up, "VRD x.y Design Guide",
|
||||
available at http://developer.intel.com/.
|
||||
|
||||
AMD Opteron processors don't follow the Intel specifications.
|
||||
I'm going to "make up" 2.4 as the spec number for the Opterons.
|
||||
No good reason just a mnemonic for the 24x Opteron processor
|
||||
series.
|
||||
|
||||
Opteron VID encoding is:
|
||||
00000 = 1.550 V
|
||||
00001 = 1.525 V
|
||||
. . . .
|
||||
11110 = 0.800 V
|
||||
11111 = 0.000 V (off)
|
||||
|
||||
The 17 specification is in fact Intel Mobile Voltage Positioning -
|
||||
(IMVP-II). You can find more information in the datasheet of Max1718
|
||||
http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2452
|
||||
|
||||
The 13 specification corresponds to the Intel Pentium M series. There
|
||||
doesn't seem to be any named specification for these. The conversion
|
||||
tables are detailed directly in the various Pentium M datasheets:
|
||||
http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm
|
||||
|
||||
The 14 specification corresponds to Intel Core series. There
|
||||
doesn't seem to be any named specification for these. The conversion
|
||||
tables are detailed directly in the various Pentium Core datasheets:
|
||||
http://www.intel.com/design/mobile/datashts/309221.htm
|
||||
|
||||
The 110 (VRM 11) specification corresponds to Intel Conroe based series.
|
||||
http://www.intel.com/design/processor/applnots/313214.htm
|
||||
*/
|
||||
|
||||
/* vrm is the VRM/VRD document version multiplied by 10.
|
||||
val is the 4-bit or more VID code.
|
||||
Returned value is in mV to avoid floating point in the kernel.
|
||||
Some VID have some bits in uV scale, this is rounded to mV */
|
||||
/*
|
||||
* vrm is the VRM/VRD document version multiplied by 10.
|
||||
* val is the 4-bit or more VID code.
|
||||
* Returned value is in mV to avoid floating point in the kernel.
|
||||
* Some VID have some bits in uV scale, this is rounded to mV.
|
||||
*/
|
||||
int vid_from_reg(int val, u8 vrm)
|
||||
{
|
||||
int vid;
|
||||
@ -96,9 +94,11 @@ int vid_from_reg(int val, u8 vrm)
|
||||
if (val < 0x02 || val > 0xb2)
|
||||
return 0;
|
||||
return((1600000 - (val - 2) * 6250 + 500) / 1000);
|
||||
case 24: /* Opteron processor */
|
||||
val &= 0x1f;
|
||||
return(val == 0x1f ? 0 : 1550 - val * 25);
|
||||
|
||||
case 24: /* AMD NPT 0Fh (Athlon64 & Opteron) */
|
||||
val &= 0x3f;
|
||||
return (val < 32) ? 1550 - 25 * val
|
||||
: 775 - (25 * (val - 31)) / 2;
|
||||
|
||||
case 91: /* VRM 9.1 */
|
||||
case 90: /* VRM 9.0 */
|
||||
@ -141,9 +141,9 @@ int vid_from_reg(int val, u8 vrm)
|
||||
|
||||
|
||||
/*
|
||||
After this point is the code to automatically determine which
|
||||
VRM/VRD specification should be used depending on the CPU.
|
||||
*/
|
||||
* After this point is the code to automatically determine which
|
||||
* VRM/VRD specification should be used depending on the CPU.
|
||||
*/
|
||||
|
||||
struct vrm_model {
|
||||
u8 vendor;
|
||||
|
@ -151,9 +151,9 @@ static int fix_pwm_polarity;
|
||||
/* The IT8718F has the VID value in a different register, in Super-I/O
|
||||
configuration space. */
|
||||
#define IT87_REG_VID 0x0a
|
||||
/* Warning: register 0x0b is used for something completely different in
|
||||
new chips/revisions. I suspect only 16-bit tachometer mode will work
|
||||
for these. */
|
||||
/* The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b
|
||||
for fan divisors. Later IT8712F revisions must use 16-bit tachometer
|
||||
mode. */
|
||||
#define IT87_REG_FAN_DIV 0x0b
|
||||
#define IT87_REG_FAN_16BIT 0x0c
|
||||
|
||||
@ -234,6 +234,7 @@ static const unsigned int pwm_freq[8] = {
|
||||
struct it87_sio_data {
|
||||
enum chips type;
|
||||
/* Values read from Super-I/O config space */
|
||||
u8 revision;
|
||||
u8 vid_value;
|
||||
};
|
||||
|
||||
@ -242,6 +243,7 @@ struct it87_sio_data {
|
||||
struct it87_data {
|
||||
struct device *hwmon_dev;
|
||||
enum chips type;
|
||||
u8 revision;
|
||||
|
||||
unsigned short addr;
|
||||
const char *name;
|
||||
@ -268,6 +270,16 @@ struct it87_data {
|
||||
u8 manual_pwm_ctl[3]; /* manual PWM value set by user */
|
||||
};
|
||||
|
||||
static inline int has_16bit_fans(const struct it87_data *data)
|
||||
{
|
||||
/* IT8705F Datasheet 0.4.1, 3h == Version G.
|
||||
IT8712F Datasheet 0.9.1, section 8.3.5 indicates 7h == Version I.
|
||||
These are the first revisions with 16bit tachometer support. */
|
||||
return (data->type == it87 && data->revision >= 0x03)
|
||||
|| (data->type == it8712 && data->revision >= 0x07)
|
||||
|| data->type == it8716
|
||||
|| data->type == it8718;
|
||||
}
|
||||
|
||||
static int it87_probe(struct platform_device *pdev);
|
||||
static int __devexit it87_remove(struct platform_device *pdev);
|
||||
@ -991,8 +1003,9 @@ static int __init it87_find(unsigned short *address,
|
||||
}
|
||||
|
||||
err = 0;
|
||||
sio_data->revision = superio_inb(DEVREV) & 0x0f;
|
||||
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
|
||||
chip_type, *address, superio_inb(DEVREV) & 0x0f);
|
||||
chip_type, *address, sio_data->revision);
|
||||
|
||||
/* Read GPIO config and VID value from LDN 7 (GPIO) */
|
||||
if (chip_type != IT8705F_DEVID) {
|
||||
@ -1045,6 +1058,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
||||
|
||||
data->addr = res->start;
|
||||
data->type = sio_data->type;
|
||||
data->revision = sio_data->revision;
|
||||
data->name = names[sio_data->type];
|
||||
|
||||
/* Now, we do the remaining detection. */
|
||||
@ -1069,7 +1083,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
||||
goto ERROR2;
|
||||
|
||||
/* Do not create fan files for disabled fans */
|
||||
if (data->type == it8716 || data->type == it8718) {
|
||||
if (has_16bit_fans(data)) {
|
||||
/* 16-bit tachometers */
|
||||
if (data->has_fan & (1 << 0)) {
|
||||
if ((err = device_create_file(dev,
|
||||
@ -1350,7 +1364,7 @@ static void __devinit it87_init_device(struct platform_device *pdev)
|
||||
data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
|
||||
|
||||
/* Set tachometers to 16-bit mode if needed */
|
||||
if (data->type == it8716 || data->type == it8718) {
|
||||
if (has_16bit_fans(data)) {
|
||||
tmp = it87_read_value(data, IT87_REG_FAN_16BIT);
|
||||
if (~tmp & 0x07 & data->has_fan) {
|
||||
dev_dbg(&pdev->dev,
|
||||
@ -1358,10 +1372,13 @@ static void __devinit it87_init_device(struct platform_device *pdev)
|
||||
it87_write_value(data, IT87_REG_FAN_16BIT,
|
||||
tmp | 0x07);
|
||||
}
|
||||
if (tmp & (1 << 4))
|
||||
data->has_fan |= (1 << 3); /* fan4 enabled */
|
||||
if (tmp & (1 << 5))
|
||||
data->has_fan |= (1 << 4); /* fan5 enabled */
|
||||
/* IT8705F only supports three fans. */
|
||||
if (data->type != it87) {
|
||||
if (tmp & (1 << 4))
|
||||
data->has_fan |= (1 << 3); /* fan4 enabled */
|
||||
if (tmp & (1 << 5))
|
||||
data->has_fan |= (1 << 4); /* fan5 enabled */
|
||||
}
|
||||
}
|
||||
|
||||
/* Set current fan mode registers and the default settings for the
|
||||
@ -1426,7 +1443,7 @@ static struct it87_data *it87_update_device(struct device *dev)
|
||||
data->fan[i] = it87_read_value(data,
|
||||
IT87_REG_FAN[i]);
|
||||
/* Add high byte if in 16-bit mode */
|
||||
if (data->type == it8716 || data->type == it8718) {
|
||||
if (has_16bit_fans(data)) {
|
||||
data->fan[i] |= it87_read_value(data,
|
||||
IT87_REG_FANX[i]) << 8;
|
||||
data->fan_min[i] |= it87_read_value(data,
|
||||
@ -1443,8 +1460,7 @@ static struct it87_data *it87_update_device(struct device *dev)
|
||||
}
|
||||
|
||||
/* Newer chips don't have clock dividers */
|
||||
if ((data->has_fan & 0x07) && data->type != it8716
|
||||
&& data->type != it8718) {
|
||||
if ((data->has_fan & 0x07) && !has_16bit_fans(data)) {
|
||||
i = it87_read_value(data, IT87_REG_FAN_DIV);
|
||||
data->fan_div[0] = i & 0x07;
|
||||
data->fan_div[1] = (i >> 3) & 0x07;
|
||||
@ -1460,7 +1476,8 @@ static struct it87_data *it87_update_device(struct device *dev)
|
||||
data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL);
|
||||
|
||||
data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE);
|
||||
/* The 8705 does not have VID capability */
|
||||
/* The 8705 does not have VID capability.
|
||||
The 8718 does not use IT87_REG_VID for the same purpose. */
|
||||
if (data->type == it8712 || data->type == it8716) {
|
||||
data->vid = it87_read_value(data, IT87_REG_VID);
|
||||
/* The older IT8712F revisions had only 5 VID pins,
|
||||
|
@ -55,8 +55,11 @@ I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs "
|
||||
static const u8 THMC50_REG_TEMP[] = { 0x27, 0x26, 0x20 };
|
||||
static const u8 THMC50_REG_TEMP_MIN[] = { 0x3A, 0x38, 0x2C };
|
||||
static const u8 THMC50_REG_TEMP_MAX[] = { 0x39, 0x37, 0x2B };
|
||||
static const u8 THMC50_REG_TEMP_CRITICAL[] = { 0x13, 0x14, 0x14 };
|
||||
static const u8 THMC50_REG_TEMP_DEFAULT[] = { 0x17, 0x18, 0x18 };
|
||||
|
||||
#define THMC50_REG_CONF_nFANOFF 0x20
|
||||
#define THMC50_REG_CONF_PROGRAMMED 0x08
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct thmc50_data {
|
||||
@ -72,6 +75,7 @@ struct thmc50_data {
|
||||
s8 temp_input[3];
|
||||
s8 temp_max[3];
|
||||
s8 temp_min[3];
|
||||
s8 temp_critical[3];
|
||||
u8 analog_out;
|
||||
u8 alarms;
|
||||
};
|
||||
@ -199,6 +203,15 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp_critical(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
struct thmc50_data *data = thmc50_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->temp_critical[nr] * 1000);
|
||||
}
|
||||
|
||||
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -214,7 +227,9 @@ static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp, \
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
|
||||
show_temp_min, set_temp_min, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
|
||||
show_temp_max, set_temp_max, offset - 1);
|
||||
show_temp_max, set_temp_max, offset - 1); \
|
||||
static SENSOR_DEVICE_ATTR(temp##offset##_crit, S_IRUGO, \
|
||||
show_temp_critical, NULL, offset - 1);
|
||||
|
||||
temp_reg(1);
|
||||
temp_reg(2);
|
||||
@ -234,10 +249,12 @@ static struct attribute *thmc50_attributes[] = {
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
@ -254,6 +271,7 @@ static struct attribute *temp3_attributes[] = {
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
NULL
|
||||
@ -429,6 +447,10 @@ static struct thmc50_data *thmc50_update_device(struct device *dev)
|
||||
|
||||
int temps = data->has_temp3 ? 3 : 2;
|
||||
int i;
|
||||
int prog = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
|
||||
|
||||
prog &= THMC50_REG_CONF_PROGRAMMED;
|
||||
|
||||
for (i = 0; i < temps; i++) {
|
||||
data->temp_input[i] = i2c_smbus_read_byte_data(client,
|
||||
THMC50_REG_TEMP[i]);
|
||||
@ -436,6 +458,10 @@ static struct thmc50_data *thmc50_update_device(struct device *dev)
|
||||
THMC50_REG_TEMP_MAX[i]);
|
||||
data->temp_min[i] = i2c_smbus_read_byte_data(client,
|
||||
THMC50_REG_TEMP_MIN[i]);
|
||||
data->temp_critical[i] =
|
||||
i2c_smbus_read_byte_data(client,
|
||||
prog ? THMC50_REG_TEMP_CRITICAL[i]
|
||||
: THMC50_REG_TEMP_DEFAULT[i]);
|
||||
}
|
||||
data->analog_out =
|
||||
i2c_smbus_read_byte_data(client, THMC50_REG_ANALOG_OUT);
|
||||
|
@ -67,10 +67,6 @@ module_param(force_i2c, byte, 0);
|
||||
MODULE_PARM_DESC(force_i2c,
|
||||
"Initialize the i2c address of the sensors");
|
||||
|
||||
static int reset;
|
||||
module_param(reset, bool, 0);
|
||||
MODULE_PARM_DESC(reset, "Set to one to reset chip on load");
|
||||
|
||||
static int init = 1;
|
||||
module_param(init, bool, 0);
|
||||
MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
|
||||
@ -209,6 +205,13 @@ static const u16 w83627hf_reg_temp_over[] = { 0x39, 0x155, 0x255 };
|
||||
#define W83627HF_REG_PWM1 0x5A
|
||||
#define W83627HF_REG_PWM2 0x5B
|
||||
|
||||
static const u8 W83627THF_REG_PWM_ENABLE[] = {
|
||||
0x04, /* FAN 1 mode */
|
||||
0x04, /* FAN 2 mode */
|
||||
0x12, /* FAN AUX mode */
|
||||
};
|
||||
static const u8 W83627THF_PWM_ENABLE_SHIFT[] = { 2, 4, 1 };
|
||||
|
||||
#define W83627THF_REG_PWM1 0x01 /* 697HF/637HF/687THF too */
|
||||
#define W83627THF_REG_PWM2 0x03 /* 697HF/637HF/687THF too */
|
||||
#define W83627THF_REG_PWM3 0x11 /* 637HF/687THF too */
|
||||
@ -366,6 +369,9 @@ struct w83627hf_data {
|
||||
u32 alarms; /* Register encoding, combined */
|
||||
u32 beep_mask; /* Register encoding, combined */
|
||||
u8 pwm[3]; /* Register value */
|
||||
u8 pwm_enable[3]; /* 1 = manual
|
||||
2 = thermal cruise (also called SmartFan I)
|
||||
3 = fan speed cruise */
|
||||
u8 pwm_freq[3]; /* Register value */
|
||||
u16 sens[3]; /* 1 = pentium diode; 2 = 3904 diode;
|
||||
4 = thermistor */
|
||||
@ -956,6 +962,42 @@ static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 2);
|
||||
|
||||
static ssize_t
|
||||
show_pwm_enable(struct device *dev, struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
int nr = to_sensor_dev_attr(devattr)->index;
|
||||
struct w83627hf_data *data = w83627hf_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->pwm_enable[nr]);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
store_pwm_enable(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int nr = to_sensor_dev_attr(devattr)->index;
|
||||
struct w83627hf_data *data = dev_get_drvdata(dev);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
u8 reg;
|
||||
|
||||
if (!val || (val > 3)) /* modes 1, 2 and 3 are supported */
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm_enable[nr] = val;
|
||||
reg = w83627hf_read_value(data, W83627THF_REG_PWM_ENABLE[nr]);
|
||||
reg &= ~(0x03 << W83627THF_PWM_ENABLE_SHIFT[nr]);
|
||||
reg |= (val - 1) << W83627THF_PWM_ENABLE_SHIFT[nr];
|
||||
w83627hf_write_value(data, W83627THF_REG_PWM_ENABLE[nr], reg);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 0);
|
||||
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
|
||||
store_pwm_enable, 2);
|
||||
|
||||
static ssize_t
|
||||
show_pwm_freq(struct device *dev, struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
@ -1223,6 +1265,11 @@ static struct attribute *w83627hf_attributes_opt[] = {
|
||||
&sensor_dev_attr_pwm1_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_freq.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_freq.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1366,6 +1413,19 @@ static int __devinit w83627hf_probe(struct platform_device *pdev)
|
||||
&sensor_dev_attr_pwm3_freq.dev_attr)))
|
||||
goto ERROR4;
|
||||
|
||||
if (data->type != w83627hf)
|
||||
if ((err = device_create_file(dev,
|
||||
&sensor_dev_attr_pwm1_enable.dev_attr))
|
||||
|| (err = device_create_file(dev,
|
||||
&sensor_dev_attr_pwm2_enable.dev_attr)))
|
||||
goto ERROR4;
|
||||
|
||||
if (data->type == w83627thf || data->type == w83637hf
|
||||
|| data->type == w83687thf)
|
||||
if ((err = device_create_file(dev,
|
||||
&sensor_dev_attr_pwm3_enable.dev_attr)))
|
||||
goto ERROR4;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
@ -1536,29 +1596,6 @@ static void __devinit w83627hf_init_device(struct platform_device *pdev)
|
||||
enum chips type = data->type;
|
||||
u8 tmp;
|
||||
|
||||
if (reset) {
|
||||
/* Resetting the chip has been the default for a long time,
|
||||
but repeatedly caused problems (fans going to full
|
||||
speed...) so it is now optional. It might even go away if
|
||||
nobody reports it as being useful, as I see very little
|
||||
reason why this would be needed at all. */
|
||||
dev_info(&pdev->dev, "If reset=1 solved a problem you were "
|
||||
"having, please report!\n");
|
||||
|
||||
/* save this register */
|
||||
i = w83627hf_read_value(data, W83781D_REG_BEEP_CONFIG);
|
||||
/* Reset all except Watchdog values and last conversion values
|
||||
This sets fan-divs to 2, among others */
|
||||
w83627hf_write_value(data, W83781D_REG_CONFIG, 0x80);
|
||||
/* Restore the register and disable power-on abnormal beep.
|
||||
This saves FAN 1/2/3 input/output values set by BIOS. */
|
||||
w83627hf_write_value(data, W83781D_REG_BEEP_CONFIG, i | 0x80);
|
||||
/* Disable master beep-enable (reset turns it on).
|
||||
Individual beeps should be reset to off but for some reason
|
||||
disabling this bit helps some people not get beeped */
|
||||
w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, 0);
|
||||
}
|
||||
|
||||
/* Minimize conflicts with other winbond i2c-only clients... */
|
||||
/* disable i2c subclients... how to disable main i2c client?? */
|
||||
/* force i2c address to relatively uncommon address */
|
||||
@ -1655,6 +1692,7 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev)
|
||||
{
|
||||
struct w83627hf_data *data = dev_get_drvdata(dev);
|
||||
int i, num_temps = (data->type == w83697hf) ? 2 : 3;
|
||||
int num_pwms = (data->type == w83697hf) ? 2 : 3;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@ -1707,6 +1745,15 @@ static struct w83627hf_data *w83627hf_update_device(struct device *dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data->type != w83627hf) {
|
||||
for (i = 0; i < num_pwms; i++) {
|
||||
u8 tmp = w83627hf_read_value(data,
|
||||
W83627THF_REG_PWM_ENABLE[i]);
|
||||
data->pwm_enable[i] =
|
||||
((tmp >> W83627THF_PWM_ENABLE_SHIFT[i])
|
||||
& 0x03) + 1;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < num_temps; i++) {
|
||||
data->temp[i] = w83627hf_read_value(
|
||||
data, w83627hf_reg_temp[i]);
|
||||
|
@ -233,11 +233,9 @@ static u8 fan_to_reg(long rpm, int div)
|
||||
static u8 div_to_reg(int nr, long val)
|
||||
{
|
||||
int i;
|
||||
int max;
|
||||
|
||||
/* first three fan's divisor max out at 8, rest max out at 128 */
|
||||
max = (nr < 3) ? 8 : 128;
|
||||
val = SENSORS_LIMIT(val, 1, max) >> 1;
|
||||
/* fan divisors max out at 128 */
|
||||
val = SENSORS_LIMIT(val, 1, 128) >> 1;
|
||||
for (i = 0; i < 7; i++) {
|
||||
if (val == 0)
|
||||
break;
|
||||
@ -530,6 +528,7 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
unsigned long min;
|
||||
u8 tmp_fan_div;
|
||||
u8 fan_div_reg;
|
||||
u8 vbat_reg;
|
||||
int indx = 0;
|
||||
u8 keep_mask = 0;
|
||||
u8 new_shift = 0;
|
||||
@ -581,6 +580,16 @@ static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
w83791d_write(client, W83791D_REG_FAN_DIV[indx],
|
||||
fan_div_reg | tmp_fan_div);
|
||||
|
||||
/* Bit 2 of fans 0-2 is stored in the vbat register (bits 5-7) */
|
||||
if (nr < 3) {
|
||||
keep_mask = ~(1 << (nr + 5));
|
||||
vbat_reg = w83791d_read(client, W83791D_REG_VBAT)
|
||||
& keep_mask;
|
||||
tmp_fan_div = (data->fan_div[nr] << (3 + nr)) & ~keep_mask;
|
||||
w83791d_write(client, W83791D_REG_VBAT,
|
||||
vbat_reg | tmp_fan_div);
|
||||
}
|
||||
|
||||
/* Restore fan_min */
|
||||
data->fan_min[nr] = fan_to_reg(min, DIV_FROM_REG(data->fan_div[nr]));
|
||||
w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]);
|
||||
@ -1182,6 +1191,7 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
|
||||
struct w83791d_data *data = i2c_get_clientdata(client);
|
||||
int i, j;
|
||||
u8 reg_array_tmp[3];
|
||||
u8 vbat_reg;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@ -1219,6 +1229,12 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
|
||||
data->fan_div[3] = reg_array_tmp[2] & 0x07;
|
||||
data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07;
|
||||
|
||||
/* The fan divisor for fans 0-2 get bit 2 from
|
||||
bits 5-7 respectively of vbat register */
|
||||
vbat_reg = w83791d_read(client, W83791D_REG_VBAT);
|
||||
for (i = 0; i < 3; i++)
|
||||
data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04;
|
||||
|
||||
/* Update the first temperature sensor */
|
||||
for (i = 0; i < 3; i++) {
|
||||
data->temp1[i] = w83791d_read(client,
|
||||
|
Loading…
Reference in New Issue
Block a user