[PATCH 1/2] drivers: hwmon: max31827: Add PEC support

From: Radu Sabau
Date: Wed May 22 2024 - 08:40:56 EST


Add support for PEC by attaching PEC attribute to the i2c device.
Add pec_store and pec_show function for accesing the "pec" file.

Signed-off-by: Radu Sabau <radu.sabau@xxxxxxxxxx>
---
Documentation/hwmon/max31827.rst | 13 ++++-
drivers/hwmon/max31827.c | 95 +++++++++++++++++++++++++++-----
2 files changed, 92 insertions(+), 16 deletions(-)

diff --git a/Documentation/hwmon/max31827.rst b/Documentation/hwmon/max31827.rst
index 44ab9dc064cb..9c11a9518c67 100644
--- a/Documentation/hwmon/max31827.rst
+++ b/Documentation/hwmon/max31827.rst
@@ -131,7 +131,14 @@ 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 Support
+-----------
+
+When reading a register value, the PEC byte is computed and sent by the chip.
+
+PEC on word data transaction respresents a signifcant increase in bandwitdh
+usage (+33% for both write and reads) in normal conditions.

-PEC is not implemented.
+Since this operation implies there will be an extra delay to each
+transaction, PEC can be disabled or enabled through sysfs.
+Just write 1 to the "pec" file for enabling PEC and 0 for disabling it.
diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
index f8a13b30f100..16a1524413db 100644
--- a/drivers/hwmon/max31827.c
+++ b/drivers/hwmon/max31827.c
@@ -11,19 +11,20 @@
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
-#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>

-#define MAX31827_T_REG 0x0
+#define MAX31827_T_REG 0x0
#define MAX31827_CONFIGURATION_REG 0x2
-#define MAX31827_TH_REG 0x4
-#define MAX31827_TL_REG 0x6
-#define MAX31827_TH_HYST_REG 0x8
-#define MAX31827_TL_HYST_REG 0xA
+#define MAX31827_TH_REG 0x4
+#define MAX31827_TL_REG 0x6
+#define MAX31827_TH_HYST_REG 0x8
+#define MAX31827_TL_HYST_REG 0xA

#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)
@@ -46,6 +47,8 @@
#define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000)
#define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0)

+#define DEBUG_FS_DATA_MAX 16
+
enum chips { max31827 = 1, max31828, max31829 };

enum max31827_cnv {
@@ -151,8 +154,8 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg,
goto unlock;

ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
- MAX31827_CONFIGURATION_CNV_RATE_MASK,
- cnv_rate);
+ MAX31827_CONFIGURATION_CNV_RATE_MASK,
+ cnv_rate);

unlock:
mutex_unlock(&st->lock);
@@ -344,7 +347,7 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_enable:
if (val >> 1)
- return -EINVAL;
+ return -EOPNOTSUPP;

mutex_lock(&st->lock);
/**
@@ -384,7 +387,7 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
case hwmon_chip:
if (attr == hwmon_chip_update_interval) {
if (!st->enable)
- return -EINVAL;
+ return -EOPNOTSUPP;

/*
* Convert the desired conversion rate into register
@@ -472,11 +475,60 @@ static ssize_t temp1_resolution_store(struct device *dev,

return ret ? ret : count;
}
-
static DEVICE_ATTR_RW(temp1_resolution);

+static ssize_t pec_show(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ 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 = to_i2c_client(dev);
+ unsigned int val, val2;
+ int err;
+
+ err = kstrtouint(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val2 = FIELD_PREP(MAX31827_CONFIGURATION_PEC_EN_MASK, !!val);
+
+ switch (val) {
+ case 0:
+ err = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
+ MAX31827_CONFIGURATION_PEC_EN_MASK,
+ val2);
+ if (err)
+ return err;
+
+ client->flags &= ~I2C_CLIENT_PEC;
+ break;
+ case 1:
+ err = regmap_update_bits(st->regmap, 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(pec);
+
static struct attribute *max31827_attrs[] = {
&dev_attr_temp1_resolution.attr,
+ &dev_attr_pec.attr,
NULL
};
ATTRIBUTE_GROUPS(max31827);
@@ -493,8 +545,8 @@ static int max31827_init_client(struct max31827_state *st,
struct device *dev)
{
struct fwnode_handle *fwnode;
+ unsigned int data, lsb_idx;
unsigned int res = 0;
- u32 data, lsb_idx;
enum chips type;
bool prop;
int ret;
@@ -578,6 +630,11 @@ static int max31827_init_client(struct max31827_state *st,
return regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, res);
}

+static void max31827_remove_pec(void *dev)
+{
+ device_remove_file(dev, &dev_attr_pec);
+}
+
static const struct hwmon_channel_info *max31827_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_MIN |
HWMON_T_MIN_HYST | HWMON_T_MIN_ALARM |
@@ -627,6 +684,16 @@ static int max31827_probe(struct i2c_client *client)
if (err)
return err;

+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) {
+ err = device_create_file(dev, &dev_attr_pec);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(dev, max31827_remove_pec, dev);
+ if (err)
+ return err;
+ }
+
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, st,
&max31827_chip_info,
max31827_groups);
@@ -652,15 +719,17 @@ static const struct of_device_id max31827_of_match[] = {
MODULE_DEVICE_TABLE(of, max31827_of_match);

static struct i2c_driver max31827_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "max31827",
.of_match_table = max31827_of_match,
},
- .probe = max31827_probe,
+ .probe_new = max31827_probe,
.id_table = max31827_i2c_ids,
};
module_i2c_driver(max31827_driver);

MODULE_AUTHOR("Daniel Matyas <daniel.matyas@xxxxxxxxxx>");
+MODULE_AUTHOR("Radu Sabau <radu.sabau@xxxxxxxxxx>");
MODULE_DESCRIPTION("Maxim MAX31827 low-power temperature switch driver");
MODULE_LICENSE("GPL");
--
2.34.1