summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorZhikui Ren <zhikui.ren@intel.com>2022-01-21 04:26:31 +0300
committerZhikui Ren <zhikui.ren@intel.com>2022-03-10 01:11:54 +0300
commitf99c8aa5142aaa83cb19aaacf2166dafbb3828aa (patch)
tree78e42bbe8dc792c1e3087a032a2997c43b171cb7 /drivers/hwmon
parent4e36b22c7f85525a2fd7a4afecc12f7d3c84de04 (diff)
downloadlinux-f99c8aa5142aaa83cb19aaacf2166dafbb3828aa.tar.xz
hwmon: (pmbus) auto detect pmbus fan pwm mode
PMBus device with fan controls have two possible control modes RPM and PWM. RPM mode is assumed and FANx_TARGET sensor is always created to provide read and write access of fan speed control. PWM only devices have been able to use the FANx_TARGET sensor to read commanded fan speed correctly because the pmbus core driver caches the write value and return it when the TARGET sensor is read. Recent pmbus_core driver change clears the cached value once the write to FANx_TARGET completes. Read FANx_TARGET sensor now reads from actual PMBus register. As a result, RPM mode only device, FANx_TARGET sensor always read 0. Add detection of current fan control mode during pmbus_identify and set flag PMBUS_HAVE_PWM12 and/or PMBUS_HAVE_PWM34 if PWM mode is detected. PWMx and PWMx_ENABLE sensors will be created based on these flags. Fixes: 1ae5aaf5d1c5 ("hwmon: (pmbus) Clear sensor data after chip write" Signed-off-by: Zhikui Ren <zhikui.ren@intel.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/pmbus/pmbus.c8
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c58
2 files changed, 64 insertions, 2 deletions
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index d0d386990af5..ce2f020f09d7 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -29,6 +29,7 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int page;
+ int fan_mode;
/* Sensors detected on page 0 only */
if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN))
@@ -47,13 +48,20 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,
info->func[0] |= PMBUS_HAVE_FAN12;
if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12))
info->func[0] |= PMBUS_HAVE_STATUS_FAN12;
+ fan_mode = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ if ((fan_mode & (PB_FAN_1_RPM | PB_FAN_2_RPM)) != (PB_FAN_1_RPM | PB_FAN_2_RPM))
+ info->func[0] |= PMBUS_HAVE_PWM12;
}
if (pmbus_check_byte_register(client, 0, PMBUS_FAN_CONFIG_34) &&
pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) {
info->func[0] |= PMBUS_HAVE_FAN34;
if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34))
info->func[0] |= PMBUS_HAVE_STATUS_FAN34;
+ fan_mode = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_34);
+ if ((fan_mode & (PB_FAN_1_RPM | PB_FAN_2_RPM)) != (PB_FAN_1_RPM | PB_FAN_2_RPM))
+ info->func[0] |= PMBUS_HAVE_PWM34;
}
+
if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1))
info->func[0] |= PMBUS_HAVE_TEMP;
if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_2))
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index ec44e4847c48..ceb1b4db985e 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -241,12 +241,32 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
}
EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
+static int pmbus_update_fan_config(struct i2c_client *client, int page, int id,
+ u8 config, u8 mask)
+{
+ int from;
+ int rv;
+ u8 to;
+
+ from = pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]);
+ if (from < 0)
+ return from;
+
+ to = (from & ~mask) | (config & mask);
+ if (to != from) {
+ rv = pmbus_write_byte_data(client, page, pmbus_fan_config_registers[id], to);
+ if (rv < 0)
+ return rv;
+ }
+ return 0;
+}
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
u16 word)
{
int bit;
int id;
+ int config;
int rv;
switch (reg) {
@@ -255,6 +275,18 @@ static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
bit = pmbus_fan_rpm_mask[id];
rv = pmbus_update_fan(client, page, id, bit, bit, word);
break;
+ case PMBUS_VIRT_PWM_1 ... PMBUS_VIRT_PWM_4:
+ id = reg - PMBUS_VIRT_PWM_1;
+ bit = pmbus_fan_rpm_mask[id];
+ /* 0 is pwm mode */
+ rv = pmbus_update_fan(client, page, id, 0, bit, word);
+ break;
+ case PMBUS_VIRT_PWM_ENABLE_1 ... PMBUS_VIRT_PWM_ENABLE_4:
+ id = reg - PMBUS_VIRT_PWM_ENABLE_1;
+ bit = pmbus_fan_rpm_mask[id];
+ config = word ? bit : 0;
+ rv = pmbus_update_fan_config(client, page, id, config, bit);
+ break;
default:
rv = -ENXIO;
break;
@@ -293,8 +325,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
int rv;
u8 to;
- from = pmbus_read_byte_data(client, page,
- pmbus_fan_config_registers[id]);
+ from = pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]);
if (from < 0)
return from;
@@ -323,16 +354,39 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
}
EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
+static int pmbus_get_fan_config(struct i2c_client *client, int page, int id, u8 mask)
+{
+ int from;
+
+ from = pmbus_read_byte_data(client, page, pmbus_fan_config_registers[id]);
+ if (from < 0)
+ return from;
+
+ return from & mask;
+}
+
static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
{
int rv;
int id;
+ int bit;
switch (reg) {
case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
id = reg - PMBUS_VIRT_FAN_TARGET_1;
rv = pmbus_get_fan_rate_device(client, page, id, rpm);
break;
+ case PMBUS_VIRT_PWM_1 ... PMBUS_VIRT_PWM_4:
+ id = reg - PMBUS_VIRT_PWM_1;
+ rv = pmbus_get_fan_rate_device(client, page, id, percent);
+ break;
+ case PMBUS_VIRT_PWM_ENABLE_1 ... PMBUS_VIRT_PWM_ENABLE_4:
+ id = reg - PMBUS_VIRT_PWM_ENABLE_1;
+ bit = pmbus_fan_rpm_mask[id];
+ rv = pmbus_get_fan_config(client, page, id, bit);
+ if (rv >= 0)
+ rv = !rv; /* PWM is enabled when rpm bit is not set (rv = 0) */
+ break;
default:
rv = -ENXIO;
break;