mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 07:34:12 +08:00
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: (sysfs-interface) Update tempX_type attribute to be more generic hwmon: (adm1031) Fix coding style issues hwmon: (it87) Add IT8728F support hwmon: (coretemp) Add missing section annotations hwmon: (lm90) Add range check to set_update_interval hwmon: (lm63) Support extended lookup table of LM96163 hwmon: (lm63) Expose automatic fan speed control lookup table hwmon: (lm63) Fix incorrect comment about I2C address hwmon: (lm63) LM64 has a dedicated pin for tachometer hwmon: (lm63) Add sensor type attribute for external sensor on LM96163 hwmon: (lm63) Add support for update_interval sysfs attribute hwmon: (lm63) Add support for writing the external critical temperature hwmon: (lm63) Add support for unsigned upper temperature limits hwmon: (lm63) Add support for LM96163 hwmon: (lm63) Add support for external temperature offset register hwmon: (lm63) Fix checkpatch errors hwmon: (max1111) Change sysfs interface to in[0-3]_input in millivolts
This commit is contained in:
commit
adfeb6e9f4
@ -26,6 +26,10 @@ Supported chips:
|
||||
Prefix: 'it8721'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Not publicly available
|
||||
* IT8728F
|
||||
Prefix: 'it8728'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
Datasheet: Not publicly available
|
||||
* SiS950 [clone of IT8705F]
|
||||
Prefix: 'it87'
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
@ -71,7 +75,7 @@ Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the IT8705F, IT8712F, IT8716F,
|
||||
IT8718F, IT8720F, IT8721F, IT8726F, IT8758E and SiS950 chips.
|
||||
IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E and SiS950 chips.
|
||||
|
||||
These chips are 'Super I/O chips', supporting floppy disks, infrared ports,
|
||||
joysticks and other miscellaneous stuff. For hardware monitoring, they
|
||||
@ -105,6 +109,9 @@ The IT8726F is just bit enhanced IT8716F with additional hardware
|
||||
for AMD power sequencing. Therefore the chip will appear as IT8716F
|
||||
to userspace applications.
|
||||
|
||||
The IT8728F is considered compatible with the IT8721F, until a datasheet
|
||||
becomes available (hopefully.)
|
||||
|
||||
Temperatures are measured in degrees Celsius. An alarm is triggered once
|
||||
when the Overtemperature Shutdown limit is crossed.
|
||||
|
||||
@ -121,8 +128,8 @@ alarm is triggered if the voltage has crossed a programmable minimum or
|
||||
maximum limit. Note that minimum in this case always means 'closest to
|
||||
zero'; this is important for negative voltage measurements. All voltage
|
||||
inputs can measure voltages between 0 and 4.08 volts, with a resolution of
|
||||
0.016 volt (except IT8721F/IT8758E: 0.012 volt.) The battery voltage in8 does
|
||||
not have limit registers.
|
||||
0.016 volt (except IT8721F/IT8758E and IT8728F: 0.012 volt.) The battery
|
||||
voltage in8 does not have limit registers.
|
||||
|
||||
On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside
|
||||
the chip (in7, in8 and optionally in3). The driver handles this transparently
|
||||
|
@ -12,6 +12,11 @@ Supported chips:
|
||||
Addresses scanned: I2C 0x18 and 0x4e
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/pf/LM/LM64.html
|
||||
* National Semiconductor LM96163
|
||||
Prefix: 'lm96163'
|
||||
Addresses scanned: I2C 0x4c
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
http://www.national.com/pf/LM/LM96163.html
|
||||
|
||||
Author: Jean Delvare <khali@linux-fr.org>
|
||||
|
||||
@ -49,16 +54,24 @@ value for measuring the speed of the fan. It can measure fan speeds down to
|
||||
Note that the pin used for fan monitoring is shared with an alert out
|
||||
function. Depending on how the board designer wanted to use the chip, fan
|
||||
speed monitoring will or will not be possible. The proper chip configuration
|
||||
is left to the BIOS, and the driver will blindly trust it.
|
||||
is left to the BIOS, and the driver will blindly trust it. Only the original
|
||||
LM63 suffers from this limitation, the LM64 and LM96163 have separate pins
|
||||
for fan monitoring and alert out. On the LM64, monitoring is always enabled;
|
||||
on the LM96163 it can be disabled.
|
||||
|
||||
A PWM output can be used to control the speed of the fan. The LM63 has two
|
||||
PWM modes: manual and automatic. Automatic mode is not fully implemented yet
|
||||
(you cannot define your custom PWM/temperature curve), and mode change isn't
|
||||
supported either.
|
||||
|
||||
The lm63 driver will not update its values more frequently than every
|
||||
second; reading them more often will do no harm, but will return 'old'
|
||||
values.
|
||||
The lm63 driver will not update its values more frequently than configured with
|
||||
the update_interval sysfs attribute; reading them more often will do no harm,
|
||||
but will return 'old' values. Values in the automatic fan control lookup table
|
||||
(attributes pwm1_auto_*) have their own independent lifetime of 5 seconds.
|
||||
|
||||
The LM64 is effectively an LM63 with GPIO lines. The driver does not
|
||||
support these GPIO lines at present.
|
||||
|
||||
The LM96163 is an enhanced version of LM63 with improved temperature accuracy
|
||||
and better PWM resolution. For LM96163, the external temperature sensor type is
|
||||
configurable as CPU embedded diode(1) or 3904 transistor(2).
|
||||
|
@ -304,7 +304,7 @@ value (fastest fan speed) wins.
|
||||
temp[1-*]_type Sensor type selection.
|
||||
Integers 1 to 6
|
||||
RW
|
||||
1: PII/Celeron Diode
|
||||
1: CPU embedded diode
|
||||
2: 3904 transistor
|
||||
3: thermal diode
|
||||
4: thermistor
|
||||
|
@ -474,8 +474,8 @@ config SENSORS_IT87
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for ITE IT8705F, IT8712F,
|
||||
IT8716F, IT8718F, IT8720F, IT8721F, IT8726F and IT8758E sensor
|
||||
chips, and the SiS960 clone.
|
||||
IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F and IT8758E
|
||||
sensor chips, and the SiS960 clone.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called it87.
|
||||
@ -515,11 +515,11 @@ config SENSORS_LINEAGE
|
||||
will be called lineage-pem.
|
||||
|
||||
config SENSORS_LM63
|
||||
tristate "National Semiconductor LM63 and LM64"
|
||||
tristate "National Semiconductor LM63 and compatibles"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the National
|
||||
Semiconductor LM63 and LM64 remote diode digital temperature
|
||||
Semiconductor LM63, LM64, and LM96163 remote diode digital temperature
|
||||
sensors with integrated fan control. Such chips are found
|
||||
on the Tyan S4882 (Thunder K8QS Pro) motherboard, among
|
||||
others.
|
||||
|
@ -155,7 +155,8 @@ adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
|
||||
#define TEMP_OFFSET_FROM_REG(val) TEMP_FROM_REG((val) < 0 ? \
|
||||
(val) | 0x70 : (val))
|
||||
|
||||
#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
|
||||
#define FAN_FROM_REG(reg, div) ((reg) ? \
|
||||
(11250 * 60) / ((reg) * (div)) : 0)
|
||||
|
||||
static int FAN_TO_REG(int reg, int div)
|
||||
{
|
||||
@ -174,8 +175,8 @@ static int FAN_TO_REG(int reg, int div)
|
||||
(((reg) & 0x1F) | (((val) << 5) & 0xe0))
|
||||
|
||||
#define AUTO_TEMP_MIN_TO_REG(val, reg) \
|
||||
((((val)/500) & 0xf8)|((reg) & 0x7))
|
||||
#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7)))
|
||||
((((val) / 500) & 0xf8) | ((reg) & 0x7))
|
||||
#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1 << ((reg) & 0x7)))
|
||||
#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2))
|
||||
|
||||
#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2)
|
||||
@ -202,7 +203,7 @@ static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
|
||||
|
||||
/* FAN auto control */
|
||||
#define GET_FAN_AUTO_BITFIELD(data, idx) \
|
||||
(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]
|
||||
(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx % 2]
|
||||
|
||||
/* The tables below contains the possible values for the auto fan
|
||||
* control bitfields. the index in the table is the register value.
|
||||
@ -230,7 +231,7 @@ static const auto_chan_table_t auto_channel_select_table_adm1030 = {
|
||||
*/
|
||||
static int
|
||||
get_fan_auto_nearest(struct adm1031_data *data,
|
||||
int chan, u8 val, u8 reg, u8 * new_reg)
|
||||
int chan, u8 val, u8 reg, u8 *new_reg)
|
||||
{
|
||||
int i;
|
||||
int first_match = -1, exact_match = -1;
|
||||
@ -258,13 +259,13 @@ get_fan_auto_nearest(struct adm1031_data *data,
|
||||
}
|
||||
}
|
||||
|
||||
if (exact_match >= 0) {
|
||||
if (exact_match >= 0)
|
||||
*new_reg = exact_match;
|
||||
} else if (first_match >= 0) {
|
||||
else if (first_match >= 0)
|
||||
*new_reg = first_match;
|
||||
} else {
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -283,23 +284,28 @@ set_fan_auto_channel(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
u8 reg;
|
||||
int ret;
|
||||
u8 old_fan_mode;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old_fan_mode = data->conf1;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®))) {
|
||||
ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®);
|
||||
if (ret) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
|
||||
if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) ^
|
||||
(old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
|
||||
if (data->conf1 & ADM1031_CONF1_AUTO_MODE){
|
||||
if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
|
||||
/* Switch to Auto Fan Mode
|
||||
* Save PWM registers
|
||||
* Set PWM registers to 33% Both */
|
||||
@ -350,7 +356,12 @@ set_auto_temp_min(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
|
||||
@ -374,10 +385,16 @@ set_auto_temp_max(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]);
|
||||
data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr],
|
||||
data->pwm[nr]);
|
||||
adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
|
||||
data->temp_max[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
@ -410,8 +427,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
int reg;
|
||||
long val;
|
||||
int ret, reg;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) &&
|
||||
@ -449,9 +470,13 @@ static int trust_fan_readings(struct adm1031_data *data, int chan)
|
||||
|
||||
if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
|
||||
switch (data->conf1 & 0x60) {
|
||||
case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */
|
||||
case 0x00:
|
||||
/*
|
||||
* remote temp1 controls fan1,
|
||||
* remote temp2 controls fan2
|
||||
*/
|
||||
res = data->temp[chan+1] >=
|
||||
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
|
||||
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
|
||||
break;
|
||||
case 0x20: /* remote temp1 controls both fans */
|
||||
res =
|
||||
@ -515,7 +540,12 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (val) {
|
||||
@ -534,10 +564,15 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
u8 tmp;
|
||||
int old_div;
|
||||
int new_min;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = val == 8 ? 0xc0 :
|
||||
val == 4 ? 0x80 :
|
||||
@ -631,9 +666,13 @@ static ssize_t set_temp_offset(struct device *dev,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val;
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
val = SENSORS_LIMIT(val, -15000, 15000);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_offset[nr] = TEMP_OFFSET_TO_REG(val);
|
||||
@ -648,9 +687,13 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val;
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_min[nr] = TEMP_TO_REG(val);
|
||||
@ -665,9 +708,13 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val;
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_max[nr] = TEMP_TO_REG(val);
|
||||
@ -682,9 +729,13 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct adm1031_data *data = i2c_get_clientdata(client);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
int val;
|
||||
long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = simple_strtol(buf, NULL, 10);
|
||||
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_crit[nr] = TEMP_TO_REG(val);
|
||||
@ -711,7 +762,8 @@ temp_reg(2);
|
||||
temp_reg(3);
|
||||
|
||||
/* Alarms */
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct adm1031_data *data = adm1031_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->alarm);
|
||||
@ -919,12 +971,13 @@ static int adm1031_probe(struct i2c_client *client,
|
||||
adm1031_init_client(client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&client->dev.kobj, &adm1031_group)))
|
||||
err = sysfs_create_group(&client->dev.kobj, &adm1031_group);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
|
||||
if (data->chip_type == adm1031) {
|
||||
if ((err = sysfs_create_group(&client->dev.kobj,
|
||||
&adm1031_group_opt)))
|
||||
err = sysfs_create_group(&client->dev.kobj, &adm1031_group_opt);
|
||||
if (err)
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
@ -970,14 +1023,13 @@ static void adm1031_init_client(struct i2c_client *client)
|
||||
}
|
||||
/* Initialize the ADM1031 chip (enables fan speed reading ) */
|
||||
read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
|
||||
if ((read_val | mask) != read_val) {
|
||||
adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
|
||||
}
|
||||
if ((read_val | mask) != read_val)
|
||||
adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
|
||||
|
||||
read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
|
||||
if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
|
||||
adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
|
||||
ADM1031_CONF1_MONITOR_ENABLE);
|
||||
adm1031_write_value(client, ADM1031_REG_CONF1,
|
||||
read_val | ADM1031_CONF1_MONITOR_ENABLE);
|
||||
}
|
||||
|
||||
/* Read the chip's update rate */
|
||||
@ -1024,8 +1076,7 @@ static struct adm1031_data *adm1031_update_device(struct device *dev)
|
||||
/* oldh is actually newer */
|
||||
if (newh != oldh)
|
||||
dev_warn(&client->dev,
|
||||
"Remote temperature may be "
|
||||
"wrong.\n");
|
||||
"Remote temperature may be wrong.\n");
|
||||
#endif
|
||||
}
|
||||
data->temp[chan] = newh;
|
||||
@ -1052,22 +1103,24 @@ static struct adm1031_data *adm1031_update_device(struct device *dev)
|
||||
data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
|
||||
|
||||
data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
|
||||
| (adm1031_read_value(client, ADM1031_REG_STATUS(1))
|
||||
<< 8);
|
||||
if (data->chip_type == adm1030) {
|
||||
| (adm1031_read_value(client, ADM1031_REG_STATUS(1)) << 8);
|
||||
if (data->chip_type == adm1030)
|
||||
data->alarm &= 0xc0ff;
|
||||
}
|
||||
|
||||
for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) {
|
||||
for (chan = 0; chan < (data->chip_type == adm1030 ? 1 : 2);
|
||||
chan++) {
|
||||
data->fan_div[chan] =
|
||||
adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan));
|
||||
adm1031_read_value(client,
|
||||
ADM1031_REG_FAN_DIV(chan));
|
||||
data->fan_min[chan] =
|
||||
adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan));
|
||||
adm1031_read_value(client,
|
||||
ADM1031_REG_FAN_MIN(chan));
|
||||
data->fan[chan] =
|
||||
adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan));
|
||||
adm1031_read_value(client,
|
||||
ADM1031_REG_FAN_SPEED(chan));
|
||||
data->pwm[chan] =
|
||||
0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >>
|
||||
(4*chan));
|
||||
(adm1031_read_value(client,
|
||||
ADM1031_REG_PWM) >> (4 * chan)) & 0x0f;
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
|
@ -190,7 +190,8 @@ static ssize_t show_temp(struct device *dev,
|
||||
return tdata->valid ? sprintf(buf, "%d\n", tdata->temp) : -EAGAIN;
|
||||
}
|
||||
|
||||
static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
static int __cpuinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id,
|
||||
struct device *dev)
|
||||
{
|
||||
/* The 100C is default for both mobile and non mobile CPUs */
|
||||
|
||||
@ -284,7 +285,8 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
return tjmax;
|
||||
}
|
||||
|
||||
static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
static int __cpuinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
|
||||
struct device *dev)
|
||||
{
|
||||
int err;
|
||||
u32 eax, edx;
|
||||
@ -323,7 +325,8 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
return adjust_tjmax(c, id, dev);
|
||||
}
|
||||
|
||||
static int create_name_attr(struct platform_data *pdata, struct device *dev)
|
||||
static int __devinit create_name_attr(struct platform_data *pdata,
|
||||
struct device *dev)
|
||||
{
|
||||
sysfs_attr_init(&pdata->name_attr.attr);
|
||||
pdata->name_attr.attr.name = "name";
|
||||
@ -332,8 +335,8 @@ static int create_name_attr(struct platform_data *pdata, struct device *dev)
|
||||
return device_create_file(dev, &pdata->name_attr);
|
||||
}
|
||||
|
||||
static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
||||
int attr_no)
|
||||
static int __cpuinit create_core_attrs(struct temp_data *tdata,
|
||||
struct device *dev, int attr_no)
|
||||
{
|
||||
int err, i;
|
||||
static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev,
|
||||
@ -383,7 +386,7 @@ static int __cpuinit chk_ucode_version(unsigned int cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device *coretemp_get_pdev(unsigned int cpu)
|
||||
static struct platform_device __cpuinit *coretemp_get_pdev(unsigned int cpu)
|
||||
{
|
||||
u16 phys_proc_id = TO_PHYS_ID(cpu);
|
||||
struct pdev_entry *p;
|
||||
@ -400,7 +403,8 @@ static struct platform_device *coretemp_get_pdev(unsigned int cpu)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
||||
static struct temp_data __cpuinit *init_temp_data(unsigned int cpu,
|
||||
int pkg_flag)
|
||||
{
|
||||
struct temp_data *tdata;
|
||||
|
||||
@ -418,7 +422,7 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
||||
return tdata;
|
||||
}
|
||||
|
||||
static int create_core_data(struct platform_device *pdev,
|
||||
static int __cpuinit create_core_data(struct platform_device *pdev,
|
||||
unsigned int cpu, int pkg_flag)
|
||||
{
|
||||
struct temp_data *tdata;
|
||||
@ -489,7 +493,7 @@ exit_free:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void coretemp_add_core(unsigned int cpu, int pkg_flag)
|
||||
static void __cpuinit coretemp_add_core(unsigned int cpu, int pkg_flag)
|
||||
{
|
||||
struct platform_device *pdev = coretemp_get_pdev(cpu);
|
||||
int err;
|
||||
@ -618,7 +622,7 @@ exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void coretemp_device_remove(unsigned int cpu)
|
||||
static void __cpuinit coretemp_device_remove(unsigned int cpu)
|
||||
{
|
||||
struct pdev_entry *p, *n;
|
||||
u16 phys_proc_id = TO_PHYS_ID(cpu);
|
||||
@ -634,7 +638,7 @@ static void coretemp_device_remove(unsigned int cpu)
|
||||
mutex_unlock(&pdev_list_mutex);
|
||||
}
|
||||
|
||||
static bool is_any_core_online(struct platform_data *pdata)
|
||||
static bool __cpuinit is_any_core_online(struct platform_data *pdata)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
* IT8720F Super I/O chip w/LPC interface
|
||||
* IT8721F Super I/O chip w/LPC interface
|
||||
* IT8726F Super I/O chip w/LPC interface
|
||||
* IT8728F Super I/O chip w/LPC interface
|
||||
* IT8758E Super I/O chip w/LPC interface
|
||||
* Sis950 A clone of the IT8705F
|
||||
*
|
||||
@ -58,7 +59,7 @@
|
||||
|
||||
#define DRVNAME "it87"
|
||||
|
||||
enum chips { it87, it8712, it8716, it8718, it8720, it8721 };
|
||||
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728 };
|
||||
|
||||
static unsigned short force_id;
|
||||
module_param(force_id, ushort, 0);
|
||||
@ -135,6 +136,7 @@ static inline void superio_exit(void)
|
||||
#define IT8720F_DEVID 0x8720
|
||||
#define IT8721F_DEVID 0x8721
|
||||
#define IT8726F_DEVID 0x8726
|
||||
#define IT8728F_DEVID 0x8728
|
||||
#define IT87_ACT_REG 0x30
|
||||
#define IT87_BASE_REG 0x60
|
||||
|
||||
@ -274,11 +276,31 @@ struct it87_data {
|
||||
s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */
|
||||
};
|
||||
|
||||
static inline int has_12mv_adc(const struct it87_data *data)
|
||||
{
|
||||
/*
|
||||
* IT8721F and later have a 12 mV ADC, also with internal scaling
|
||||
* on selected inputs.
|
||||
*/
|
||||
return data->type == it8721
|
||||
|| data->type == it8728;
|
||||
}
|
||||
|
||||
static inline int has_newer_autopwm(const struct it87_data *data)
|
||||
{
|
||||
/*
|
||||
* IT8721F and later have separate registers for the temperature
|
||||
* mapping and the manual duty cycle.
|
||||
*/
|
||||
return data->type == it8721
|
||||
|| data->type == it8728;
|
||||
}
|
||||
|
||||
static u8 in_to_reg(const struct it87_data *data, int nr, long val)
|
||||
{
|
||||
long lsb;
|
||||
|
||||
if (data->type == it8721) {
|
||||
if (has_12mv_adc(data)) {
|
||||
if (data->in_scaled & (1 << nr))
|
||||
lsb = 24;
|
||||
else
|
||||
@ -292,7 +314,7 @@ static u8 in_to_reg(const struct it87_data *data, int nr, long val)
|
||||
|
||||
static int in_from_reg(const struct it87_data *data, int nr, int val)
|
||||
{
|
||||
if (data->type == it8721) {
|
||||
if (has_12mv_adc(data)) {
|
||||
if (data->in_scaled & (1 << nr))
|
||||
return val * 24;
|
||||
else
|
||||
@ -329,7 +351,7 @@ static inline u16 FAN16_TO_REG(long rpm)
|
||||
|
||||
static u8 pwm_to_reg(const struct it87_data *data, long val)
|
||||
{
|
||||
if (data->type == it8721)
|
||||
if (has_newer_autopwm(data))
|
||||
return val;
|
||||
else
|
||||
return val >> 1;
|
||||
@ -337,7 +359,7 @@ static u8 pwm_to_reg(const struct it87_data *data, long val)
|
||||
|
||||
static int pwm_from_reg(const struct it87_data *data, u8 reg)
|
||||
{
|
||||
if (data->type == it8721)
|
||||
if (has_newer_autopwm(data))
|
||||
return reg;
|
||||
else
|
||||
return (reg & 0x7f) << 1;
|
||||
@ -374,7 +396,8 @@ static inline int has_16bit_fans(const struct it87_data *data)
|
||||
|| data->type == it8716
|
||||
|| data->type == it8718
|
||||
|| data->type == it8720
|
||||
|| data->type == it8721;
|
||||
|| data->type == it8721
|
||||
|| data->type == it8728;
|
||||
}
|
||||
|
||||
static inline int has_old_autopwm(const struct it87_data *data)
|
||||
@ -842,7 +865,7 @@ static ssize_t set_pwm_enable(struct device *dev,
|
||||
data->fan_main_ctrl);
|
||||
} else {
|
||||
if (val == 1) /* Manual mode */
|
||||
data->pwm_ctrl[nr] = data->type == it8721 ?
|
||||
data->pwm_ctrl[nr] = has_newer_autopwm(data) ?
|
||||
data->pwm_temp_map[nr] :
|
||||
data->pwm_duty[nr];
|
||||
else /* Automatic mode */
|
||||
@ -870,7 +893,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->type == it8721) {
|
||||
if (has_newer_autopwm(data)) {
|
||||
/* If we are in automatic mode, the PWM duty cycle register
|
||||
* is read-only so we can't write the value */
|
||||
if (data->pwm_ctrl[nr] & 0x80) {
|
||||
@ -1311,8 +1334,8 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr,
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int nr = to_sensor_dev_attr(attr)->index;
|
||||
|
||||
return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr]
|
||||
: labels[nr]);
|
||||
return sprintf(buf, "%s\n", has_12mv_adc(data) ? labels_it8721[nr]
|
||||
: labels[nr]);
|
||||
}
|
||||
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
|
||||
@ -1605,6 +1628,9 @@ static int __init it87_find(unsigned short *address,
|
||||
case IT8721F_DEVID:
|
||||
sio_data->type = it8721;
|
||||
break;
|
||||
case IT8728F_DEVID:
|
||||
sio_data->type = it8728;
|
||||
break;
|
||||
case 0xffff: /* No device at all */
|
||||
goto exit;
|
||||
default:
|
||||
@ -1646,8 +1672,11 @@ static int __init it87_find(unsigned short *address,
|
||||
superio_select(GPIO);
|
||||
|
||||
reg = superio_inb(IT87_SIO_GPIO3_REG);
|
||||
if (sio_data->type == it8721) {
|
||||
/* The IT8721F/IT8758E doesn't have VID pins at all */
|
||||
if (sio_data->type == it8721 || sio_data->type == it8728) {
|
||||
/*
|
||||
* The IT8721F/IT8758E doesn't have VID pins at all,
|
||||
* not sure about the IT8728F.
|
||||
*/
|
||||
sio_data->skip_vid = 1;
|
||||
} else {
|
||||
/* We need at least 4 VID pins */
|
||||
@ -1692,7 +1721,8 @@ static int __init it87_find(unsigned short *address,
|
||||
}
|
||||
if (reg & (1 << 0))
|
||||
sio_data->internal |= (1 << 0);
|
||||
if ((reg & (1 << 1)) || sio_data->type == it8721)
|
||||
if ((reg & (1 << 1)) || sio_data->type == it8721 ||
|
||||
sio_data->type == it8728)
|
||||
sio_data->internal |= (1 << 1);
|
||||
|
||||
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
|
||||
@ -1770,6 +1800,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
||||
"it8718",
|
||||
"it8720",
|
||||
"it8721",
|
||||
"it8728",
|
||||
};
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
@ -1807,7 +1838,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
|
||||
enable_pwm_interface = it87_check_pwm(dev);
|
||||
|
||||
/* Starting with IT8721F, we handle scaling of internal voltages */
|
||||
if (data->type == it8721) {
|
||||
if (has_12mv_adc(data)) {
|
||||
if (sio_data->internal & (1 << 0))
|
||||
data->in_scaled |= (1 << 3); /* in3 is AVCC */
|
||||
if (sio_data->internal & (1 << 1))
|
||||
@ -2093,7 +2124,7 @@ static void __devinit it87_init_device(struct platform_device *pdev)
|
||||
static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
|
||||
{
|
||||
data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr));
|
||||
if (data->type == it8721) {
|
||||
if (has_newer_autopwm(data)) {
|
||||
data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
|
||||
data->pwm_duty[nr] = it87_read_value(data,
|
||||
IT87_REG_PWM_DUTY(nr));
|
||||
|
@ -47,10 +47,14 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Addresses to scan
|
||||
* Address is fully defined internally and cannot be changed.
|
||||
* Address is fully defined internally and cannot be changed except for
|
||||
* LM64 which has one pin dedicated to address selection.
|
||||
* LM63 and LM96163 have address 0x4c.
|
||||
* LM64 can have address 0x18 or 0x4e.
|
||||
*/
|
||||
|
||||
static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
@ -60,6 +64,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
*/
|
||||
|
||||
#define LM63_REG_CONFIG1 0x03
|
||||
#define LM63_REG_CONVRATE 0x04
|
||||
#define LM63_REG_CONFIG2 0xBF
|
||||
#define LM63_REG_CONFIG_FAN 0x4A
|
||||
|
||||
@ -70,6 +75,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
|
||||
#define LM63_REG_PWM_VALUE 0x4C
|
||||
#define LM63_REG_PWM_FREQ 0x4D
|
||||
#define LM63_REG_LUT_TEMP_HYST 0x4F
|
||||
#define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr))
|
||||
#define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr))
|
||||
|
||||
#define LM63_REG_LOCAL_TEMP 0x00
|
||||
#define LM63_REG_LOCAL_HIGH 0x05
|
||||
@ -91,6 +99,16 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
#define LM63_REG_MAN_ID 0xFE
|
||||
#define LM63_REG_CHIP_ID 0xFF
|
||||
|
||||
#define LM96163_REG_TRUTHERM 0x30
|
||||
#define LM96163_REG_REMOTE_TEMP_U_MSB 0x31
|
||||
#define LM96163_REG_REMOTE_TEMP_U_LSB 0x32
|
||||
#define LM96163_REG_CONFIG_ENHANCED 0x45
|
||||
|
||||
#define LM63_MAX_CONVRATE 9
|
||||
|
||||
#define LM63_MAX_CONVRATE_HZ 32
|
||||
#define LM96163_MAX_CONVRATE_HZ 26
|
||||
|
||||
/*
|
||||
* Conversions and various macros
|
||||
* For tachometer counts, the LM63 uses 16-bit values.
|
||||
@ -112,15 +130,24 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
(val) >= 127000 ? 127 : \
|
||||
(val) < 0 ? ((val) - 500) / 1000 : \
|
||||
((val) + 500) / 1000)
|
||||
#define TEMP8U_TO_REG(val) ((val) <= 0 ? 0 : \
|
||||
(val) >= 255000 ? 255 : \
|
||||
((val) + 500) / 1000)
|
||||
#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
|
||||
#define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
|
||||
(val) >= 127875 ? 0x7FE0 : \
|
||||
(val) < 0 ? ((val) - 62) / 125 * 32 : \
|
||||
((val) + 62) / 125 * 32)
|
||||
#define TEMP11U_TO_REG(val) ((val) <= 0 ? 0 : \
|
||||
(val) >= 255875 ? 0xFFE0 : \
|
||||
((val) + 62) / 125 * 32)
|
||||
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : \
|
||||
(val) >= 127000 ? 127 : \
|
||||
((val) + 500) / 1000)
|
||||
|
||||
#define UPDATE_INTERVAL(max, rate) \
|
||||
((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
|
||||
|
||||
/*
|
||||
* Functions declaration
|
||||
*/
|
||||
@ -134,7 +161,7 @@ static struct lm63_data *lm63_update_device(struct device *dev);
|
||||
static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
|
||||
static void lm63_init_client(struct i2c_client *client);
|
||||
|
||||
enum chips { lm63, lm64 };
|
||||
enum chips { lm63, lm64, lm96163 };
|
||||
|
||||
/*
|
||||
* Driver data (common to all clients)
|
||||
@ -143,6 +170,7 @@ enum chips { lm63, lm64 };
|
||||
static const struct i2c_device_id lm63_id[] = {
|
||||
{ "lm63", lm63 },
|
||||
{ "lm64", lm64 },
|
||||
{ "lm96163", lm96163 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm63_id);
|
||||
@ -167,26 +195,53 @@ struct lm63_data {
|
||||
struct device *hwmon_dev;
|
||||
struct mutex update_lock;
|
||||
char valid; /* zero until following fields are valid */
|
||||
char lut_valid; /* zero until lut fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
int kind;
|
||||
unsigned long lut_last_updated; /* in jiffies */
|
||||
enum chips kind;
|
||||
int temp2_offset;
|
||||
|
||||
int update_interval; /* in milliseconds */
|
||||
int max_convrate_hz;
|
||||
int lut_size; /* 8 or 12 */
|
||||
|
||||
/* registers values */
|
||||
u8 config, config_fan;
|
||||
u16 fan[2]; /* 0: input
|
||||
1: low limit */
|
||||
u8 pwm1_freq;
|
||||
u8 pwm1_value;
|
||||
s8 temp8[3]; /* 0: local input
|
||||
u8 pwm1[13]; /* 0: current output
|
||||
1-12: lookup table */
|
||||
s8 temp8[15]; /* 0: local input
|
||||
1: local high limit
|
||||
2: remote critical limit */
|
||||
s16 temp11[3]; /* 0: remote input
|
||||
2: remote critical limit
|
||||
3-14: lookup table */
|
||||
s16 temp11[4]; /* 0: remote input
|
||||
1: remote low limit
|
||||
2: remote high limit */
|
||||
2: remote high limit
|
||||
3: remote offset */
|
||||
u16 temp11u; /* remote input (unsigned) */
|
||||
u8 temp2_crit_hyst;
|
||||
u8 lut_temp_hyst;
|
||||
u8 alarms;
|
||||
bool pwm_highres;
|
||||
bool lut_temp_highres;
|
||||
bool remote_unsigned; /* true if unsigned remote upper limits */
|
||||
bool trutherm;
|
||||
};
|
||||
|
||||
static inline int temp8_from_reg(struct lm63_data *data, int nr)
|
||||
{
|
||||
if (data->remote_unsigned)
|
||||
return TEMP8_FROM_REG((u8)data->temp8[nr]);
|
||||
return TEMP8_FROM_REG(data->temp8[nr]);
|
||||
}
|
||||
|
||||
static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
|
||||
{
|
||||
return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs callback functions and files
|
||||
*/
|
||||
@ -204,7 +259,12 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val = simple_strtoul(buf, NULL, 10);
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->fan[1] = FAN_TO_REG(val);
|
||||
@ -216,13 +276,22 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy,
|
||||
static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ?
|
||||
255 : (data->pwm1_value * 255 + data->pwm1_freq) /
|
||||
(2 * data->pwm1_freq));
|
||||
int nr = attr->index;
|
||||
int pwm;
|
||||
|
||||
if (data->pwm_highres)
|
||||
pwm = data->pwm1[nr];
|
||||
else
|
||||
pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
|
||||
255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
|
||||
(2 * data->pwm1_freq);
|
||||
|
||||
return sprintf(buf, "%d\n", pwm);
|
||||
}
|
||||
|
||||
static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
|
||||
@ -231,22 +300,26 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
|
||||
int err;
|
||||
|
||||
if (!(data->config_fan & 0x20)) /* register is read-only */
|
||||
return -EPERM;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = SENSORS_LIMIT(val, 0, 255);
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm1_value = val <= 0 ? 0 :
|
||||
val >= 255 ? 2 * data->pwm1_freq :
|
||||
(val * data->pwm1_freq * 2 + 127) / 255;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
|
||||
data->pwm1[0] = data->pwm_highres ? val :
|
||||
(val * data->pwm1_freq * 2 + 127) / 255;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy,
|
||||
char *buf)
|
||||
static ssize_t show_pwm1_enable(struct device *dev,
|
||||
struct device_attribute *dummy, char *buf)
|
||||
{
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
|
||||
@ -273,21 +346,47 @@ static ssize_t show_remote_temp8(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index])
|
||||
return sprintf(buf, "%d\n", temp8_from_reg(data, attr->index)
|
||||
+ data->temp2_offset);
|
||||
}
|
||||
|
||||
static ssize_t set_local_temp8(struct device *dev,
|
||||
struct device_attribute *dummy,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t show_lut_temp(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
|
||||
+ data->temp2_offset);
|
||||
}
|
||||
|
||||
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
int nr = attr->index;
|
||||
int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH;
|
||||
long val;
|
||||
int err;
|
||||
int temp;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp8[1] = TEMP8_TO_REG(val);
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]);
|
||||
if (nr == 2) {
|
||||
if (data->remote_unsigned)
|
||||
temp = TEMP8U_TO_REG(val - data->temp2_offset);
|
||||
else
|
||||
temp = TEMP8_TO_REG(val - data->temp2_offset);
|
||||
} else {
|
||||
temp = TEMP8_TO_REG(val);
|
||||
}
|
||||
data->temp8[nr] = temp;
|
||||
i2c_smbus_write_byte_data(client, reg, temp);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
@ -297,28 +396,56 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index])
|
||||
+ data->temp2_offset);
|
||||
int nr = attr->index;
|
||||
int temp;
|
||||
|
||||
if (!nr) {
|
||||
/*
|
||||
* Use unsigned temperature unless its value is zero.
|
||||
* If it is zero, use signed temperature.
|
||||
*/
|
||||
if (data->temp11u)
|
||||
temp = TEMP11_FROM_REG(data->temp11u);
|
||||
else
|
||||
temp = TEMP11_FROM_REG(data->temp11[nr]);
|
||||
} else {
|
||||
if (data->remote_unsigned && nr == 2)
|
||||
temp = TEMP11_FROM_REG((u16)data->temp11[nr]);
|
||||
else
|
||||
temp = TEMP11_FROM_REG(data->temp11[nr]);
|
||||
}
|
||||
return sprintf(buf, "%d\n", temp + data->temp2_offset);
|
||||
}
|
||||
|
||||
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
static const u8 reg[4] = {
|
||||
static const u8 reg[6] = {
|
||||
LM63_REG_REMOTE_LOW_MSB,
|
||||
LM63_REG_REMOTE_LOW_LSB,
|
||||
LM63_REG_REMOTE_HIGH_MSB,
|
||||
LM63_REG_REMOTE_HIGH_LSB,
|
||||
LM63_REG_REMOTE_OFFSET_MSB,
|
||||
LM63_REG_REMOTE_OFFSET_LSB,
|
||||
};
|
||||
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
int err;
|
||||
int nr = attr->index;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
|
||||
if (data->remote_unsigned && nr == 2)
|
||||
data->temp11[nr] = TEMP11U_TO_REG(val - data->temp2_offset);
|
||||
else
|
||||
data->temp11[nr] = TEMP11_TO_REG(val - data->temp2_offset);
|
||||
|
||||
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
|
||||
data->temp11[nr] >> 8);
|
||||
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
|
||||
@ -327,35 +454,143 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Hysteresis register holds a relative value, while we want to present
|
||||
an absolute to user-space */
|
||||
static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
|
||||
char *buf)
|
||||
/*
|
||||
* Hysteresis register holds a relative value, while we want to present
|
||||
* an absolute to user-space
|
||||
*/
|
||||
static ssize_t show_temp2_crit_hyst(struct device *dev,
|
||||
struct device_attribute *dummy, char *buf)
|
||||
{
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2])
|
||||
return sprintf(buf, "%d\n", temp8_from_reg(data, 2)
|
||||
+ data->temp2_offset
|
||||
- TEMP8_FROM_REG(data->temp2_crit_hyst));
|
||||
}
|
||||
|
||||
/* And now the other way around, user-space provides an absolute
|
||||
hysteresis value and we have to store a relative one */
|
||||
static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
|
||||
static ssize_t show_lut_temp_hyst(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
|
||||
+ data->temp2_offset
|
||||
- TEMP8_FROM_REG(data->lut_temp_hyst));
|
||||
}
|
||||
|
||||
/*
|
||||
* And now the other way around, user-space provides an absolute
|
||||
* hysteresis value and we have to store a relative one
|
||||
*/
|
||||
static ssize_t set_temp2_crit_hyst(struct device *dev,
|
||||
struct device_attribute *dummy,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
long val = simple_strtol(buf, NULL, 10);
|
||||
long val;
|
||||
int err;
|
||||
long hyst;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
hyst = TEMP8_FROM_REG(data->temp8[2]) + data->temp2_offset - val;
|
||||
hyst = temp8_from_reg(data, 2) + data->temp2_offset - val;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
|
||||
HYST_TO_REG(hyst));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set conversion rate.
|
||||
* client->update_lock must be held when calling this function.
|
||||
*/
|
||||
static void lm63_set_convrate(struct i2c_client *client, struct lm63_data *data,
|
||||
unsigned int interval)
|
||||
{
|
||||
int i;
|
||||
unsigned int update_interval;
|
||||
|
||||
/* Shift calculations to avoid rounding errors */
|
||||
interval <<= 6;
|
||||
|
||||
/* find the nearest update rate */
|
||||
update_interval = (1 << (LM63_MAX_CONVRATE + 6)) * 1000
|
||||
/ data->max_convrate_hz;
|
||||
for (i = 0; i < LM63_MAX_CONVRATE; i++, update_interval >>= 1)
|
||||
if (interval >= update_interval * 3 / 4)
|
||||
break;
|
||||
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
|
||||
data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
|
||||
}
|
||||
|
||||
static ssize_t show_update_interval(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", data->update_interval);
|
||||
}
|
||||
|
||||
static ssize_t set_update_interval(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
lm63_set_convrate(client, data, SENSORS_LIMIT(val, 0, 100000));
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_type(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
|
||||
return sprintf(buf, data->trutherm ? "1\n" : "2\n");
|
||||
}
|
||||
|
||||
static ssize_t set_type(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val != 1 && val != 2)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->trutherm = val == 1;
|
||||
reg = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02;
|
||||
i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM,
|
||||
reg | (data->trutherm ? 0x02 : 0x00));
|
||||
data->valid = 0;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
|
||||
char *buf)
|
||||
{
|
||||
@ -377,27 +612,87 @@ static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
|
||||
set_fan, 1);
|
||||
|
||||
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1);
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
|
||||
static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 11);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 11);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 13);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO,
|
||||
show_lut_temp, NULL, 14);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO,
|
||||
show_lut_temp_hyst, NULL, 14);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
|
||||
set_local_temp8, 1);
|
||||
set_temp8, 1);
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 2);
|
||||
/*
|
||||
* On LM63, temp2_crit can be set only once, which should be job
|
||||
* of the bootloader.
|
||||
*/
|
||||
static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
|
||||
set_temp11, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8,
|
||||
NULL, 2);
|
||||
set_temp8, 2);
|
||||
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
|
||||
set_temp2_crit_hyst);
|
||||
|
||||
static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type);
|
||||
|
||||
/* Individual alarm files */
|
||||
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
|
||||
@ -408,14 +703,43 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
|
||||
/* Raw alarm file for compatibility */
|
||||
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
|
||||
|
||||
static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
|
||||
set_update_interval);
|
||||
|
||||
static struct attribute *lm63_attributes[] = {
|
||||
&dev_attr_pwm1.attr,
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&dev_attr_pwm1_enable.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point5_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_offset.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&dev_attr_temp2_crit_hyst.attr,
|
||||
|
||||
@ -425,10 +749,54 @@ static struct attribute *lm63_attributes[] = {
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
&dev_attr_update_interval.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *lm63_attributes_extra_lut[] = {
|
||||
&sensor_dev_attr_pwm1_auto_point9_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point9_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point9_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point10_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point10_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point10_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point11_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point11_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point11_temp_hyst.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point12_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point12_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point12_temp_hyst.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm63_group_extra_lut = {
|
||||
.attrs = lm63_attributes_extra_lut,
|
||||
};
|
||||
|
||||
/*
|
||||
* On LM63, temp2_crit can be set only once, which should be job
|
||||
* of the bootloader.
|
||||
* On LM64, temp2_crit can always be set.
|
||||
* On LM96163, temp2_crit can be set if bit 1 of the configuration
|
||||
* register is true.
|
||||
*/
|
||||
static umode_t lm63_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
|
||||
if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
|
||||
&& (data->kind == lm64 ||
|
||||
(data->kind == lm96163 && (data->config & 0x02))))
|
||||
return attr->mode | S_IWUSR;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group lm63_group = {
|
||||
.is_visible = lm63_attribute_mode,
|
||||
.attrs = lm63_attributes,
|
||||
};
|
||||
|
||||
@ -487,6 +855,8 @@ static int lm63_detect(struct i2c_client *new_client,
|
||||
strlcpy(info->type, "lm63", I2C_NAME_SIZE);
|
||||
else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
|
||||
strlcpy(info->type, "lm64", I2C_NAME_SIZE);
|
||||
else if (chip_id == 0x49 && address == 0x4c)
|
||||
strlcpy(info->type, "lm96163", I2C_NAME_SIZE);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
@ -518,12 +888,24 @@ static int lm63_probe(struct i2c_client *new_client,
|
||||
lm63_init_client(new_client);
|
||||
|
||||
/* Register sysfs hooks */
|
||||
if ((err = sysfs_create_group(&new_client->dev.kobj,
|
||||
&lm63_group)))
|
||||
err = sysfs_create_group(&new_client->dev.kobj, &lm63_group);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
if (data->config & 0x04) { /* tachometer enabled */
|
||||
if ((err = sysfs_create_group(&new_client->dev.kobj,
|
||||
&lm63_group_fan1)))
|
||||
err = sysfs_create_group(&new_client->dev.kobj,
|
||||
&lm63_group_fan1);
|
||||
if (err)
|
||||
goto exit_remove_files;
|
||||
}
|
||||
if (data->kind == lm96163) {
|
||||
err = device_create_file(&new_client->dev,
|
||||
&dev_attr_temp2_type);
|
||||
if (err)
|
||||
goto exit_remove_files;
|
||||
|
||||
err = sysfs_create_group(&new_client->dev.kobj,
|
||||
&lm63_group_extra_lut);
|
||||
if (err)
|
||||
goto exit_remove_files;
|
||||
}
|
||||
|
||||
@ -538,17 +920,25 @@ static int lm63_probe(struct i2c_client *new_client,
|
||||
exit_remove_files:
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm63_group);
|
||||
sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1);
|
||||
if (data->kind == lm96163) {
|
||||
device_remove_file(&new_client->dev, &dev_attr_temp2_type);
|
||||
sysfs_remove_group(&new_client->dev.kobj,
|
||||
&lm63_group_extra_lut);
|
||||
}
|
||||
exit_free:
|
||||
kfree(data);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Idealy we shouldn't have to initialize anything, since the BIOS
|
||||
should have taken care of everything */
|
||||
/*
|
||||
* Ideally we shouldn't have to initialize anything, since the BIOS
|
||||
* should have taken care of everything
|
||||
*/
|
||||
static void lm63_init_client(struct i2c_client *client)
|
||||
{
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
u8 convrate;
|
||||
|
||||
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
|
||||
data->config_fan = i2c_smbus_read_byte_data(client,
|
||||
@ -561,16 +951,57 @@ static void lm63_init_client(struct i2c_client *client)
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
|
||||
data->config);
|
||||
}
|
||||
/* Tachometer is always enabled on LM64 */
|
||||
if (data->kind == lm64)
|
||||
data->config |= 0x04;
|
||||
|
||||
/* We may need pwm1_freq before ever updating the client data */
|
||||
data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
|
||||
if (data->pwm1_freq == 0)
|
||||
data->pwm1_freq = 1;
|
||||
|
||||
switch (data->kind) {
|
||||
case lm63:
|
||||
case lm64:
|
||||
data->max_convrate_hz = LM63_MAX_CONVRATE_HZ;
|
||||
data->lut_size = 8;
|
||||
break;
|
||||
case lm96163:
|
||||
data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ;
|
||||
data->lut_size = 12;
|
||||
data->trutherm
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_TRUTHERM) & 0x02;
|
||||
break;
|
||||
}
|
||||
convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
|
||||
if (unlikely(convrate > LM63_MAX_CONVRATE))
|
||||
convrate = LM63_MAX_CONVRATE;
|
||||
data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz,
|
||||
convrate);
|
||||
|
||||
/*
|
||||
* For LM96163, check if high resolution PWM
|
||||
* and unsigned temperature format is enabled.
|
||||
*/
|
||||
if (data->kind == lm96163) {
|
||||
u8 config_enhanced
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_CONFIG_ENHANCED);
|
||||
if (config_enhanced & 0x20)
|
||||
data->lut_temp_highres = true;
|
||||
if ((config_enhanced & 0x10)
|
||||
&& !(data->config_fan & 0x08) && data->pwm1_freq == 8)
|
||||
data->pwm_highres = true;
|
||||
if (config_enhanced & 0x08)
|
||||
data->remote_unsigned = true;
|
||||
}
|
||||
|
||||
/* Show some debug info about the LM63 configuration */
|
||||
dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
|
||||
(data->config & 0x04) ? "tachometer input" :
|
||||
"alert output");
|
||||
if (data->kind == lm63)
|
||||
dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
|
||||
(data->config & 0x04) ? "tachometer input" :
|
||||
"alert output");
|
||||
dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
|
||||
(data->config_fan & 0x08) ? "1.4" : "360",
|
||||
((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
|
||||
@ -586,6 +1017,10 @@ static int lm63_remove(struct i2c_client *client)
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm63_group);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
|
||||
if (data->kind == lm96163) {
|
||||
device_remove_file(&client->dev, &dev_attr_temp2_type);
|
||||
sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
@ -595,10 +1030,15 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct lm63_data *data = i2c_get_clientdata(client);
|
||||
unsigned long next_update;
|
||||
int i;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
next_update = data->last_updated
|
||||
+ msecs_to_jiffies(data->update_interval) + 1;
|
||||
|
||||
if (time_after(jiffies, next_update) || !data->valid) {
|
||||
if (data->config & 0x04) { /* tachometer enabled */
|
||||
/* order matters for fan1_input */
|
||||
data->fan[0] = i2c_smbus_read_byte_data(client,
|
||||
@ -615,8 +1055,8 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
||||
LM63_REG_PWM_FREQ);
|
||||
if (data->pwm1_freq == 0)
|
||||
data->pwm1_freq = 1;
|
||||
data->pwm1_value = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_PWM_VALUE);
|
||||
data->pwm1[0] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_PWM_VALUE);
|
||||
|
||||
data->temp8[0] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_TEMP);
|
||||
@ -636,6 +1076,17 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
||||
LM63_REG_REMOTE_HIGH_MSB) << 8)
|
||||
| i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_REMOTE_HIGH_LSB);
|
||||
data->temp11[3] = (i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_REMOTE_OFFSET_MSB) << 8)
|
||||
| i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_REMOTE_OFFSET_LSB);
|
||||
|
||||
if (data->kind == lm96163)
|
||||
data->temp11u = (i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
|
||||
| i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_REMOTE_TEMP_U_LSB);
|
||||
|
||||
data->temp8[2] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_REMOTE_TCRIT);
|
||||
data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
|
||||
@ -648,6 +1099,21 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
|
||||
!data->lut_valid) {
|
||||
for (i = 0; i < data->lut_size; i++) {
|
||||
data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LUT_PWM(i));
|
||||
data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LUT_TEMP(i));
|
||||
}
|
||||
data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LUT_TEMP_HYST);
|
||||
|
||||
data->lut_last_updated = jiffies;
|
||||
data->lut_valid = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
|
@ -917,7 +917,7 @@ static ssize_t set_update_interval(struct device *dev,
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
lm90_set_convrate(client, data, val);
|
||||
lm90_set_convrate(client, data, SENSORS_LIMIT(val, 0, 100000));
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
|
@ -106,11 +106,14 @@ static ssize_t show_adc(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret);
|
||||
/* assume the reference voltage to be 2.048V, with an 8-bit sample,
|
||||
* the LSB weight is 8mV
|
||||
*/
|
||||
return sprintf(buf, "%d\n", ret * 8);
|
||||
}
|
||||
|
||||
#define MAX1111_ADC_ATTR(_id) \
|
||||
SENSOR_DEVICE_ATTR(adc##_id##_in, S_IRUGO, show_adc, NULL, _id)
|
||||
SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
|
||||
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
|
||||
static MAX1111_ADC_ATTR(0);
|
||||
@ -120,10 +123,10 @@ static MAX1111_ADC_ATTR(3);
|
||||
|
||||
static struct attribute *max1111_attributes[] = {
|
||||
&dev_attr_name.attr,
|
||||
&sensor_dev_attr_adc0_in.dev_attr.attr,
|
||||
&sensor_dev_attr_adc1_in.dev_attr.attr,
|
||||
&sensor_dev_attr_adc2_in.dev_attr.attr,
|
||||
&sensor_dev_attr_adc3_in.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user