mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-02-20 15:26:48 +07:00
hwmon: (lm63) Expose automatic fan speed control lookup table
The LM63 and compatible devices have a lookup table to control the fan speed automatically. Expose it in sysfs. Values are cached for 5 seconds, independently of the other register values to avoid slowing down "sensors". We might make the table values writable in the future. Signed-off-by: Jean Delvare <khali@linux-fr.org> Tested-by: Guenter Roeck <guenter.roeck@ericsson.com> Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
This commit is contained in:
parent
d93ab78070
commit
d216f6809e
@ -66,7 +66,8 @@ supported either.
|
|||||||
|
|
||||||
The lm63 driver will not update its values more frequently than configured with
|
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,
|
the update_interval sysfs attribute; reading them more often will do no harm,
|
||||||
but will return 'old' values.
|
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
|
The LM64 is effectively an LM63 with GPIO lines. The driver does not
|
||||||
support these GPIO lines at present.
|
support these GPIO lines at present.
|
||||||
|
@ -75,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_VALUE 0x4C
|
||||||
#define LM63_REG_PWM_FREQ 0x4D
|
#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_TEMP 0x00
|
||||||
#define LM63_REG_LOCAL_HIGH 0x05
|
#define LM63_REG_LOCAL_HIGH 0x05
|
||||||
@ -192,7 +195,9 @@ struct lm63_data {
|
|||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
struct mutex update_lock;
|
struct mutex update_lock;
|
||||||
char valid; /* zero until following fields are valid */
|
char valid; /* zero until following fields are valid */
|
||||||
|
char lut_valid; /* zero until lut fields are valid */
|
||||||
unsigned long last_updated; /* in jiffies */
|
unsigned long last_updated; /* in jiffies */
|
||||||
|
unsigned long lut_last_updated; /* in jiffies */
|
||||||
enum chips kind;
|
enum chips kind;
|
||||||
int temp2_offset;
|
int temp2_offset;
|
||||||
|
|
||||||
@ -204,18 +209,22 @@ struct lm63_data {
|
|||||||
u16 fan[2]; /* 0: input
|
u16 fan[2]; /* 0: input
|
||||||
1: low limit */
|
1: low limit */
|
||||||
u8 pwm1_freq;
|
u8 pwm1_freq;
|
||||||
u8 pwm1_value;
|
u8 pwm1[9]; /* 0: current output
|
||||||
s8 temp8[3]; /* 0: local input
|
1-8: lookup table */
|
||||||
|
s8 temp8[11]; /* 0: local input
|
||||||
1: local high limit
|
1: local high limit
|
||||||
2: remote critical limit */
|
2: remote critical limit
|
||||||
|
3-10: lookup table */
|
||||||
s16 temp11[4]; /* 0: remote input
|
s16 temp11[4]; /* 0: remote input
|
||||||
1: remote low limit
|
1: remote low limit
|
||||||
2: remote high limit
|
2: remote high limit
|
||||||
3: remote offset */
|
3: remote offset */
|
||||||
u16 temp11u; /* remote input (unsigned) */
|
u16 temp11u; /* remote input (unsigned) */
|
||||||
u8 temp2_crit_hyst;
|
u8 temp2_crit_hyst;
|
||||||
|
u8 lut_temp_hyst;
|
||||||
u8 alarms;
|
u8 alarms;
|
||||||
bool pwm_highres;
|
bool pwm_highres;
|
||||||
|
bool lut_temp_highres;
|
||||||
bool remote_unsigned; /* true if unsigned remote upper limits */
|
bool remote_unsigned; /* true if unsigned remote upper limits */
|
||||||
bool trutherm;
|
bool trutherm;
|
||||||
};
|
};
|
||||||
@ -227,6 +236,11 @@ static inline int temp8_from_reg(struct lm63_data *data, int nr)
|
|||||||
return TEMP8_FROM_REG(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
|
* Sysfs callback functions and files
|
||||||
*/
|
*/
|
||||||
@ -261,17 +275,19 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
|
|||||||
return count;
|
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)
|
char *buf)
|
||||||
{
|
{
|
||||||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||||
struct lm63_data *data = lm63_update_device(dev);
|
struct lm63_data *data = lm63_update_device(dev);
|
||||||
|
int nr = attr->index;
|
||||||
int pwm;
|
int pwm;
|
||||||
|
|
||||||
if (data->pwm_highres)
|
if (data->pwm_highres)
|
||||||
pwm = data->pwm1_value;
|
pwm = data->pwm1[nr];
|
||||||
else
|
else
|
||||||
pwm = data->pwm1_value >= 2 * data->pwm1_freq ?
|
pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
|
||||||
255 : (data->pwm1_value * 255 + data->pwm1_freq) /
|
255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
|
||||||
(2 * data->pwm1_freq);
|
(2 * data->pwm1_freq);
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", pwm);
|
return sprintf(buf, "%d\n", pwm);
|
||||||
@ -294,9 +310,9 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
|
|||||||
|
|
||||||
val = SENSORS_LIMIT(val, 0, 255);
|
val = SENSORS_LIMIT(val, 0, 255);
|
||||||
mutex_lock(&data->update_lock);
|
mutex_lock(&data->update_lock);
|
||||||
data->pwm1_value = data->pwm_highres ? val :
|
data->pwm1[0] = data->pwm_highres ? val :
|
||||||
(val * data->pwm1_freq * 2 + 127) / 255;
|
(val * data->pwm1_freq * 2 + 127) / 255;
|
||||||
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
|
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]);
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -333,6 +349,16 @@ static ssize_t show_remote_temp8(struct device *dev,
|
|||||||
+ data->temp2_offset);
|
+ data->temp2_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
@ -440,6 +466,17 @@ static ssize_t show_temp2_crit_hyst(struct device *dev,
|
|||||||
- TEMP8_FROM_REG(data->temp2_crit_hyst));
|
- TEMP8_FROM_REG(data->temp2_crit_hyst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* And now the other way around, user-space provides an absolute
|
||||||
* hysteresis value and we have to store a relative one
|
* hysteresis value and we have to store a relative one
|
||||||
@ -574,8 +611,48 @@ static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
|
|||||||
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
|
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
|
||||||
set_fan, 1);
|
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 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(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
|
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,
|
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
|
||||||
@ -609,8 +686,33 @@ static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
|
|||||||
set_update_interval);
|
set_update_interval);
|
||||||
|
|
||||||
static struct attribute *lm63_attributes[] = {
|
static struct attribute *lm63_attributes[] = {
|
||||||
&dev_attr_pwm1.attr,
|
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||||
&dev_attr_pwm1_enable.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_temp1_input.dev_attr.attr,
|
||||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||||
@ -834,6 +936,8 @@ static void lm63_init_client(struct i2c_client *client)
|
|||||||
u8 config_enhanced
|
u8 config_enhanced
|
||||||
= i2c_smbus_read_byte_data(client,
|
= i2c_smbus_read_byte_data(client,
|
||||||
LM96163_REG_CONFIG_ENHANCED);
|
LM96163_REG_CONFIG_ENHANCED);
|
||||||
|
if (config_enhanced & 0x20)
|
||||||
|
data->lut_temp_highres = true;
|
||||||
if ((config_enhanced & 0x10)
|
if ((config_enhanced & 0x10)
|
||||||
&& !(data->config_fan & 0x08) && data->pwm1_freq == 8)
|
&& !(data->config_fan & 0x08) && data->pwm1_freq == 8)
|
||||||
data->pwm_highres = true;
|
data->pwm_highres = true;
|
||||||
@ -872,6 +976,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct lm63_data *data = i2c_get_clientdata(client);
|
struct lm63_data *data = i2c_get_clientdata(client);
|
||||||
unsigned long next_update;
|
unsigned long next_update;
|
||||||
|
int i;
|
||||||
|
|
||||||
mutex_lock(&data->update_lock);
|
mutex_lock(&data->update_lock);
|
||||||
|
|
||||||
@ -895,8 +1000,8 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||||||
LM63_REG_PWM_FREQ);
|
LM63_REG_PWM_FREQ);
|
||||||
if (data->pwm1_freq == 0)
|
if (data->pwm1_freq == 0)
|
||||||
data->pwm1_freq = 1;
|
data->pwm1_freq = 1;
|
||||||
data->pwm1_value = i2c_smbus_read_byte_data(client,
|
data->pwm1[0] = i2c_smbus_read_byte_data(client,
|
||||||
LM63_REG_PWM_VALUE);
|
LM63_REG_PWM_VALUE);
|
||||||
|
|
||||||
data->temp8[0] = i2c_smbus_read_byte_data(client,
|
data->temp8[0] = i2c_smbus_read_byte_data(client,
|
||||||
LM63_REG_LOCAL_TEMP);
|
LM63_REG_LOCAL_TEMP);
|
||||||
@ -939,6 +1044,21 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||||||
data->valid = 1;
|
data->valid = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
|
||||||
|
!data->lut_valid) {
|
||||||
|
for (i = 0; i < 8; 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);
|
mutex_unlock(&data->update_lock);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
Loading…
Reference in New Issue
Block a user