hwmon: (max31827) Add custom attribute for resolution
authorDaniel Matyas <daniel.matyas@analog.com>
Tue, 31 Oct 2023 18:21:57 +0000 (20:21 +0200)
committerGuenter Roeck <linux@roeck-us.net>
Mon, 11 Dec 2023 14:42:58 +0000 (06:42 -0800)
Added custom channel-specific (temp1) attribute for resolution. The wait
time for a conversion in one-shot mode (enable = 0) depends on the
resolution.

When resolution is 12-bit, the conversion time is 140ms, but the minimum
update_interval is 125ms. Handled this problem by waiting an additional
15ms (125ms + 15ms = 140ms).

Added 'mask' parameter to the shutdown_write() function. Now it can
either write or update bits, depending on the value of mask. This is
needed, because for alarms a write is necessary, but for resolution only
the resolution bits should be updated.

Signed-off-by: Daniel Matyas <daniel.matyas@analog.com>
Link: https://lore.kernel.org/r/20231031182158.124608-5-daniel.matyas@analog.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Documentation/hwmon/max31827.rst
drivers/hwmon/max31827.c

index a8bbfb85dd021fef1df7af7f722f8f54df7a7224..44ab9dc064cb38575f49028c27c6a497db6cb744 100644 (file)
@@ -90,11 +90,28 @@ the data sheet are:
 
 Enabling the device when it is already enabled has the side effect of setting
 the conversion frequency to 1 conv/s. The conversion time varies depending on
-the resolution. The conversion time doubles with every bit of increased
-resolution. For 10 bit resolution 35ms are needed, while for 12 bit resolution
-(default) 140ms. When chip is in shutdown mode and a read operation is
-requested, one-shot is triggered, the device waits for 140 (conversion time) ms,
-and only after that is the temperature value register read.
+the resolution.
+
+The conversion time doubles with every bit of increased resolution. The
+available resolutions are:
+
+- 8 bit -> 8.75 ms conversion time
+- 9 bit -> 17.5 ms conversion time
+- 10 bit -> 35 ms conversion time
+- 12 bit (default) -> 140 ms conversion time
+
+There is a temp1_resolution attribute which indicates the unit change in the
+input temperature in milli-degrees C.
+
+- 1000 mC -> 8 bit
+- 500 mC -> 9 bit
+- 250 mC -> 10 bit
+- 62 mC -> 12 bit (default) - actually this is 62.5, but the fil returns 62
+
+When chip is in shutdown mode and a read operation is requested, one-shot is
+triggered, the device waits for <conversion time> ms, and only after that is
+the temperature value register read. Note that the conversion times are rounded
+up to the nearest possible integer.
 
 The LSB of the temperature values is 0.0625 degrees Celsius, but the values of
 the temperatures are displayed in milli-degrees. This means, that some data is
@@ -117,4 +134,4 @@ corresponding status bits.
 Notes
 -----
 
-PEC and resolution are not implemented.
+PEC is not implemented.
index 6b7bfacf6f0ad1d062874df639d37f1ce6b87691..4a8c3e37c5d3235ae7da212f50837a8bf99782f8 100644 (file)
@@ -37,6 +37,9 @@
 #define MAX31827_FLT_Q_1       0x0
 #define MAX31827_FLT_Q_4       0x2
 
+#define MAX31827_8_BIT_CNV_TIME                9
+#define MAX31827_9_BIT_CNV_TIME                18
+#define MAX31827_10_BIT_CNV_TIME       35
 #define MAX31827_12_BIT_CNV_TIME       140
 
 #define MAX31827_16_BIT_TO_M_DGR(x)    (sign_extend32(x, 15) * 1000 / 16)
@@ -65,6 +68,27 @@ static const u16 max31827_conversions[] = {
        [MAX31827_CNV_8_HZ] = 125,
 };
 
+enum max31827_resolution {
+       MAX31827_RES_8_BIT = 0,
+       MAX31827_RES_9_BIT,
+       MAX31827_RES_10_BIT,
+       MAX31827_RES_12_BIT,
+};
+
+static const u16 max31827_resolutions[] = {
+       [MAX31827_RES_8_BIT] = 1000,
+       [MAX31827_RES_9_BIT] = 500,
+       [MAX31827_RES_10_BIT] = 250,
+       [MAX31827_RES_12_BIT] = 62,
+};
+
+static const u16 max31827_conv_times[] = {
+       [MAX31827_RES_8_BIT] = MAX31827_8_BIT_CNV_TIME,
+       [MAX31827_RES_9_BIT] = MAX31827_9_BIT_CNV_TIME,
+       [MAX31827_RES_10_BIT] = MAX31827_10_BIT_CNV_TIME,
+       [MAX31827_RES_12_BIT] = MAX31827_12_BIT_CNV_TIME,
+};
+
 struct max31827_state {
        /*
         * Prevent simultaneous access to the i2c client.
@@ -72,6 +96,8 @@ struct max31827_state {
        struct mutex lock;
        struct regmap *regmap;
        bool enable;
+       unsigned int resolution;
+       unsigned int update_interval;
 };
 
 static const struct regmap_config max31827_regmap = {
@@ -88,9 +114,9 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg,
        int ret;
 
        /*
-        * Before the Temperature Threshold Alarm and Alarm Hysteresis Threshold
-        * register values are changed over I2C, the part must be in shutdown
-        * mode.
+        * Before the Temperature Threshold Alarm, Alarm Hysteresis Threshold
+        * and Resolution bits from Configuration register are changed over I2C,
+        * the part must be in shutdown mode.
         *
         * Mutex is used to ensure, that some other process doesn't change the
         * configuration register.
@@ -208,9 +234,18 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type,
                                        mutex_unlock(&st->lock);
                                        return ret;
                                }
-
-                               msleep(MAX31827_12_BIT_CNV_TIME);
+                               msleep(max31827_conv_times[st->resolution]);
                        }
+
+                       /*
+                        * For 12-bit resolution the conversion time is 140 ms,
+                        * thus an additional 15 ms is needed to complete the
+                        * conversion: 125 ms + 15 ms = 140 ms
+                        */
+                       if (max31827_resolutions[st->resolution] == 12 &&
+                           st->update_interval == 125)
+                               usleep_range(15000, 20000);
+
                        ret = regmap_read(st->regmap, MAX31827_T_REG, &uval);
 
                        mutex_unlock(&st->lock);
@@ -367,10 +402,14 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
                        res = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK,
                                         res);
 
-                       return regmap_update_bits(st->regmap,
-                                                 MAX31827_CONFIGURATION_REG,
-                                                 MAX31827_CONFIGURATION_CNV_RATE_MASK,
-                                                 res);
+                       ret = regmap_update_bits(st->regmap,
+                                                MAX31827_CONFIGURATION_REG,
+                                                MAX31827_CONFIGURATION_CNV_RATE_MASK,
+                                                res);
+                       if (ret)
+                               return ret;
+
+                       st->update_interval = val;
                }
                break;
 
@@ -378,9 +417,70 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
                return -EOPNOTSUPP;
        }
 
-       return -EOPNOTSUPP;
+       return 0;
+}
+
+static ssize_t temp1_resolution_show(struct device *dev,
+                                    struct device_attribute *devattr,
+                                    char *buf)
+{
+       struct max31827_state *st = dev_get_drvdata(dev);
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &val);
+       if (ret)
+               return ret;
+
+       val = FIELD_GET(MAX31827_CONFIGURATION_RESOLUTION_MASK, val);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", max31827_resolutions[val]);
+}
+
+static ssize_t temp1_resolution_store(struct device *dev,
+                                     struct device_attribute *devattr,
+                                     const char *buf, size_t count)
+{
+       struct max31827_state *st = dev_get_drvdata(dev);
+       unsigned int idx = 0;
+       unsigned int val;
+       int ret;
+
+       ret = kstrtouint(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       /*
+        * Convert the desired resolution into register
+        * bits. idx is already initialized with 0.
+        *
+        * This was inspired by lm73 driver.
+        */
+       while (idx < ARRAY_SIZE(max31827_resolutions) &&
+              val < max31827_resolutions[idx])
+               idx++;
+
+       if (idx == ARRAY_SIZE(max31827_resolutions))
+               idx = ARRAY_SIZE(max31827_resolutions) - 1;
+
+       st->resolution = idx;
+
+       ret = shutdown_write(st, MAX31827_CONFIGURATION_REG,
+                            MAX31827_CONFIGURATION_RESOLUTION_MASK,
+                            FIELD_PREP(MAX31827_CONFIGURATION_RESOLUTION_MASK,
+                                       idx));
+
+       return ret ? ret : count;
 }
 
+static DEVICE_ATTR_RW(temp1_resolution);
+
+static struct attribute *max31827_attrs[] = {
+       &dev_attr_temp1_resolution.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(max31827);
+
 static const struct i2c_device_id max31827_i2c_ids[] = {
        { "max31827", max31827 },
        { "max31828", max31828 },
@@ -529,7 +629,7 @@ static int max31827_probe(struct i2c_client *client)
 
        hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, st,
                                                         &max31827_chip_info,
-                                                        NULL);
+                                                        max31827_groups);
 
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }