diff options
author | Andrew Jeffery <andrew@aj.id.au> | 2018-04-03 17:26:55 +0300 |
---|---|---|
committer | Joel Stanley <joel@jms.id.au> | 2019-09-16 06:30:01 +0300 |
commit | 7c084e3f5d50512be0243f8d1fe2954ce9a766a8 (patch) | |
tree | 9c0b150cc473d4f1f6fce0fd4160ab466480ebad /drivers/hwmon | |
parent | 45c6aec8c6a1ddcb9b4836c279df37cd7f40dda4 (diff) | |
download | linux-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.c | 207 |
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; } |