hwmon: (lm90) Improve PEC support

PEC (packet error checking) support for ADM1032 is currently only enabled
if the chip was auto-detected, but not if a chip is instantiated
explicitly. Always enable PEC support by introducing a chip feature flag
indicating partial PEC support. Also, for consistency, disable PEC support
by default to match existing functionality if the chip was not auto-
detected.

At the same time, introduce generic support for PEC with a separate feature
flag. This will be used when support for chips with full PEC functionality
is added.

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
Guenter Roeck 2021-10-31 09:34:33 -07:00
parent f68480cc32
commit 3b0982ff93
2 changed files with 32 additions and 30 deletions

View File

@ -423,6 +423,6 @@ two transactions will typically mean twice as much delay waiting for
transaction completion, effectively doubling the register cache refresh time. transaction completion, effectively doubling the register cache refresh time.
I guess reliability comes at a price, but it's quite expensive this time. I guess reliability comes at a price, but it's quite expensive this time.
So, as not everyone might enjoy the slowdown, PEC can be disabled through So, as not everyone might enjoy the slowdown, PEC is disabled by default and
sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1 can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be
to that file to enable PEC again. enabled. Write 0 to that file to disable PEC again.

View File

@ -179,6 +179,8 @@ enum chips { adm1032, adt7461, g781, lm86, lm90, lm99,
#define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */ #define LM90_PAUSE_FOR_CONFIG BIT(9) /* Pause conversion for config */
#define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */ #define LM90_HAVE_CRIT BIT(10) /* Chip supports CRIT/OVERT register */
#define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */ #define LM90_HAVE_CRIT_ALRM_SWP BIT(11) /* critical alarm bits swapped */
#define LM90_HAVE_PEC BIT(12) /* Chip supports PEC */
#define LM90_HAVE_PARTIAL_PEC BIT(13) /* Partial PEC support (adm1032)*/
/* LM90 status */ /* LM90 status */
#define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */ #define LM90_STATUS_LTHRM BIT(0) /* local THERM limit tripped */
@ -346,7 +348,8 @@ struct lm90_params {
static const struct lm90_params lm90_params[] = { static const struct lm90_params lm90_params[] = {
[adm1032] = { [adm1032] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
| LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT, | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_CRIT
| LM90_HAVE_PARTIAL_PEC,
.alert_alarms = 0x7c, .alert_alarms = 0x7c,
.max_convrate = 10, .max_convrate = 10,
}, },
@ -519,10 +522,10 @@ struct lm90_data {
*/ */
/* /*
* The ADM1032 supports PEC but not on write byte transactions, so we need * If the chip supports PEC but not on write byte transactions, we need
* to explicitly ask for a transaction without PEC. * to explicitly ask for a transaction without PEC.
*/ */
static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value) static inline s32 lm90_write_no_pec(struct i2c_client *client, u8 value)
{ {
return i2c_smbus_xfer(client->adapter, client->addr, return i2c_smbus_xfer(client->adapter, client->addr,
client->flags & ~I2C_CLIENT_PEC, client->flags & ~I2C_CLIENT_PEC,
@ -531,22 +534,24 @@ static inline s32 adm1032_write_byte(struct i2c_client *client, u8 value)
/* /*
* It is assumed that client->update_lock is held (unless we are in * It is assumed that client->update_lock is held (unless we are in
* detection or initialization steps). This matters when PEC is enabled, * detection or initialization steps). This matters when PEC is enabled
* because we don't want the address pointer to change between the write * for chips with partial PEC support, because we don't want the address
* byte and the read byte transactions. * pointer to change between the write byte and the read byte transactions.
*/ */
static int lm90_read_reg(struct i2c_client *client, u8 reg) static int lm90_read_reg(struct i2c_client *client, u8 reg)
{ {
struct lm90_data *data = i2c_get_clientdata(client);
bool partial_pec = (client->flags & I2C_CLIENT_PEC) &&
(data->flags & LM90_HAVE_PARTIAL_PEC);
int err; int err;
if (client->flags & I2C_CLIENT_PEC) { if (partial_pec) {
err = adm1032_write_byte(client, reg); err = lm90_write_no_pec(client, reg);
if (err >= 0) if (err)
err = i2c_smbus_read_byte(client);
} else
err = i2c_smbus_read_byte_data(client, reg);
return err; return err;
return i2c_smbus_read_byte(client);
}
return i2c_smbus_read_byte_data(client, reg);
} }
/* /*
@ -1135,7 +1140,7 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
return (val + 125) / 250 * 64; return (val + 125) / 250 * 64;
} }
/* pec used for ADM1032 only */ /* pec used for devices with PEC support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf) char *buf)
{ {
@ -1675,13 +1680,6 @@ static int lm90_detect(struct i2c_client *client,
&& (config1 & 0x3F) == 0x00 && (config1 & 0x3F) == 0x00
&& convrate <= 0x0A) { && convrate <= 0x0A) {
name = "adm1032"; name = "adm1032";
/*
* The ADM1032 supports PEC, but only if combined
* transactions are not used.
*/
if (i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE))
info->flags |= I2C_CLIENT_PEC;
} else } else
if (chip_id == 0x51 /* ADT7461 */ if (chip_id == 0x51 /* ADT7461 */
&& (config1 & 0x1B) == 0x00 && (config1 & 0x1B) == 0x00
@ -2005,10 +2003,6 @@ static int lm90_probe(struct i2c_client *client)
data->kind = (enum chips)of_device_get_match_data(&client->dev); data->kind = (enum chips)of_device_get_match_data(&client->dev);
else else
data->kind = i2c_match_id(lm90_id, client)->driver_data; data->kind = i2c_match_id(lm90_id, client)->driver_data;
if (data->kind == adm1032) {
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
client->flags &= ~I2C_CLIENT_PEC;
}
/* /*
* Different devices have different alarm bits triggering the * Different devices have different alarm bits triggering the
@ -2019,6 +2013,14 @@ static int lm90_probe(struct i2c_client *client)
/* Set chip capabilities */ /* Set chip capabilities */
data->flags = lm90_params[data->kind].flags; data->flags = lm90_params[data->kind].flags;
if ((data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_PEC))
data->flags &= ~(LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC);
if ((data->flags & LM90_HAVE_PARTIAL_PEC) &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
data->flags &= ~LM90_HAVE_PARTIAL_PEC;
data->chip.ops = &lm90_ops; data->chip.ops = &lm90_ops;
data->chip.info = data->info; data->chip.info = data->info;
@ -2081,7 +2083,7 @@ static int lm90_probe(struct i2c_client *client)
* The 'pec' attribute is attached to the i2c device and thus created * The 'pec' attribute is attached to the i2c device and thus created
* separately. * separately.
*/ */
if (client->flags & I2C_CLIENT_PEC) { if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) {
err = device_create_file(dev, &dev_attr_pec); err = device_create_file(dev, &dev_attr_pec);
if (err) if (err)
return err; return err;