2023-12-14 14:37:48

by Matyas, Daniel

[permalink] [raw]
Subject: [PATCH 1/3] hwmon: max31827: Add PEC support

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 <[email protected]>
---
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)
+ return tmp;
+
+ *val = tmp;
+ return 0;
+}
+
+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
+ 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;
+
+ 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;
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;
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;

--
2.34.1


2023-12-14 14:37:53

by Matyas, Daniel

[permalink] [raw]
Subject: [PATCH 3/3] hwmon: max31827: Compatible for adaq4224

Compatible string "adi,adaq4224_temp" is accepted in device tree.
When this string is seen in the device tree, the name of the device
changes to "adaq4224_temp" and the default configuration of max31827
is loaded.

This modification was requested by the costumer, so that whenever one
analyzes the available devices, one can know for sure, that max31827 is
part of the adaq4224.

Signed-off-by: Daniel Matyas <[email protected]>
---
drivers/hwmon/max31827.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
index db93492193bd..c3500a5b2c29 100644
--- a/drivers/hwmon/max31827.c
+++ b/drivers/hwmon/max31827.c
@@ -48,7 +48,7 @@
#define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000)
#define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0)

-enum chips { max31827 = 1, max31828, max31829 };
+enum chips { max31827 = 1, max31828, max31829, adaq4224_temp };

enum max31827_cnv {
MAX31827_CNV_1_DIV_64_HZ = 1,
@@ -592,6 +592,7 @@ static const struct i2c_device_id max31827_i2c_ids[] = {
{ "max31827", max31827 },
{ "max31828", max31828 },
{ "max31829", max31829 },
+ { "adaq4224_temp", adaq4224_temp },
{ }
};
MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids);
@@ -620,6 +621,9 @@ static int max31827_init_client(struct max31827_state *st)
res |= FIELD_PREP(MAX31827_CONFIGURATION_TIMEOUT_MASK, !prop);

type = (enum chips)(uintptr_t)device_get_match_data(dev);
+ if (type == adaq4224_temp) {
+ dev->driver->name = "adaq4224_temp";
+ }

if (fwnode_property_present(fwnode, "adi,alarm-pol")) {
ret = fwnode_property_read_u32(fwnode, "adi,alarm-pol", &data);
@@ -633,6 +637,7 @@ static int max31827_init_client(struct max31827_state *st)
*/
switch (type) {
case max31827:
+ case adaq4224_temp:
case max31828:
res |= FIELD_PREP(MAX31827_CONFIGURATION_ALRM_POL_MASK,
MAX31827_ALRM_POL_LOW);
@@ -669,6 +674,7 @@ static int max31827_init_client(struct max31827_state *st)
*/
switch (type) {
case max31827:
+ case adaq4224_temp:
res |= FIELD_PREP(MAX31827_CONFIGURATION_FLT_Q_MASK,
MAX31827_FLT_Q_1);
break;
@@ -750,6 +756,10 @@ static const struct of_device_id max31827_of_match[] = {
.compatible = "adi,max31829",
.data = (void *)max31829
},
+ {
+ .compatible = "adi,adaq4224_temp",
+ .data = (void *)adaq4224_temp
+ },
{ }
};
MODULE_DEVICE_TABLE(of, max31827_of_match);
--
2.34.1

2023-12-14 14:37:56

by Matyas, Daniel

[permalink] [raw]
Subject: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

In the device ada4224 the max31827 temperature sensor will be used, so
the default values corresponding to adaq4224_temp are the same for
max31827.

Signed-off-by: Daniel Matyas <[email protected]>
---
Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
index f60e06ab7d0a..9f3b0839aa46 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
@@ -20,6 +20,7 @@ properties:
- const: adi,max31827
- items:
- enum:
+ - adi,adaq4224_temp
- adi,max31828
- adi,max31829
- const: adi,max31827
@@ -81,7 +82,9 @@ allOf:
properties:
compatible:
contains:
- const: adi,max31827
+ enum:
+ - adi,max31827
+ - adi,adaq4224_temp

then:
properties:
--
2.34.1

2023-12-14 15:15:55

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 3/3] hwmon: max31827: Compatible for adaq4224

On 12/14/23 06:36, Daniel Matyas wrote:
> Compatible string "adi,adaq4224_temp" is accepted in device tree.
> When this string is seen in the device tree, the name of the device
> changes to "adaq4224_temp" and the default configuration of max31827
> is loaded.
>
> This modification was requested by the costumer, so that whenever one

customer

> analyzes the available devices, one can know for sure, that max31827 is
> part of the adaq4224.
>

There is (officially) no such chip. I have no idea if this is a new chip
or something else, and I have no idea how it relates to max31827.
Either case, the "_temp" in the chip name doesn't make sense to me.

> Signed-off-by: Daniel Matyas <[email protected]>
> ---
> drivers/hwmon/max31827.c | 12 +++++++++++-
> 1 file changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
> index db93492193bd..c3500a5b2c29 100644
> --- a/drivers/hwmon/max31827.c
> +++ b/drivers/hwmon/max31827.c
> @@ -48,7 +48,7 @@
> #define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000)
> #define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0)
>
> -enum chips { max31827 = 1, max31828, max31829 };
> +enum chips { max31827 = 1, max31828, max31829, adaq4224_temp };
>
> enum max31827_cnv {
> MAX31827_CNV_1_DIV_64_HZ = 1,
> @@ -592,6 +592,7 @@ static const struct i2c_device_id max31827_i2c_ids[] = {
> { "max31827", max31827 },
> { "max31828", max31828 },
> { "max31829", max31829 },
> + { "adaq4224_temp", adaq4224_temp },
> { }
> };
> MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids);
> @@ -620,6 +621,9 @@ static int max31827_init_client(struct max31827_state *st)
> res |= FIELD_PREP(MAX31827_CONFIGURATION_TIMEOUT_MASK, !prop);
>
> type = (enum chips)(uintptr_t)device_get_match_data(dev);
> + if (type == adaq4224_temp) {
> + dev->driver->name = "adaq4224_temp";
> + }
>

No, sorry, we don't make such changes. The driver has a fixed name
which is independent of the device connected to it.

Guenter

2023-12-14 15:25:56

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On 12/14/23 06:36, Daniel Matyas wrote:
> In the device ada4224 the max31827 temperature sensor will be used, so
> the default values corresponding to adaq4224_temp are the same for
> max31827.
>

I don't know what that device is, but if the max31827 is used it should
be instantiated as max31827.

Guenter

> Signed-off-by: Daniel Matyas <[email protected]>
> ---
> Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> index f60e06ab7d0a..9f3b0839aa46 100644
> --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> +++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> @@ -20,6 +20,7 @@ properties:
> - const: adi,max31827
> - items:
> - enum:
> + - adi,adaq4224_temp
> - adi,max31828
> - adi,max31829
> - const: adi,max31827
> @@ -81,7 +82,9 @@ allOf:
> properties:
> compatible:
> contains:
> - const: adi,max31827
> + enum:
> + - adi,max31827
> + - adi,adaq4224_temp
>
> then:
> properties:

2023-12-14 15:39:33

by Conor Dooley

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On Thu, Dec 14, 2023 at 07:15:13AM -0800, Guenter Roeck wrote:
> On 12/14/23 06:36, Daniel Matyas wrote:
> > In the device ada4224 the max31827 temperature sensor will be used, so
> > the default values corresponding to adaq4224_temp are the same for
> > max31827.
> >
>
> I don't know what that device is, but if the max31827 is used it should
> be instantiated as max31827.

An improved commit message would be rather helpful here, as google did
not turn up any information on what this new device is.
Taking the patch on face value, a couple comments below.

> > Signed-off-by: Daniel Matyas <[email protected]>
> > ---
> > Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5 ++++-
> > 1 file changed, 4 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > index f60e06ab7d0a..9f3b0839aa46 100644
> > --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > +++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > @@ -20,6 +20,7 @@ properties:
> > - const: adi,max31827
> > - items:
> > - enum:
> > + - adi,adaq4224_temp

No underscores in the compatible please.

> > - adi,max31828
> > - adi,max31829
> > - const: adi,max31827
> > @@ -81,7 +82,9 @@ allOf:
> > properties:
> > compatible:
> > contains:
> > - const: adi,max31827
> > + enum:
> > + - adi,max31827
> > + - adi,adaq4224_temp

This doesn't do anything afaict, since the binding doesn't allow
"adi,adaq4224_temp" without "adi,max31827".

> > then:
> > properties:
>


Attachments:
(No filename) (1.78 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-14 16:11:10

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

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 <[email protected]>

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.

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;
>

2023-12-15 08:50:34

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On 14/12/2023 15:36, Daniel Matyas wrote:
> In the device ada4224 the max31827 temperature sensor will be used, so
> the default values corresponding to adaq4224_temp are the same for
> max31827.
>
> Signed-off-by: Daniel Matyas <[email protected]>

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching.

> ---
> Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> index f60e06ab7d0a..9f3b0839aa46 100644
> --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> +++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> @@ -20,6 +20,7 @@ properties:
> - const: adi,max31827
> - items:
> - enum:
> + - adi,adaq4224_temp

Underscores are not allowed



Best regards,
Krzysztof


2023-12-15 16:03:18

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On 12/15/23 00:49, Krzysztof Kozlowski wrote:
> On 14/12/2023 15:36, Daniel Matyas wrote:
>> In the device ada4224 the max31827 temperature sensor will be used, so
>> the default values corresponding to adaq4224_temp are the same for
>> max31827.
>>
>> Signed-off-by: Daniel Matyas <[email protected]>
>
> Please use subject prefixes matching the subsystem. You can get them for
> example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
> your patch is touching.
>
>> ---
>> Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5 ++++-
>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>> index f60e06ab7d0a..9f3b0839aa46 100644
>> --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>> +++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>> @@ -20,6 +20,7 @@ properties:
>> - const: adi,max31827
>> - items:
>> - enum:
>> + - adi,adaq4224_temp
>
> Underscores are not allowed
>

That isn't the main problem with this patch.
https://github.com/analogdevicesinc/linux/tree/dev_adaq4224_dts
suggests that it may be a development system which utilizes the max31827.
From there, we can see that there is a devicetree description of a board
with that name which uses

temperature1: temperature@5f {
compatible = "adi,adaq4224_temp";
reg = <0x5f>;
vref-supply = <&vio>;

adi,comp-int;
adi,alarm-pol = <0>;
adi,fault-q = <1>;
adi,timeout-enable;
};

That doesn't make sense to me. I don't know why they don't just reference max31827.
I am most definitely not going to accept a driver change just to map adi,adaq4224_temp
(or adi,adaq4224-temp) to the actually used temperature sensor chip. If we start
accepting that, we'd end up with no end of
"<vendor>,<board_name>-{temp,voltage,current,power,...}"
compatibles.

Looking more into the above mentioned repository/branch, an earlier version
of the dts file did reference adi,max31827 for that chip. It also looks like
there may be an adaq4224 ADC (per drivers/iio/adc/ad4630.c), but that would be
a SPI chip. It seems highly unlikely that a SPI ADC would have a separate I2C
interface connected to a temperature sensor. Confusing.

There is also some indication that this may some IP to be loaded into an FPGA.
which utilizes an FPGA implementation of max31827 (or maybe connects to one).
If that is the case, it should still be referenced as max31827.

All that wasted time because of "let's provide as little as possible information
about what we are actually doing" :-(.

Guenter


2023-12-15 21:46:25

by Matyas, Daniel

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



> -----Original Message-----
> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> Sent: Thursday, December 14, 2023 6:10 PM
> To: Matyas, Daniel <[email protected]>
> Cc: Jean Delvare <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley
> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> 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 <[email protected]>
>
> The "PEC" attribute needs to be attached to the I2C device.
> See lm90.c or pmbus_core.c for examples.
>

I added pec_show() and pec_store() functions and created the pec file within the max31827_groups.
I did not set the flags, because I want them to be set only in pec_store. By default the PEC flag should not be set.

> 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.
>

So I am not sure about this, but it seems to me, that regmap_write is not sending a PEC byte and regmap_read is not checking the PEC byte by default. My rationale was: i2c_smbus_write_word_data() is using the i2c_xfer function, which has as argument the client->flags. So, if I2C_CLIENT_PEC is set in client->flags, that would be transmitted by i2c_xfer. I am not convinced about this, but lm90 and pmbus_core are not using regmap, so I went ahead and replaced it.

If what I am thinking is wrong, please correct me.

> 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
>

Sorry for this! I thought, if I am adding client to the private structure I might as well delete the second parameter of init_client, because I can easily retrieve the device structure from client. I added this line so that the changes to the code are kept to a minimum.

> > 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;
> >

2023-12-16 01:33:07

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

On 12/15/23 12:28, Matyas, Daniel wrote:
>
>
>> -----Original Message-----
>> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
>> Sent: Thursday, December 14, 2023 6:10 PM
>> To: Matyas, Daniel <[email protected]>
>> Cc: Jean Delvare <[email protected]>; Rob Herring
>> <[email protected]>; Krzysztof Kozlowski
>> <[email protected]>; Conor Dooley
>> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
>> [email protected]; [email protected]; linux-
>> [email protected]; [email protected]
>> 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 <[email protected]>
>>
>> The "PEC" attribute needs to be attached to the I2C device.
>> See lm90.c or pmbus_core.c for examples.
>>
>
> I added pec_show() and pec_store() functions and created the pec file within the max31827_groups.
> I did not set the flags, because I want them to be set only in pec_store. By default the PEC flag should not be set.
>

That is not the point. Again,

>> The "PEC" attribute needs to be attached to the I2C device.
>> See lm90.c or pmbus_core.c for examples.

That is not about regmap, it is about the location of the "pec" attribute.

>> 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.
>>
>
> So I am not sure about this, but it seems to me, that regmap_write is not sending a PEC byte and regmap_read is not checking the PEC byte by default. My rationale was: i2c_smbus_write_word_data() is using the i2c_xfer function, which has as argument the client->flags. So, if I2C_CLIENT_PEC is set in client->flags, that would be transmitted by i2c_xfer. I am not convinced about this, but lm90 and pmbus_core are not using regmap, so I went ahead and replaced it.
>
> If what I am thinking is wrong, please correct me.
>

regmap uses smbus functions to access the chip if the underlying i2c driver
supports SMBus word operations. I fail to see the difference to your code.
Sure, max31827_reg_write() executes another read after the write, but that
is again a 16-bit operation and might we well be implemented as another
regmap operation to read the status register.

It would be possible to replace the regmap i2c code, use raw regmap
code instead, and provide ->read and ->write callbacks in struct regmap_config,
but I am not convinced that this would be beneficial.

Either case, sorry, I can not follow your line of argument.

>> 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
>>
>
> Sorry for this! I thought, if I am adding client to the private structure I might as well delete the second parameter of init_client, because I can easily retrieve the device structure from client. I added this line so that the changes to the code are kept to a minimum.
>

You are making unnecessary changes (several unsigned int -> u16
plus this one), and claim this would be "so that the changes to
the code are kept to a minimum". Really ? How does making
unnecessary changes keep the changes to the code to a minimum ?

Guenter


2023-12-18 09:11:17

by Matyas, Daniel

[permalink] [raw]
Subject: RE: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string



> -----Original Message-----
> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> Sent: Friday, December 15, 2023 6:03 PM
> To: Krzysztof Kozlowski <[email protected]>; Matyas, Daniel
> <[email protected]>
> Cc: Jean Delvare <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley
> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as
> compatible string
>
> [External]
>
> On 12/15/23 00:49, Krzysztof Kozlowski wrote:
> > On 14/12/2023 15:36, Daniel Matyas wrote:
> >> In the device ada4224 the max31827 temperature sensor will be used,
> >> so the default values corresponding to adaq4224_temp are the same
> for
> >> max31827.
> >>
> >> Signed-off-by: Daniel Matyas <[email protected]>
> >
> > Please use subject prefixes matching the subsystem. You can get them
> > for example with `git log --oneline -- DIRECTORY_OR_FILE` on the
> > directory your patch is touching.
> >
> >> ---
> >> Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5
> ++++-
> >> 1 file changed, 4 insertions(+), 1 deletion(-)
> >>
> >> diff --git
> >> a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> >> b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> >> index f60e06ab7d0a..9f3b0839aa46 100644
> >> --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> >> +++
> b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> >> @@ -20,6 +20,7 @@ properties:
> >> - const: adi,max31827
> >> - items:
> >> - enum:
> >> + - adi,adaq4224_temp
> >
> > Underscores are not allowed
> >
>
> That isn't the main problem with this patch.
> https://urldefense.com/v3/__https://github.com/analogdevicesinc/linux/
> tree/dev_adaq4224_dts__;!!A3Ni8CS0y2Y!_2D1w1DD5sjJrNyArZYZ3QW9
> nS8URmP6X0n6R7q1sBnDB1HPL6jROhD_w9u3fJixt2hDDNtO6VpgLM1Jka
> Q$
> suggests that it may be a development system which utilizes the
> max31827.
> From there, we can see that there is a devicetree description of a board
> with that name which uses
>
> temperature1: temperature@5f {
> compatible = "adi,adaq4224_temp";
> reg = <0x5f>;
> vref-supply = <&vio>;
>
> adi,comp-int;
> adi,alarm-pol = <0>;
> adi,fault-q = <1>;
> adi,timeout-enable;
> };
>
> That doesn't make sense to me. I don't know why they don't just
> reference max31827.
> I am most definitely not going to accept a driver change just to map
> adi,adaq4224_temp (or adi,adaq4224-temp) to the actually used
> temperature sensor chip. If we start accepting that, we'd end up with no
> end of "<vendor>,<board_name>-{temp,voltage,current,power,...}"
> compatibles.
>
> Looking more into the above mentioned repository/branch, an earlier
> version of the dts file did reference adi,max31827 for that chip. It also
> looks like there may be an adaq4224 ADC (per drivers/iio/adc/ad4630.c),
> but that would be a SPI chip. It seems highly unlikely that a SPI ADC would
> have a separate I2C interface connected to a temperature sensor.
> Confusing.
>
> There is also some indication that this may some IP to be loaded into an
> FPGA.
> which utilizes an FPGA implementation of max31827 (or maybe connects
> to one).
> If that is the case, it should still be referenced as max31827.
>
> All that wasted time because of "let's provide as little as possible
> information about what we are actually doing" :-(.
>
> Guenter

I asked around to get some more clarification on the matter. There will be a new chip released, named adaq4224. This chip will have the max31827 implemented in the silicon, so the driver used to get temperature information would be max31827.c. The chip will have spi and i2c communication too. The other driver you mentioned, the ad4630.c will communicate through spi.

Because the chip has a different name, I was asked to add a new label for the max31827, so that it will be clear for the user, that the max31827 is part of the chip.

In ad4630.c the indio_dev->name is changed. Would it be ok, if I did something similar? Is there something similar in hwmon? Maybe dev->init_name?

Daniel

2023-12-18 09:13:55

by Matyas, Daniel

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



> -----Original Message-----
> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> Sent: Saturday, December 16, 2023 3:33 AM
> To: Matyas, Daniel <[email protected]>
> Cc: Jean Delvare <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley
> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>
> [External]
>
> On 12/15/23 12:28, Matyas, Daniel wrote:
> >
> >
> >> -----Original Message-----
> >> From: Guenter Roeck <[email protected]> On Behalf Of Guenter
> Roeck
> >> Sent: Thursday, December 14, 2023 6:10 PM
> >> To: Matyas, Daniel <[email protected]>
> >> Cc: Jean Delvare <[email protected]>; Rob Herring
> >> <[email protected]>; Krzysztof Kozlowski
> >> <[email protected]>; Conor Dooley
> >> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> >> [email protected]; [email protected]; linux-
> >> [email protected]; [email protected]
> >> 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 <[email protected]>
> >>
> >> The "PEC" attribute needs to be attached to the I2C device.
> >> See lm90.c or pmbus_core.c for examples.
> >>
> >
> > I added pec_show() and pec_store() functions and created the pec file
> within the max31827_groups.
> > I did not set the flags, because I want them to be set only in pec_store.
> By default the PEC flag should not be set.
> >
>
> That is not the point. Again,
>
> >> The "PEC" attribute needs to be attached to the I2C device.
> >> See lm90.c or pmbus_core.c for examples.
>
> That is not about regmap, it is about the location of the "pec" attribute.
>

I understand that this is not about regmap. Still, I would argue, that when I am registering the device with groups, the "pec" attribute is attached.

> >> 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.
> >>
> >
> > So I am not sure about this, but it seems to me, that regmap_write is not
> sending a PEC byte and regmap_read is not checking the PEC byte by
> default. My rationale was: i2c_smbus_write_word_data() is using the
> i2c_xfer function, which has as argument the client->flags. So, if
> I2C_CLIENT_PEC is set in client->flags, that would be transmitted by
> i2c_xfer. I am not convinced about this, but lm90 and pmbus_core are not
> using regmap, so I went ahead and replaced it.
> >
> > If what I am thinking is wrong, please correct me.
> >
>
> regmap uses smbus functions to access the chip if the underlying i2c
> driver supports SMBus word operations. I fail to see the difference to your
> code.
> Sure, max31827_reg_write() executes another read after the write, but
> that is again a 16-bit operation and might we well be implemented as
> another regmap operation to read the status register.
>
> It would be possible to replace the regmap i2c code, use raw regmap code
> instead, and provide ->read and ->write callbacks in struct regmap_config,
> but I am not convinced that this would be beneficial.
>
> Either case, sorry, I can not follow your line of argument.
>
> >> 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
> >>
> >
> > Sorry for this! I thought, if I am adding client to the private structure I
> might as well delete the second parameter of init_client, because I can
> easily retrieve the device structure from client. I added this line so that
> the changes to the code are kept to a minimum.
> >
>
> You are making unnecessary changes (several unsigned int -> u16 plus this
> one), and claim this would be "so that the changes to the code are kept to
> a minimum". Really ? How does making unnecessary changes keep the
> changes to the code to a minimum ?
>
> Guenter

2023-12-18 15:43:52

by Matyas, Daniel

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



> -----Original Message-----
> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> Sent: Thursday, December 14, 2023 6:10 PM
> To: Matyas, Daniel <[email protected]>
> Cc: Jean Delvare <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley
> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> 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 <[email protected]>
>
> 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;
> >

2023-12-18 16:25:41

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

On 12/18/23 01:12, Matyas, Daniel wrote:
>
>
>> -----Original Message-----
>> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
>> Sent: Saturday, December 16, 2023 3:33 AM
>> To: Matyas, Daniel <[email protected]>
>> Cc: Jean Delvare <[email protected]>; Rob Herring
>> <[email protected]>; Krzysztof Kozlowski
>> <[email protected]>; Conor Dooley
>> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
>> [email protected]; [email protected]; linux-
>> [email protected]; [email protected]
>> Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>>
>> [External]
>>
>> On 12/15/23 12:28, Matyas, Daniel wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Guenter Roeck <[email protected]> On Behalf Of Guenter
>> Roeck
>>>> Sent: Thursday, December 14, 2023 6:10 PM
>>>> To: Matyas, Daniel <[email protected]>
>>>> Cc: Jean Delvare <[email protected]>; Rob Herring
>>>> <[email protected]>; Krzysztof Kozlowski
>>>> <[email protected]>; Conor Dooley
>>>> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
>>>> [email protected]; [email protected]; linux-
>>>> [email protected]; [email protected]
>>>> 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 <[email protected]>
>>>>
>>>> The "PEC" attribute needs to be attached to the I2C device.
>>>> See lm90.c or pmbus_core.c for examples.
>>>>
>>>
>>> I added pec_show() and pec_store() functions and created the pec file
>> within the max31827_groups.
>>> I did not set the flags, because I want them to be set only in pec_store.
>> By default the PEC flag should not be set.
>>>
>>
>> That is not the point. Again,
>>
>> >> The "PEC" attribute needs to be attached to the I2C device.
>> >> See lm90.c or pmbus_core.c for examples.
>>
>> That is not about regmap, it is about the location of the "pec" attribute.
>>
>
> I understand that this is not about regmap. Still, I would argue, that when I am registering the device with groups, the "pec" attribute is attached.
>

Sure. To the hwmon device. I asked you to attach it to the i2c device, as
implemented all other hwmon drivers supporting this attribute. I am not
inclined to make an exception for this driver, and I do not see a reason
to do so.

Guenter


2023-12-18 16:31:27

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

On 12/18/23 06:55, Matyas, Daniel wrote:
[ ... ]
>> 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.
>

The SMBus code in drivers/i2c/i2c-core-smbus.c is supposed to check
if the received PEC is correct for SMBus transfers. Are you saying
that this doesn't work, or that regmap doesn't use SMBus functions
to communicate with the chip ?

Thanks,
Guenter


2023-12-18 17:28:34

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On 12/18/23 01:08, Matyas, Daniel wrote:
>
>
>> -----Original Message-----
>> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
>> Sent: Friday, December 15, 2023 6:03 PM
>> To: Krzysztof Kozlowski <[email protected]>; Matyas, Daniel
>> <[email protected]>
>> Cc: Jean Delvare <[email protected]>; Rob Herring
>> <[email protected]>; Krzysztof Kozlowski
>> <[email protected]>; Conor Dooley
>> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
>> [email protected]; [email protected]; linux-
>> [email protected]; [email protected]
>> Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as
>> compatible string
>>
>> [External]
>>
>> On 12/15/23 00:49, Krzysztof Kozlowski wrote:
>>> On 14/12/2023 15:36, Daniel Matyas wrote:
>>>> In the device ada4224 the max31827 temperature sensor will be used,
>>>> so the default values corresponding to adaq4224_temp are the same
>> for
>>>> max31827.
>>>>
>>>> Signed-off-by: Daniel Matyas <[email protected]>
>>>
>>> Please use subject prefixes matching the subsystem. You can get them
>>> for example with `git log --oneline -- DIRECTORY_OR_FILE` on the
>>> directory your patch is touching.
>>>
>>>> ---
>>>> Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5
>> ++++-
>>>> 1 file changed, 4 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git
>>>> a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>>>> b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>>>> index f60e06ab7d0a..9f3b0839aa46 100644
>>>> --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>>>> +++
>> b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
>>>> @@ -20,6 +20,7 @@ properties:
>>>> - const: adi,max31827
>>>> - items:
>>>> - enum:
>>>> + - adi,adaq4224_temp
>>>
>>> Underscores are not allowed
>>>
>>
>> That isn't the main problem with this patch.
>> https://urldefense.com/v3/__https://github.com/analogdevicesinc/linux/
>> tree/dev_adaq4224_dts__;!!A3Ni8CS0y2Y!_2D1w1DD5sjJrNyArZYZ3QW9
>> nS8URmP6X0n6R7q1sBnDB1HPL6jROhD_w9u3fJixt2hDDNtO6VpgLM1Jka
>> Q$
>> suggests that it may be a development system which utilizes the
>> max31827.
>> From there, we can see that there is a devicetree description of a board
>> with that name which uses
>>
>> temperature1: temperature@5f {
>> compatible = "adi,adaq4224_temp";
>> reg = <0x5f>;
>> vref-supply = <&vio>;
>>
>> adi,comp-int;
>> adi,alarm-pol = <0>;
>> adi,fault-q = <1>;
>> adi,timeout-enable;
>> };
>>
>> That doesn't make sense to me. I don't know why they don't just
>> reference max31827.
>> I am most definitely not going to accept a driver change just to map
>> adi,adaq4224_temp (or adi,adaq4224-temp) to the actually used
>> temperature sensor chip. If we start accepting that, we'd end up with no
>> end of "<vendor>,<board_name>-{temp,voltage,current,power,...}"
>> compatibles.
>>
>> Looking more into the above mentioned repository/branch, an earlier
>> version of the dts file did reference adi,max31827 for that chip. It also
>> looks like there may be an adaq4224 ADC (per drivers/iio/adc/ad4630.c),
>> but that would be a SPI chip. It seems highly unlikely that a SPI ADC would
>> have a separate I2C interface connected to a temperature sensor.
>> Confusing.
>>
>> There is also some indication that this may some IP to be loaded into an
>> FPGA.
>> which utilizes an FPGA implementation of max31827 (or maybe connects
>> to one).
>> If that is the case, it should still be referenced as max31827.
>>
>> All that wasted time because of "let's provide as little as possible
>> information about what we are actually doing" :-(.
>>
>> Guenter
>
> I asked around to get some more clarification on the matter. There will be a new chip released, named adaq4224. This chip will have the max31827 implemented in the silicon, so the driver used to get temperature information would be max31827.c. The chip will have spi and i2c communication too. The other driver you mentioned, the ad4630.c will communicate through spi.
>
> Because the chip has a different name, I was asked to add a new label for the max31827, so that it will be clear for the user, that the max31827 is part of the chip.
>

It is still a max31827 core. If there is no difference in programming
to max31827, it can possibly be reflected in devicetree as
"adi,adaq4224-temp", in addition to "adi,max31827". This would not
require a change in the driver code as "adi,max31827" would still
be mandatory.

But I don't think changing the driver or device name for this purpose
would be appropriate. We don't do that for any of the other hwmon
drivers, and I really don't want to open a floodgate. The lm75 driver
supports more than 20 chips from various different vendors,
but it is still the lm75 driver. The jc42 driver supports many chips
from different vendors, but the driver only has a single
"jedec,jc-42.4-temp" binding, and devices are instantiated as "jc42".
I don't see why this one should be handled differently.

Guenter


2023-12-18 19:01:32

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

On 12/18/23 09:59, Matyas, Daniel wrote:
>
>
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> *Von:* Guenter Roeck <[email protected]> im Auftrag von Guenter Roeck <[email protected]>
> *Gesendet:* Montag, Dezember 18, 2023 6:26:57 nachm.
> *An:* Matyas, Daniel <[email protected]>
> *Cc:* Jean Delvare <[email protected]>; Rob Herring <[email protected]>; Krzysztof Kozlowski <[email protected]>; Conor Dooley <[email protected]>; Jonathan Corbet <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>
> *Betreff:* Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>
> [External]
>
> On 12/18/23 06:55, Matyas, Daniel wrote:
> [ ... ]
>>> 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.
>>
>
> The SMBus code in drivers/i2c/i2c-core-smbus.c is supposed to check
> if the received PEC is correct for SMBus transfers. Are you saying
> that this doesn't work, or that regmap doesn't use SMBus functions
> to communicate with the chip ?
>
> Thanks,
> Guenter
>
>
> I am 70% sure, that the regmap does not use SMBus functions.
>

It should.

$ git grep smbus drivers/base/regmap/regmap-i2c.c
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_read_byte_data(i2c, reg);
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: return i2c_smbus_write_byte_data(i2c, reg, val);
drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus regmap_smbus_byte = {
drivers/base/regmap/regmap-i2c.c: .reg_write = regmap_smbus_byte_reg_write,
drivers/base/regmap/regmap-i2c.c: .reg_read = regmap_smbus_byte_reg_read,
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_read_word_data(i2c, reg);
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: return i2c_smbus_write_word_data(i2c, reg, val);
drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus regmap_smbus_word = {
drivers/base/regmap/regmap-i2c.c: .reg_write = regmap_smbus_word_reg_write,
drivers/base/regmap/regmap-i2c.c: .reg_read = regmap_smbus_word_reg_read,
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_word_read_swapped(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_read_word_swapped(i2c, reg);
drivers/base/regmap/regmap-i2c.c:static int regmap_smbus_word_write_swapped(void *context, unsigned int reg,
drivers/base/regmap/regmap-i2c.c: return i2c_smbus_write_word_swapped(i2c, reg, val);
drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus regmap_smbus_word_swapped = {
drivers/base/regmap/regmap-i2c.c: .reg_write = regmap_smbus_word_write_swapped,
drivers/base/regmap/regmap-i2c.c: .reg_read = regmap_smbus_word_read_swapped,
drivers/base/regmap/regmap-i2c.c:static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
drivers/base/regmap/regmap-i2c.c: return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
drivers/base/regmap/regmap-i2c.c:static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
drivers/base/regmap/regmap-i2c.c: .write = regmap_i2c_smbus_i2c_write,
drivers/base/regmap/regmap-i2c.c: .read = regmap_i2c_smbus_i2c_read,
drivers/base/regmap/regmap-i2c.c:static int regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
drivers/base/regmap/regmap-i2c.c: return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
drivers/base/regmap/regmap-i2c.c:static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
drivers/base/regmap/regmap-i2c.c: ret = i2c_smbus_read_byte(i2c);
drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
drivers/base/regmap/regmap-i2c.c: .write = regmap_i2c_smbus_i2c_write_reg16,
drivers/base/regmap/regmap-i2c.c: .read = regmap_i2c_smbus_i2c_read_reg16,
drivers/base/regmap/regmap-i2c.c: bus = &regmap_i2c_smbus_i2c_block;
drivers/base/regmap/regmap-i2c.c: bus = &regmap_i2c_smbus_i2c_block_reg16;
drivers/base/regmap/regmap-i2c.c: bus = &regmap_smbus_word;
drivers/base/regmap/regmap-i2c.c: bus = &regmap_smbus_word_swapped;
drivers/base/regmap/regmap-i2c.c: bus = &regmap_smbus_byte;

If that doesn't work for some reason, I'd rather figure out why instead of
starting to drop regmap support.

Guenter

2023-12-19 16:49:16

by Conor Dooley

[permalink] [raw]
Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as compatible string

On Mon, Dec 18, 2023 at 09:22:19AM -0800, Guenter Roeck wrote:
> On 12/18/23 01:08, Matyas, Daniel wrote:
> >
> >
> > > -----Original Message-----
> > > From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> > > Sent: Friday, December 15, 2023 6:03 PM
> > > To: Krzysztof Kozlowski <[email protected]>; Matyas, Daniel
> > > <[email protected]>
> > > Cc: Jean Delvare <[email protected]>; Rob Herring
> > > <[email protected]>; Krzysztof Kozlowski
> > > <[email protected]>; Conor Dooley
> > > <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> > > [email protected]; [email protected]; linux-
> > > [email protected]; [email protected]
> > > Subject: Re: [PATCH 2/3] bindings: hwmon: Add adi,adaq4224_temp as
> > > compatible string
> > >
> > > [External]
> > >
> > > On 12/15/23 00:49, Krzysztof Kozlowski wrote:
> > > > On 14/12/2023 15:36, Daniel Matyas wrote:
> > > > > In the device ada4224 the max31827 temperature sensor will be used,
> > > > > so the default values corresponding to adaq4224_temp are the same
> > > for
> > > > > max31827.
> > > > >
> > > > > Signed-off-by: Daniel Matyas <[email protected]>
> > > >
> > > > Please use subject prefixes matching the subsystem. You can get them
> > > > for example with `git log --oneline -- DIRECTORY_OR_FILE` on the
> > > > directory your patch is touching.
> > > >
> > > > > ---
> > > > > Documentation/devicetree/bindings/hwmon/adi,max31827.yaml | 5
> > > ++++-
> > > > > 1 file changed, 4 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git
> > > > > a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > > > > b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > > > > index f60e06ab7d0a..9f3b0839aa46 100644
> > > > > --- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > > > > +++
> > > b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
> > > > > @@ -20,6 +20,7 @@ properties:
> > > > > - const: adi,max31827
> > > > > - items:
> > > > > - enum:
> > > > > + - adi,adaq4224_temp
> > > >
> > > > Underscores are not allowed
> > > >
> > >
> > > That isn't the main problem with this patch.
> > > https://urldefense.com/v3/__https://github.com/analogdevicesinc/linux/
> > > tree/dev_adaq4224_dts__;!!A3Ni8CS0y2Y!_2D1w1DD5sjJrNyArZYZ3QW9
> > > nS8URmP6X0n6R7q1sBnDB1HPL6jROhD_w9u3fJixt2hDDNtO6VpgLM1Jka
> > > Q$
> > > suggests that it may be a development system which utilizes the
> > > max31827.
> > > From there, we can see that there is a devicetree description of a board
> > > with that name which uses
> > >
> > > temperature1: temperature@5f {
> > > compatible = "adi,adaq4224_temp";
> > > reg = <0x5f>;
> > > vref-supply = <&vio>;
> > >
> > > adi,comp-int;
> > > adi,alarm-pol = <0>;
> > > adi,fault-q = <1>;
> > > adi,timeout-enable;
> > > };
> > >
> > > That doesn't make sense to me. I don't know why they don't just
> > > reference max31827.
> > > I am most definitely not going to accept a driver change just to map
> > > adi,adaq4224_temp (or adi,adaq4224-temp) to the actually used
> > > temperature sensor chip. If we start accepting that, we'd end up with no
> > > end of "<vendor>,<board_name>-{temp,voltage,current,power,...}"
> > > compatibles.
> > >
> > > Looking more into the above mentioned repository/branch, an earlier
> > > version of the dts file did reference adi,max31827 for that chip. It also
> > > looks like there may be an adaq4224 ADC (per drivers/iio/adc/ad4630.c),
> > > but that would be a SPI chip. It seems highly unlikely that a SPI ADC would
> > > have a separate I2C interface connected to a temperature sensor.
> > > Confusing.
> > >
> > > There is also some indication that this may some IP to be loaded into an
> > > FPGA.
> > > which utilizes an FPGA implementation of max31827 (or maybe connects
> > > to one).
> > > If that is the case, it should still be referenced as max31827.
> > >
> > > All that wasted time because of "let's provide as little as possible
> > > information about what we are actually doing" :-(.
> > >
> > > Guenter
> >
> > I asked around to get some more clarification on the matter. There will be a new chip released, named adaq4224. This chip will have the max31827 implemented in the silicon, so the driver used to get temperature information would be max31827.c. The chip will have spi and i2c communication too. The other driver you mentioned, the ad4630.c will communicate through spi.
> >
> > Because the chip has a different name, I was asked to add a new label for the max31827, so that it will be clear for the user, that the max31827 is part of the chip.
> >
>
> It is still a max31827 core. If there is no difference in programming
> to max31827, it can possibly be reflected in devicetree as
> "adi,adaq4224-temp", in addition to "adi,max31827". This would not
> require a change in the driver code as "adi,max31827" would still
> be mandatory.

I am inclined to agree, but it is very hard to review these bindings
when only partial functionality of the hardware is submitted. Then
subtract any documentation of the device from the picture and clearly
reviewing becomes quite challenging.


Attachments:
(No filename) (5.46 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-20 01:49:53

by Matyas, Daniel

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



> -----Original Message-----
> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
> Sent: Monday, December 18, 2023 9:01 PM
> To: Matyas, Daniel <[email protected]>
> Cc: Jean Delvare <[email protected]>; Rob Herring
> <[email protected]>; Krzysztof Kozlowski
> <[email protected]>; Conor Dooley
> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]
> Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>
> [External]
>
> On 12/18/23 09:59, Matyas, Daniel wrote:
> >
> >
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------------------------------------------------------------------
> > ----------
> > *Von:* Guenter Roeck <[email protected]> im Auftrag von Guenter
> Roeck
> > <[email protected]>
> > *Gesendet:* Montag, Dezember 18, 2023 6:26:57 nachm.
> > *An:* Matyas, Daniel <[email protected]>
> > *Cc:* Jean Delvare <[email protected]>; Rob Herring
> > <[email protected]>; Krzysztof Kozlowski
> > <[email protected]>; Conor Dooley
> > <[email protected]>; Jonathan Corbet <[email protected]>;
> > [email protected] <[email protected]>;
> > [email protected] <[email protected]>;
> > [email protected] <[email protected]>;
> > [email protected] <[email protected]>
> > *Betreff:* Re: [PATCH 1/3] hwmon: max31827: Add PEC support
> >
> > [External]
> >
> > On 12/18/23 06:55, Matyas, Daniel wrote:
> > [ ... ]
> >>> 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.
> >>
> >
> > The SMBus code in drivers/i2c/i2c-core-smbus.c is supposed to check if
> > the received PEC is correct for SMBus transfers. Are you saying that
> > this doesn't work, or that regmap doesn't use SMBus functions to
> > communicate with the chip ?
> >
> > Thanks,
> > Guenter
> >
> >
> > I am 70% sure, that the regmap does not use SMBus functions.
> >
>
> It should.
>
> $ git grep smbus drivers/base/regmap/regmap-i2c.c
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_byte_reg_read(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_read_byte_data(i2c, reg);
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_byte_reg_write(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: return
> i2c_smbus_write_byte_data(i2c, reg, val);
> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
> regmap_smbus_byte = {
> drivers/base/regmap/regmap-i2c.c: .reg_write =
> regmap_smbus_byte_reg_write,
> drivers/base/regmap/regmap-i2c.c: .reg_read =
> regmap_smbus_byte_reg_read,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_word_reg_read(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_read_word_data(i2c, reg);
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_word_reg_write(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: return
> i2c_smbus_write_word_data(i2c, reg, val);
> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
> regmap_smbus_word = {
> drivers/base/regmap/regmap-i2c.c: .reg_write =
> regmap_smbus_word_reg_write,
> drivers/base/regmap/regmap-i2c.c: .reg_read =
> regmap_smbus_word_reg_read,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_word_read_swapped(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_read_word_swapped(i2c, reg);
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_smbus_word_write_swapped(void *context, unsigned int reg,
> drivers/base/regmap/regmap-i2c.c: return
> i2c_smbus_write_word_swapped(i2c, reg, val);
> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
> regmap_smbus_word_swapped = {
> drivers/base/regmap/regmap-i2c.c: .reg_write =
> regmap_smbus_word_write_swapped,
> drivers/base/regmap/regmap-i2c.c: .reg_read =
> regmap_smbus_word_read_swapped,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_i2c_smbus_i2c_write(void *context, const void *data,
> drivers/base/regmap/regmap-i2c.c: return
> i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_i2c_smbus_i2c_read(void *context, const void *reg,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
> regmap_i2c_smbus_i2c_block = {
> drivers/base/regmap/regmap-i2c.c: .write =
> regmap_i2c_smbus_i2c_write,
> drivers/base/regmap/regmap-i2c.c: .read =
> regmap_i2c_smbus_i2c_read,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
> drivers/base/regmap/regmap-i2c.c: return
> i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
> drivers/base/regmap/regmap-i2c.c:static int
> regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
> drivers/base/regmap/regmap-i2c.c: ret =
> i2c_smbus_read_byte(i2c);
> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
> regmap_i2c_smbus_i2c_block_reg16 = {
> drivers/base/regmap/regmap-i2c.c: .write =
> regmap_i2c_smbus_i2c_write_reg16,
> drivers/base/regmap/regmap-i2c.c: .read =
> regmap_i2c_smbus_i2c_read_reg16,
> drivers/base/regmap/regmap-i2c.c: bus =
> &regmap_i2c_smbus_i2c_block;
> drivers/base/regmap/regmap-i2c.c: bus =
> &regmap_i2c_smbus_i2c_block_reg16;
> drivers/base/regmap/regmap-i2c.c: bus =
> &regmap_smbus_word;
> drivers/base/regmap/regmap-i2c.c: bus =
> &regmap_smbus_word_swapped;
> drivers/base/regmap/regmap-i2c.c: bus = &regmap_smbus_byte;
>
> If that doesn't work for some reason, I'd rather figure out why instead of
> starting to drop regmap support.
>
> Guenter

I tried to figure it out and this is what I came up with. The code snippet below is from drivers/base/regmap/regmap-i2c.c:

static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
const struct i2c_adapter_quirks *quirks;
const struct regmap_bus *bus = NULL;
struct regmap_bus *ret_bus;
u16 max_read = 0, max_write = 0;

if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
bus = &regmap_i2c;
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = &regmap_i2c_smbus_i2c_block;
else if (config->val_bits == 8 && config->reg_bits == 16 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = &regmap_i2c_smbus_i2c_block_reg16;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
bus = &regmap_smbus_word;
break;
case REGMAP_ENDIAN_BIG:
bus = &regmap_smbus_word_swapped;
break;
default: /* everything else is not supported */
break;
}

This is executed when regmap is initialized. My adapter has the I2C_FUNC_I2C functionality (I use a raspberry pi 4), so it seems to me like regmap_i2c is loaded as the bus. This uses i2c_transfer internally to read and write.

For PEC I need regmap_smbus_word. This uses i2c_smbus_xfer internally. Unlike i2c_transfer, i2c_smbus_xfer can be used to send and receive PEC byte.

What should I do?

Kind regards,
Daniel

2024-01-08 17:50:17

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support

On 12/19/23 17:49, Matyas, Daniel wrote:
>
>
>> -----Original Message-----
>> From: Guenter Roeck <[email protected]> On Behalf Of Guenter Roeck
>> Sent: Monday, December 18, 2023 9:01 PM
>> To: Matyas, Daniel <[email protected]>
>> Cc: Jean Delvare <[email protected]>; Rob Herring
>> <[email protected]>; Krzysztof Kozlowski
>> <[email protected]>; Conor Dooley
>> <[email protected]>; Jonathan Corbet <[email protected]>; linux-
>> [email protected]; [email protected]; linux-
>> [email protected]; [email protected]
>> Subject: Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>>
>> [External]
>>
>> On 12/18/23 09:59, Matyas, Daniel wrote:
>>>
>>>
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------------------------------------------------------------------
>>> ----------
>>> *Von:* Guenter Roeck <[email protected]> im Auftrag von Guenter
>> Roeck
>>> <[email protected]>
>>> *Gesendet:* Montag, Dezember 18, 2023 6:26:57 nachm.
>>> *An:* Matyas, Daniel <[email protected]>
>>> *Cc:* Jean Delvare <[email protected]>; Rob Herring
>>> <[email protected]>; Krzysztof Kozlowski
>>> <[email protected]>; Conor Dooley
>>> <[email protected]>; Jonathan Corbet <[email protected]>;
>>> [email protected] <[email protected]>;
>>> [email protected] <[email protected]>;
>>> [email protected] <[email protected]>;
>>> [email protected] <[email protected]>
>>> *Betreff:* Re: [PATCH 1/3] hwmon: max31827: Add PEC support
>>>
>>> [External]
>>>
>>> On 12/18/23 06:55, Matyas, Daniel wrote:
>>> [ ... ]
>>>>> 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.
>>>>
>>>
>>> The SMBus code in drivers/i2c/i2c-core-smbus.c is supposed to check if
>>> the received PEC is correct for SMBus transfers. Are you saying that
>>> this doesn't work, or that regmap doesn't use SMBus functions to
>>> communicate with the chip ?
>>>
>>> Thanks,
>>> Guenter
>>>
>>>
>>> I am 70% sure, that the regmap does not use SMBus functions.
>>>
>>
>> It should.
>>
>> $ git grep smbus drivers/base/regmap/regmap-i2c.c
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_byte_reg_read(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_read_byte_data(i2c, reg);
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_byte_reg_write(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: return
>> i2c_smbus_write_byte_data(i2c, reg, val);
>> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
>> regmap_smbus_byte = {
>> drivers/base/regmap/regmap-i2c.c: .reg_write =
>> regmap_smbus_byte_reg_write,
>> drivers/base/regmap/regmap-i2c.c: .reg_read =
>> regmap_smbus_byte_reg_read,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_word_reg_read(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_read_word_data(i2c, reg);
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_word_reg_write(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: return
>> i2c_smbus_write_word_data(i2c, reg, val);
>> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
>> regmap_smbus_word = {
>> drivers/base/regmap/regmap-i2c.c: .reg_write =
>> regmap_smbus_word_reg_write,
>> drivers/base/regmap/regmap-i2c.c: .reg_read =
>> regmap_smbus_word_reg_read,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_word_read_swapped(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_read_word_swapped(i2c, reg);
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_smbus_word_write_swapped(void *context, unsigned int reg,
>> drivers/base/regmap/regmap-i2c.c: return
>> i2c_smbus_write_word_swapped(i2c, reg, val);
>> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
>> regmap_smbus_word_swapped = {
>> drivers/base/regmap/regmap-i2c.c: .reg_write =
>> regmap_smbus_word_write_swapped,
>> drivers/base/regmap/regmap-i2c.c: .reg_read =
>> regmap_smbus_word_read_swapped,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_i2c_smbus_i2c_write(void *context, const void *data,
>> drivers/base/regmap/regmap-i2c.c: return
>> i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_i2c_smbus_i2c_read(void *context, const void *reg,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
>> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
>> regmap_i2c_smbus_i2c_block = {
>> drivers/base/regmap/regmap-i2c.c: .write =
>> regmap_i2c_smbus_i2c_write,
>> drivers/base/regmap/regmap-i2c.c: .read =
>> regmap_i2c_smbus_i2c_read,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_i2c_smbus_i2c_write_reg16(void *context, const void *data,
>> drivers/base/regmap/regmap-i2c.c: return
>> i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
>> drivers/base/regmap/regmap-i2c.c:static int
>> regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_write_byte_data(i2c, ((u16 *)reg)[0] & 0xff,
>> drivers/base/regmap/regmap-i2c.c: ret =
>> i2c_smbus_read_byte(i2c);
>> drivers/base/regmap/regmap-i2c.c:static const struct regmap_bus
>> regmap_i2c_smbus_i2c_block_reg16 = {
>> drivers/base/regmap/regmap-i2c.c: .write =
>> regmap_i2c_smbus_i2c_write_reg16,
>> drivers/base/regmap/regmap-i2c.c: .read =
>> regmap_i2c_smbus_i2c_read_reg16,
>> drivers/base/regmap/regmap-i2c.c: bus =
>> &regmap_i2c_smbus_i2c_block;
>> drivers/base/regmap/regmap-i2c.c: bus =
>> &regmap_i2c_smbus_i2c_block_reg16;
>> drivers/base/regmap/regmap-i2c.c: bus =
>> &regmap_smbus_word;
>> drivers/base/regmap/regmap-i2c.c: bus =
>> &regmap_smbus_word_swapped;
>> drivers/base/regmap/regmap-i2c.c: bus = &regmap_smbus_byte;
>>
>> If that doesn't work for some reason, I'd rather figure out why instead of
>> starting to drop regmap support.
>>
>> Guenter
>
> I tried to figure it out and this is what I came up with. The code snippet below is from drivers/base/regmap/regmap-i2c.c:
>
> static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
> const struct regmap_config *config)
> {
> const struct i2c_adapter_quirks *quirks;
> const struct regmap_bus *bus = NULL;
> struct regmap_bus *ret_bus;
> u16 max_read = 0, max_write = 0;
>
> if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
> bus = &regmap_i2c;
> else if (config->val_bits == 8 && config->reg_bits == 8 &&
> i2c_check_functionality(i2c->adapter,
> I2C_FUNC_SMBUS_I2C_BLOCK))
> bus = &regmap_i2c_smbus_i2c_block;
> else if (config->val_bits == 8 && config->reg_bits == 16 &&
> i2c_check_functionality(i2c->adapter,
> I2C_FUNC_SMBUS_I2C_BLOCK))
> bus = &regmap_i2c_smbus_i2c_block_reg16;
> else if (config->val_bits == 16 && config->reg_bits == 8 &&
> i2c_check_functionality(i2c->adapter,
> I2C_FUNC_SMBUS_WORD_DATA))
> switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
> case REGMAP_ENDIAN_LITTLE:
> bus = &regmap_smbus_word;
> break;
> case REGMAP_ENDIAN_BIG:
> bus = &regmap_smbus_word_swapped;
> break;
> default: /* everything else is not supported */
> break;
> }
>
> This is executed when regmap is initialized. My adapter has the I2C_FUNC_I2C functionality (I use a raspberry pi 4), so it seems to me like regmap_i2c is loaded as the bus. This uses i2c_transfer internally to read and write.
>
> For PEC I need regmap_smbus_word. This uses i2c_smbus_xfer internally. Unlike i2c_transfer, i2c_smbus_xfer can be used to send and receive PEC byte.
>
> What should I do?
>

It seems to me that regmap should not use functions not supporting PEC if
I2C_CLIENT_PEC is enabled on an i2c device. Of course, that is tricky if
not impossible to implement because the flag can be set at runtime but
the bus function assignment in regmap is static.

The only alternative I can think of is to define driver specific regmap
access functions to re-implement the regmap_smbus_word access functions.
That is less than perfect but better than to drop regmap access entirely.

Copying Mark Brown for additional input.

Thanks,
Guenter