summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorAndrew Jeffery <andrew@aj.id.au>2018-04-03 17:26:55 +0300
committerJoel Stanley <joel@jms.id.au>2019-09-16 06:30:01 +0300
commit7c084e3f5d50512be0243f8d1fe2954ce9a766a8 (patch)
tree9c0b150cc473d4f1f6fce0fd4160ab466480ebad /drivers/hwmon
parent45c6aec8c6a1ddcb9b4836c279df37cd7f40dda4 (diff)
downloadlinux-7c084e3f5d50512be0243f8d1fe2954ce9a766a8.tar.xz
pmbus (max31785): Wrap all I2C accessors in one-shot failure handlers
The MAX31785(A) has shown erratic behaviour across multiple system designs, unexpectedly clock stretching and NAKing transactions. Perform a one-shot retry if necessary for all access attempts. OpenBMC-Staging-Count: 9 Signed-off-by: Andrew Jeffery <andrew@aj.id.au> Tested-by: George Keishing <gkeishin@in.ibm.com> Signed-off-by: Joel Stanley <joel@jms.id.au>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/pmbus/max31785.c207
1 files changed, 165 insertions, 42 deletions
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
index 0b31b0f512a3..209cb559e549 100644
--- a/drivers/hwmon/pmbus/max31785.c
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -33,29 +33,105 @@ enum max31785_regs {
#define MAX31785_NR_PAGES 23
#define MAX31785_NR_FAN_PAGES 6
+/*
+ * MAX31785 dragons ahead
+ *
+ * We see weird issues where some transfers fail. There doesn't appear to be
+ * any pattern to the problem, so below we wrap all the read/write calls with a
+ * retry. The device provides no indication of this besides NACK'ing master
+ * Txs; no bits are set in STATUS_BYTE to suggest anything has gone wrong.
+ */
+
+#define max31785_retry(_func, ...) ({ \
+ /* All relevant functions return int, sue me */ \
+ int _ret = _func(__VA_ARGS__); \
+ if (_ret == -EIO) \
+ _ret = _func(__VA_ARGS__); \
+ _ret; \
+})
+
+static int max31785_i2c_smbus_read_byte_data(struct i2c_client *client,
+ int command)
+{
+ return max31785_retry(i2c_smbus_read_byte_data, client, command);
+}
+
+
+static int max31785_i2c_smbus_write_byte_data(struct i2c_client *client,
+ int command, u16 data)
+{
+ return max31785_retry(i2c_smbus_write_byte_data, client, command, data);
+}
+
+static int max31785_i2c_smbus_read_word_data(struct i2c_client *client,
+ int command)
+{
+ return max31785_retry(i2c_smbus_read_word_data, client, command);
+}
+
+static int max31785_i2c_smbus_write_word_data(struct i2c_client *client,
+ int command, u16 data)
+{
+ return max31785_retry(i2c_smbus_write_word_data, client, command, data);
+}
+
+static int max31785_pmbus_write_byte(struct i2c_client *client, int page,
+ u8 value)
+{
+ return max31785_retry(pmbus_write_byte, client, page, value);
+}
+
+static int max31785_pmbus_read_byte_data(struct i2c_client *client, int page,
+ int command)
+{
+ return max31785_retry(pmbus_read_byte_data, client, page, command);
+}
+
+static int max31785_pmbus_write_byte_data(struct i2c_client *client, int page,
+ int command, u16 data)
+{
+ return max31785_retry(pmbus_write_byte_data, client, page, command,
+ data);
+}
+
+static int max31785_pmbus_read_word_data(struct i2c_client *client, int page,
+ int command)
+{
+ return max31785_retry(pmbus_read_word_data, client, page, command);
+}
+
+static int max31785_pmbus_write_word_data(struct i2c_client *client, int page,
+ int command, u16 data)
+{
+ return max31785_retry(pmbus_write_word_data, client, page, command,
+ data);
+}
+
static int max31785_read_byte_data(struct i2c_client *client, int page,
int reg)
{
- if (page < MAX31785_NR_PAGES)
- return -ENODATA;
-
switch (reg) {
case PMBUS_VOUT_MODE:
- return -ENOTSUPP;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
+ break;
case PMBUS_FAN_CONFIG_12:
- return pmbus_read_byte_data(client, page - MAX31785_NR_PAGES,
- reg);
+ if (page >= MAX31785_NR_PAGES)
+ return max31785_pmbus_read_byte_data(client,
+ page - MAX31785_NR_PAGES,
+ reg);
+ break;
}
- return -ENODATA;
+ return max31785_pmbus_read_byte_data(client, page, reg);
}
static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
{
- if (page < MAX31785_NR_PAGES)
- return -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
- return -ENOTSUPP;
+ return max31785_pmbus_write_byte(client, page, value);
}
static int max31785_read_long_data(struct i2c_client *client, int page,
@@ -116,11 +192,13 @@ static int max31785_get_pwm_mode(struct i2c_client *client, int page)
int config;
int command;
- config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ config = max31785_pmbus_read_byte_data(client, page,
+ PMBUS_FAN_CONFIG_12);
if (config < 0)
return config;
- command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
+ command = max31785_pmbus_read_word_data(client, page,
+ PMBUS_FAN_COMMAND_1);
if (command < 0)
return command;
@@ -144,15 +222,14 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_READ_FAN_SPEED_1:
if (page < MAX31785_NR_PAGES)
- return -ENODATA;
+ return max31785_pmbus_read_word_data(client, page, reg);
rv = max31785_read_long_data(client, page - MAX31785_NR_PAGES,
reg, &val);
if (rv < 0)
return rv;
- rv = (val >> 16) & 0xffff;
- break;
+ return (val >> 16) & 0xffff;
case PMBUS_FAN_COMMAND_1:
/*
* PMBUS_FAN_COMMAND_x is probed to judge whether or not to
@@ -160,20 +237,28 @@ static int max31785_read_word_data(struct i2c_client *client, int page,
*
* Don't expose fan_target attribute for virtual pages.
*/
- rv = (page >= MAX31785_NR_PAGES) ? -ENOTSUPP : -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
break;
+ case PMBUS_VIRT_FAN_TARGET_1:
+ if (page >= MAX31785_NR_PAGES)
+ return -ENOTSUPP;
+
+ return -ENODATA;
case PMBUS_VIRT_PWM_1:
- rv = max31785_get_pwm(client, page);
- break;
+ return max31785_get_pwm(client, page);
case PMBUS_VIRT_PWM_ENABLE_1:
- rv = max31785_get_pwm_mode(client, page);
- break;
+ return max31785_get_pwm_mode(client, page);
default:
- rv = -ENODATA;
+ if (page >= MAX31785_NR_PAGES)
+ return -ENXIO;
break;
}
- return rv;
+ if (reg >= PMBUS_VIRT_BASE)
+ return -ENXIO;
+
+ return max31785_pmbus_read_word_data(client, page, reg);
}
static inline u32 max31785_scale_pwm(u32 sensor_val)
@@ -197,6 +282,31 @@ static inline u32 max31785_scale_pwm(u32 sensor_val)
return (sensor_val * 100) / 255;
}
+static int max31785_update_fan(struct i2c_client *client, int page,
+ u8 config, u8 mask, u16 command)
+{
+ int from, rv;
+ u8 to;
+
+ from = max31785_pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
+ if (from < 0)
+ return from;
+
+ to = (from & ~mask) | (config & mask);
+
+ if (to != from) {
+ rv = max31785_pmbus_write_byte_data(client, page,
+ PMBUS_FAN_CONFIG_12, to);
+ if (rv < 0)
+ return rv;
+ }
+
+ rv = max31785_pmbus_write_word_data(client, page, PMBUS_FAN_COMMAND_1,
+ command);
+
+ return rv;
+}
+
static int max31785_pwm_enable(struct i2c_client *client, int page,
u16 word)
{
@@ -226,15 +336,18 @@ static int max31785_pwm_enable(struct i2c_client *client, int page,
return -EINVAL;
}
- return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
+ return max31785_update_fan(client, page, config, PB_FAN_1_RPM, rate);
}
static int max31785_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
switch (reg) {
+ case PMBUS_VIRT_FAN_TARGET_1:
+ return max31785_update_fan(client, page, PB_FAN_1_RPM,
+ PB_FAN_1_RPM, word);
case PMBUS_VIRT_PWM_1:
- return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
+ return max31785_update_fan(client, page, 0, PB_FAN_1_RPM,
max31785_scale_pwm(word));
case PMBUS_VIRT_PWM_ENABLE_1:
return max31785_pwm_enable(client, page, word);
@@ -242,7 +355,10 @@ static int max31785_write_word_data(struct i2c_client *client, int page,
break;
}
- return -ENODATA;
+ if (reg < PMBUS_VIRT_BASE)
+ return max31785_pmbus_write_word_data(client, page, reg, word);
+
+ return -ENXIO;
}
/*
@@ -275,11 +391,11 @@ static int max31785_of_fan_config(struct i2c_client *client,
return -ENXIO;
}
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (ret < 0)
return ret;
- pb_cfg = i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);
+ pb_cfg = max31785_i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);
if (pb_cfg < 0)
return pb_cfg;
@@ -305,7 +421,9 @@ static int max31785_of_fan_config(struct i2c_client *client,
} else if (!strcmp("lock", sval)) {
mfr_cfg |= MFR_FAN_CONFIG_ROTOR;
- ret = i2c_smbus_write_word_data(client, MFR_FAN_FAULT_LIMIT, 1);
+ ret = max31785_i2c_smbus_write_word_data(client,
+ MFR_FAN_FAULT_LIMIT,
+ 1);
if (ret < 0)
return ret;
@@ -419,21 +537,23 @@ static int max31785_of_fan_config(struct i2c_client *client,
if (of_property_read_bool(child, "maxim,fan-fault-pin-mon"))
mfr_fault_resp |= MFR_FAULT_RESPONSE_MONITOR;
- ret = i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
pb_cfg & ~PB_FAN_1_INSTALLED);
if (ret < 0)
return ret;
- ret = i2c_smbus_write_word_data(client, MFR_FAN_CONFIG, mfr_cfg);
+ ret = max31785_i2c_smbus_write_word_data(client, MFR_FAN_CONFIG,
+ mfr_cfg);
if (ret < 0)
return ret;
- ret = i2c_smbus_write_byte_data(client, MFR_FAULT_RESPONSE,
- mfr_fault_resp);
+ ret = max31785_i2c_smbus_write_byte_data(client, MFR_FAULT_RESPONSE,
+ mfr_fault_resp);
if (ret < 0)
return ret;
- ret = i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12, pb_cfg);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_FAN_CONFIG_12,
+ pb_cfg);
if (ret < 0)
return ret;
@@ -478,7 +598,7 @@ static int max31785_of_tmp_config(struct i2c_client *client,
return -ENXIO;
}
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (ret < 0)
return ret;
@@ -502,7 +622,7 @@ static int max31785_of_tmp_config(struct i2c_client *client,
i++;
}
- ret = i2c_smbus_write_word_data(client, MFR_TEMP_SENSOR_CONFIG,
+ ret = max31785_i2c_smbus_write_word_data(client, MFR_TEMP_SENSOR_CONFIG,
mfr_tmp_cfg);
if (ret < 0)
return ret;
@@ -581,11 +701,11 @@ static int max31785_configure_dual_tach(struct i2c_client *client,
int i;
for (i = 0; i < MAX31785_NR_FAN_PAGES; i++) {
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
if (ret < 0)
return ret;
- ret = i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
+ ret = max31785_i2c_smbus_read_word_data(client, MFR_FAN_CONFIG);
if (ret < 0)
return ret;
@@ -623,7 +743,7 @@ static int max31785_probe(struct i2c_client *client,
*info = max31785_info;
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
if (ret < 0)
return ret;
@@ -665,17 +785,20 @@ static int max31785_probe(struct i2c_client *client,
if (!have_fan || fan_configured)
continue;
- ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i);
+ ret = max31785_i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+ i);
if (ret < 0)
return ret;
- ret = i2c_smbus_read_byte_data(client, PMBUS_FAN_CONFIG_12);
+ ret = max31785_i2c_smbus_read_byte_data(client,
+ PMBUS_FAN_CONFIG_12);
if (ret < 0)
return ret;
ret &= ~PB_FAN_1_INSTALLED;
- ret = i2c_smbus_write_word_data(client, PMBUS_FAN_CONFIG_12,
- ret);
+ ret = max31785_i2c_smbus_write_word_data(client,
+ PMBUS_FAN_CONFIG_12,
+ ret);
if (ret < 0)
return ret;
}