RE: [PATCH 1/3] hwmon: max31827: Add PEC support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Guenter Roeck <groeck7@xxxxxxxxx> On Behalf Of Guenter Roeck
> Sent: Thursday, December 14, 2023 6:10 PM
> To: Matyas, Daniel <Daniel.Matyas@xxxxxxxxxx>
> Cc: Jean Delvare <jdelvare@xxxxxxxx>; Rob Herring
> <robh+dt@xxxxxxxxxx>; Krzysztof Kozlowski
> <krzysztof.kozlowski+dt@xxxxxxxxxx>; Conor Dooley
> <conor+dt@xxxxxxxxxx>; Jonathan Corbet <corbet@xxxxxxx>; linux-
> hwmon@xxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; linux-
> kernel@xxxxxxxxxxxxxxx; linux-doc@xxxxxxxxxxxxxxx
> Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support
> 
> [External]
> 
> On 12/14/23 06:36, Daniel Matyas wrote:
> > Removed regmap and used my functions to read, write and update bits.
> > In these functions i2c_smbus_ helper functions are used. These check
> > if there were any PEC errors during read. In the write function, if
> > PEC is enabled, I check for PEC Error bit, to see if there were any errors.
> >
> > Signed-off-by: Daniel Matyas <daniel.matyas@xxxxxxxxxx>
> 
> The "PEC" attribute needs to be attached to the I2C device.
> See lm90.c or pmbus_core.c for examples.
> 
> The changes, if indeed needed, do not warant dropping regmap.
> You will need to explain why the reg_read and reg_write callbacks
> provideed by regmap can not be used.
> 
> On top of that, it is not clear why regmap can't be used in the first place.
> It seems that the major change is that one needs to read the configuration
> register after a write to see if there was a PEC error. It is not immediately
> obvious why that additional read (if indeed necessary) would require
> regmap support to be dropped.
> 

I tried out writing and and reading with regmap, but it is not working properly. Even if I modify the client flag, I still receive only 2 bytes of data (a word). I should be receiving 2+1 bytes = data + CRC-8.

With i2c_smbus reads and writes, when I set the flag, I receive the 2+1 bytes, as expected.

Daniel

> Regarding "if indeed necessary": There needs to be a comment explaining
> that the device will return ACK even after a packet with bad PEC is
> received.
> 
> > ---
> >   Documentation/hwmon/max31827.rst |  14 +-
> >   drivers/hwmon/max31827.c         | 219 +++++++++++++++++++++++-----
> ---
> >   2 files changed, 171 insertions(+), 62 deletions(-)
> >
> > diff --git a/Documentation/hwmon/max31827.rst
> > b/Documentation/hwmon/max31827.rst
> > index 44ab9dc064cb..ecbc1ddba6a7 100644
> > --- a/Documentation/hwmon/max31827.rst
> > +++ b/Documentation/hwmon/max31827.rst
> > @@ -131,7 +131,13 @@ The Fault Queue bits select how many
> consecutive temperature faults must occur
> >   before overtemperature or undertemperature faults are indicated in
> the
> >   corresponding status bits.
> >
> > -Notes
> > ------
> > -
> > -PEC is not implemented.
> > +PEC (packet error checking) can be enabled from the "pec" device
> attribute.
> > +If PEC is enabled, a PEC byte is appended to the end of each message
> transfer.
> > +This is a CRC-8 byte that is calculated on all of the message bytes
> > +(including the address/read/write byte). The last device to transmit
> > +a data byte also transmits the PEC byte. The master transmits the PEC
> > +byte after a write transaction, and the MAX31827 transmits the PEC
> byte after a read transaction.
> > +
> > +The read PEC error is handled inside the
> i2c_smbus_read_word_swapped() function.
> > +To check if the write had any PEC error a read is performed on the
> > +configuration register, to check the PEC Error bit.
> > \ No newline at end of file
> > diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
> index
> > 71ad3934dfb6..db93492193bd 100644
> > --- a/drivers/hwmon/max31827.c
> > +++ b/drivers/hwmon/max31827.c
> > @@ -11,8 +11,8 @@
> >   #include <linux/hwmon.h>
> >   #include <linux/i2c.h>
> >   #include <linux/mutex.h>
> > -#include <linux/regmap.h>
> >   #include <linux/regulator/consumer.h>
> > +#include <linux/hwmon-sysfs.h>
> >   #include <linux/of_device.h>
> >
> >   #define MAX31827_T_REG			0x0
> > @@ -24,11 +24,13 @@
> >
> >   #define MAX31827_CONFIGURATION_1SHOT_MASK	BIT(0)
> >   #define MAX31827_CONFIGURATION_CNV_RATE_MASK
> 	GENMASK(3, 1)
> > +#define MAX31827_CONFIGURATION_PEC_EN_MASK	BIT(4)
> >   #define MAX31827_CONFIGURATION_TIMEOUT_MASK	BIT(5)
> >   #define MAX31827_CONFIGURATION_RESOLUTION_MASK
> 	GENMASK(7, 6)
> >   #define MAX31827_CONFIGURATION_ALRM_POL_MASK	BIT(8)
> >   #define MAX31827_CONFIGURATION_COMP_INT_MASK	BIT(9)
> >   #define MAX31827_CONFIGURATION_FLT_Q_MASK	GENMASK(11, 10)
> > +#define MAX31827_CONFIGURATION_PEC_ERR_MASK	BIT(13)
> >   #define MAX31827_CONFIGURATION_U_TEMP_STAT_MASK	BIT(14)
> >   #define MAX31827_CONFIGURATION_O_TEMP_STAT_MASK	BIT(15)
> >
> > @@ -94,23 +96,67 @@ struct max31827_state {
> >   	 * Prevent simultaneous access to the i2c client.
> >   	 */
> >   	struct mutex lock;
> > -	struct regmap *regmap;
> >   	bool enable;
> >   	unsigned int resolution;
> >   	unsigned int update_interval;
> > +	struct i2c_client *client;
> >   };
> >
> > -static const struct regmap_config max31827_regmap = {
> > -	.reg_bits = 8,
> > -	.val_bits = 16,
> > -	.max_register = 0xA,
> > -};
> > +static int max31827_reg_read(struct i2c_client *client, u8 reg, u16
> > +*val) {
> > +	u16 tmp = i2c_smbus_read_word_swapped(client, reg);
> > +
> > +	if (tmp < 0)
> 
> An u16 variable will never be negative.
> 
> > +		return tmp;
> > +
> > +	*val = tmp;
> > +	return 0;
> > +}
> 
> If regmap can indeed not be used, it is unnecessary to provide a pointer to
> the return value. Instead, just like with smbus calls, the error return and
> the return value can be combined. Adding this function just to separate
> error from return value adds zero value (and, as can be seen from the
> above, actually adds an opportunity to introduce bugs).
> 
> > +
> > +static int max31827_reg_write(struct i2c_client *client, u8 reg, u16
> > +val) {
> > +	u16 cfg;
> > +	int ret;
> > +
> > +	ret = i2c_smbus_write_word_swapped(client, reg, val);
> > +	if (ret)
> > +		return ret;
> > +
> > +	// If PEC is not enabled, return with success
> 
> Do not mix comment styles. The rest of the driver doesn't use C++
> comments.
> Besides, the comment does not add any value.
> 
> > +	if (!(client->flags & I2C_CLIENT_PEC))
> > +		return 0;
> > +
> > +	ret = max31827_reg_read(client,
> MAX31827_CONFIGURATION_REG, &cfg);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (cfg & MAX31827_CONFIGURATION_PEC_ERR_MASK)
> > +		return -EBADMSG;
> > +
> 
> EBADMSG is "Not a data message". I don't think that is appropriate here.
> 
> I would very much prefer
> 	if (client->flags & I2C_CLIENT_PEC) {
> 		...
> 	}
> 
> > +	return 0;
> > +}
> > +
> > +static int max31827_update_bits(struct i2c_client *client, u8 reg,
> > +				u16 mask, u16 val)
> > +{
> > +	u16 tmp;
> > +	int ret;
> > +
> > +	ret = max31827_reg_read(client, reg, &tmp);
> > +	if (ret)
> > +		return ret;
> > +
> > +	tmp = (tmp & ~mask) | (val & mask);
> > +	ret = max31827_reg_write(client, reg, tmp);
> > +
> > +	return ret;
> > +}
> >
> >   static int shutdown_write(struct max31827_state *st, unsigned int reg,
> >   			  unsigned int mask, unsigned int val)
> >   {
> > -	unsigned int cfg;
> > -	unsigned int cnv_rate;
> > +	u16 cfg;
> > +	u16 cnv_rate;
> 
> I really do not see the point of those changes. u16 is more expensive than
> unsigned int on many architectures. If retained, you'll have to explain why
> this is needed and beneficial.
> 
> >   	int ret;
> >
> >   	/*
> > @@ -125,34 +171,34 @@ static int shutdown_write(struct
> max31827_state
> > *st, unsigned int reg,
> >
> >   	if (!st->enable) {
> >   		if (!mask)
> > -			ret = regmap_write(st->regmap, reg, val);
> > +			ret = max31827_reg_write(st->client, reg, val);
> >   		else
> > -			ret = regmap_update_bits(st->regmap, reg, mask,
> val);
> > +			ret = max31827_update_bits(st->client, reg,
> mask, val);
> >   		goto unlock;
> >   	}
> >
> > -	ret = regmap_read(st->regmap,
> MAX31827_CONFIGURATION_REG, &cfg);
> > +	ret = max31827_reg_read(st->client,
> MAX31827_CONFIGURATION_REG,
> > +&cfg);
> >   	if (ret)
> >   		goto unlock;
> >
> >   	cnv_rate = MAX31827_CONFIGURATION_CNV_RATE_MASK & cfg;
> >   	cfg = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK |
> >   		      MAX31827_CONFIGURATION_CNV_RATE_MASK);
> > -	ret = regmap_write(st->regmap,
> MAX31827_CONFIGURATION_REG, cfg);
> > +	ret = max31827_reg_write(st->client,
> MAX31827_CONFIGURATION_REG,
> > +cfg);
> >   	if (ret)
> >   		goto unlock;
> >
> >   	if (!mask)
> > -		ret = regmap_write(st->regmap, reg, val);
> > +		ret = max31827_reg_write(st->client, reg, val);
> >   	else
> > -		ret = regmap_update_bits(st->regmap, reg, mask, val);
> > +		ret = max31827_update_bits(st->client, reg, mask, val);
> >
> >   	if (ret)
> >   		goto unlock;
> >
> > -	ret = regmap_update_bits(st->regmap,
> MAX31827_CONFIGURATION_REG,
> > -
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > -				 cnv_rate);
> > +	ret = max31827_update_bits(st->client,
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > +				   cnv_rate);
> >
> >   unlock:
> >   	mutex_unlock(&st->lock);
> > @@ -198,15 +244,16 @@ static int max31827_read(struct device *dev,
> enum hwmon_sensor_types type,
> >   			 u32 attr, int channel, long *val)
> >   {
> >   	struct max31827_state *st = dev_get_drvdata(dev);
> > -	unsigned int uval;
> > +	u16 uval;
> >   	int ret = 0;
> >
> >   	switch (type) {
> >   	case hwmon_temp:
> >   		switch (attr) {
> >   		case hwmon_temp_enable:
> > -			ret = regmap_read(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> > +
> 	MAX31827_CONFIGURATION_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> > @@ -226,10 +273,10 @@ static int max31827_read(struct device *dev,
> enum hwmon_sensor_types type,
> >   				 * be changed during the conversion
> process.
> >   				 */
> >
> > -				ret = regmap_update_bits(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG,
> > -
> MAX31827_CONFIGURATION_1SHOT_MASK,
> > -							 1);
> > +				ret = max31827_update_bits(st->client,
> > +
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_1SHOT_MASK,
> > +							   1);
> >   				if (ret) {
> >   					mutex_unlock(&st->lock);
> >   					return ret;
> > @@ -246,7 +293,8 @@ static int max31827_read(struct device *dev,
> enum hwmon_sensor_types type,
> >   			    st->update_interval == 125)
> >   				usleep_range(15000, 20000);
> >
> > -			ret = regmap_read(st->regmap,
> MAX31827_T_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> MAX31827_T_REG,
> > +						&uval);
> >
> >   			mutex_unlock(&st->lock);
> >
> > @@ -257,23 +305,26 @@ static int max31827_read(struct device *dev,
> > enum hwmon_sensor_types type,
> >
> >   			break;
> >   		case hwmon_temp_max:
> > -			ret = regmap_read(st->regmap,
> MAX31827_TH_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> MAX31827_TH_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> >   			*val = MAX31827_16_BIT_TO_M_DGR(uval);
> >   			break;
> >   		case hwmon_temp_max_hyst:
> > -			ret = regmap_read(st->regmap,
> MAX31827_TH_HYST_REG,
> > -					  &uval);
> > +			ret = max31827_reg_read(st->client,
> > +
> 	MAX31827_TH_HYST_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> >   			*val = MAX31827_16_BIT_TO_M_DGR(uval);
> >   			break;
> >   		case hwmon_temp_max_alarm:
> > -			ret = regmap_read(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> > +
> 	MAX31827_CONFIGURATION_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> > @@ -281,23 +332,25 @@ static int max31827_read(struct device *dev,
> enum hwmon_sensor_types type,
> >   					 uval);
> >   			break;
> >   		case hwmon_temp_min:
> > -			ret = regmap_read(st->regmap,
> MAX31827_TL_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> MAX31827_TL_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> >   			*val = MAX31827_16_BIT_TO_M_DGR(uval);
> >   			break;
> >   		case hwmon_temp_min_hyst:
> > -			ret = regmap_read(st->regmap,
> MAX31827_TL_HYST_REG,
> > -					  &uval);
> > +			ret = max31827_reg_read(st->client,
> MAX31827_TL_HYST_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> >   			*val = MAX31827_16_BIT_TO_M_DGR(uval);
> >   			break;
> >   		case hwmon_temp_min_alarm:
> > -			ret = regmap_read(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> > +
> 	MAX31827_CONFIGURATION_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> > @@ -313,8 +366,9 @@ static int max31827_read(struct device *dev,
> enum
> > hwmon_sensor_types type,
> >
> >   	case hwmon_chip:
> >   		if (attr == hwmon_chip_update_interval) {
> > -			ret = regmap_read(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG, &uval);
> > +			ret = max31827_reg_read(st->client,
> > +
> 	MAX31827_CONFIGURATION_REG,
> > +						&uval);
> >   			if (ret)
> >   				break;
> >
> > @@ -355,11 +409,11 @@ static int max31827_write(struct device *dev,
> > enum hwmon_sensor_types type,
> >
> >   			st->enable = val;
> >
> > -			ret = regmap_update_bits(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG,
> > -
> MAX31827_CONFIGURATION_1SHOT_MASK |
> > -
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > -
> MAX31827_DEVICE_ENABLE(val));
> > +			ret = max31827_update_bits(st->client,
> > +
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_1SHOT_MASK |
> > +
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > +
> MAX31827_DEVICE_ENABLE(val));
> >
> >   			mutex_unlock(&st->lock);
> >
> > @@ -402,10 +456,10 @@ static int max31827_write(struct device *dev,
> enum hwmon_sensor_types type,
> >   			res =
> FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK,
> >   					 res);
> >
> > -			ret = regmap_update_bits(st->regmap,
> > -
> MAX31827_CONFIGURATION_REG,
> > -
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > -						 res);
> > +			ret = max31827_update_bits(st->client,
> > +
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_CNV_RATE_MASK,
> > +						   res);
> >   			if (ret)
> >   				return ret;
> >
> > @@ -425,10 +479,10 @@ static ssize_t temp1_resolution_show(struct
> device *dev,
> >   				     char *buf)
> >   {
> >   	struct max31827_state *st = dev_get_drvdata(dev);
> > -	unsigned int val;
> > +	u16 val;
> >   	int ret;
> >
> > -	ret = regmap_read(st->regmap,
> MAX31827_CONFIGURATION_REG, &val);
> > +	ret = max31827_reg_read(st->client,
> MAX31827_CONFIGURATION_REG,
> > +&val);
> >   	if (ret)
> >   		return ret;
> >
> > @@ -473,10 +527,63 @@ static ssize_t temp1_resolution_store(struct
> device *dev,
> >   	return ret ? ret : count;
> >   }
> >
> > +static ssize_t pec_show(struct device *dev, struct device_attribute
> *devattr,
> > +			char *buf)
> > +{
> > +	struct max31827_state *st = dev_get_drvdata(dev);
> > +	struct i2c_client *client = st->client;
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%d\n", !!(client->flags &
> > +I2C_CLIENT_PEC)); }
> > +
> > +static ssize_t pec_store(struct device *dev, struct device_attribute
> *devattr,
> > +			 const char *buf, size_t count)
> > +{
> > +	struct max31827_state *st = dev_get_drvdata(dev);
> > +	struct i2c_client *client = st->client;
> > +	unsigned int val;
> > +	u16 val2;
> > +	int err;
> > +
> > +	err = kstrtouint(buf, 10, &val);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	val2 = FIELD_PREP(MAX31827_CONFIGURATION_PEC_EN_MASK,
> val);
> > +
> > +	if (err)
> > +		return err;
> > +
> > +	switch (val) {
> > +	case 0:
> > +		client->flags &= ~I2C_CLIENT_PEC;
> > +		err = max31827_update_bits(client,
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_PEC_EN_MASK,
> > +					   val2);
> > +		if (err)
> > +			return err;
> > +		break;
> > +	case 1:
> > +		err = max31827_update_bits(client,
> MAX31827_CONFIGURATION_REG,
> > +
> MAX31827_CONFIGURATION_PEC_EN_MASK,
> > +					   val2);
> > +		if (err)
> > +			return err;
> > +		client->flags |= I2C_CLIENT_PEC;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return count;
> > +}
> > +
> >   static DEVICE_ATTR_RW(temp1_resolution);
> > +static DEVICE_ATTR_RW(pec);
> >
> >   static struct attribute *max31827_attrs[] = {
> >   	&dev_attr_temp1_resolution.attr,
> > +	&dev_attr_pec.attr,
> >   	NULL
> >   };
> >   ATTRIBUTE_GROUPS(max31827);
> > @@ -489,9 +596,9 @@ static const struct i2c_device_id
> max31827_i2c_ids[] = {
> >   };
> >   MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids);
> >
> > -static int max31827_init_client(struct max31827_state *st,
> > -				struct device *dev)
> > +static int max31827_init_client(struct max31827_state *st)
> >   {
> > +	struct device *dev = &st->client->dev;
> 
> Now we are absolutely down to personal preference changes.
> I am not at all inclined to accept such changes, sorry.
> 
> Including such changes means I'll have to put extra scrutiny on your patch
> submissions in the future to ensure that you don't try to sneak in similar
> changes, which I find quite frustrating. Is that really necessary ?
> 
> Guenter
> 
> >   	struct fwnode_handle *fwnode;
> >   	unsigned int res = 0;
> >   	u32 data, lsb_idx;
> > @@ -575,7 +682,7 @@ static int max31827_init_client(struct
> max31827_state *st,
> >   		}
> >   	}
> >
> > -	return regmap_write(st->regmap,
> MAX31827_CONFIGURATION_REG, res);
> > +	return max31827_reg_write(st->client,
> MAX31827_CONFIGURATION_REG,
> > +res);
> >   }
> >
> >   static const struct hwmon_channel_info *max31827_info[] = { @@
> > -613,17 +720,13 @@ static int max31827_probe(struct i2c_client
> *client)
> >   		return -ENOMEM;
> >
> >   	mutex_init(&st->lock);
> > -
> > -	st->regmap = devm_regmap_init_i2c(client,
> &max31827_regmap);
> > -	if (IS_ERR(st->regmap))
> > -		return dev_err_probe(dev, PTR_ERR(st->regmap),
> > -				     "Failed to allocate regmap.\n");
> > +	st->client = client;
> >
> >   	err = devm_regulator_get_enable(dev, "vref");
> >   	if (err)
> >   		return dev_err_probe(dev, err, "failed to enable
> regulator\n");
> >
> > -	err = max31827_init_client(st, dev);
> > +	err = max31827_init_client(st);
> >   	if (err)
> >   		return err;
> >





[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux