hwmon: (max6650) add thermal cooling device capability [Linux 5.2]

hwmon: (max6650) add thermal cooling device capability [Linux 5.2]

This Linux kernel change "hwmon: (max6650) add thermal cooling device capability" is included in the Linux 5.2 release. This change is authored by Jean-Francois Dagenais <jeff.dagenais [at] gmail.com> on Thu Apr 18 20:57:22 2019 -0400. The commit for this change in Linux stable tree is 4f8d374 (patch).

hwmon: (max6650) add thermal cooling device capability

This allows max6650 devices to be referenced in dts as a cooling device.

The pwm value seems duplicated in cooling_dev_state but since pwm goes
through rounding logic into data->dac, it is modified and messes with
the thermal zone state algorithms. It's also better to serve a cache
value, thus avoiding periodic actual i2c traffic.

Signed-off-by: Jean-Francois Dagenais <jeff.dagenais@gmail.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

There are 91 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/hwmon/Kconfig   |  1 +
 drivers/hwmon/max6650.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 470ba66..1915a18 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -908,6 +908,7 @@ config SENSORS_MAX6642
 config SENSORS_MAX6650
    tristate "Maxim MAX6650 sensor chip"
    depends on I2C
+   depends on THERMAL || THERMAL=n
    help
      If you say yes here you get support for the MAX6650 / MAX6651
      sensor chips.
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 95b2346..e977c2f 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -40,6 +40,7 @@
 #include <linux/hwmon-sysfs.h>
 #include <linux/err.h>
 #include <linux/of_device.h>
+#include <linux/thermal.h>

 /*
  * Insmod parameters
@@ -113,6 +114,7 @@
 struct max6650_data {
    struct i2c_client *client;
    const struct attribute_group *groups[3];
+   struct thermal_cooling_device *cooling_dev;
    struct mutex update_lock;
    int nr_fans;
    char valid; /* zero until following fields are valid */
@@ -125,6 +127,7 @@ struct max6650_data {
    u8 count;
    u8 dac;
    u8 alarm;
+   unsigned long cooling_dev_state;
 };

 static const u8 tach_reg[] = {
@@ -694,6 +697,63 @@ static int max6650_init_client(struct max6650_data *data,
    return 0;
 }

+#if IS_ENABLED(CONFIG_THERMAL)
+
+static int max6650_get_max_state(struct thermal_cooling_device *cdev,
+                unsigned long *state)
+{
+   *state = 255;
+
+   return 0;
+}
+
+static int max6650_get_cur_state(struct thermal_cooling_device *cdev,
+                unsigned long *state)
+{
+   struct max6650_data *data = cdev->devdata;
+
+   *state = data->cooling_dev_state;
+
+   return 0;
+}
+
+static int max6650_set_cur_state(struct thermal_cooling_device *cdev,
+                unsigned long state)
+{
+   struct max6650_data *data = cdev->devdata;
+   struct i2c_client *client = data->client;
+   int err;
+
+   state = clamp_val(state, 0, 255);
+
+   mutex_lock(&data->update_lock);
+
+   if (data->config & MAX6650_CFG_V12)
+       data->dac = 180 - (180 * state)/255;
+   else
+       data->dac = 76 - (76 * state)/255;
+
+   err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
+
+   if (!err) {
+       max6650_set_operating_mode(data, state ?
+                          MAX6650_CFG_MODE_OPEN_LOOP :
+                          MAX6650_CFG_MODE_OFF);
+       data->cooling_dev_state = state;
+   }
+
+   mutex_unlock(&data->update_lock);
+
+   return err < 0 ? err : 0;
+}
+
+static const struct thermal_cooling_device_ops max6650_cooling_ops = {
+   .get_max_state = max6650_get_max_state,
+   .get_cur_state = max6650_get_cur_state,
+   .set_cur_state = max6650_set_cur_state,
+};
+#endif
+
 static int max6650_probe(struct i2c_client *client,
             const struct i2c_device_id *id)
 {
@@ -709,6 +769,7 @@ static int max6650_probe(struct i2c_client *client,
        return -ENOMEM;

    data->client = client;
+   i2c_set_clientdata(client, data);
    mutex_init(&data->update_lock);
    data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data;

@@ -727,7 +788,33 @@ static int max6650_probe(struct i2c_client *client,
    hwmon_dev = devm_hwmon_device_register_with_groups(dev,
                               client->name, data,
                               data->groups);
-   return PTR_ERR_OR_ZERO(hwmon_dev);
+   err = PTR_ERR_OR_ZERO(hwmon_dev);
+   if (err)
+       return err;
+
+#if IS_ENABLED(CONFIG_THERMAL)
+   data->cooling_dev =
+       thermal_of_cooling_device_register(client->dev.of_node,
+                          client->name, data,
+                          &max6650_cooling_ops);
+   if (IS_ERR(data->cooling_dev))
+       dev_warn(&client->dev,
+            "thermal cooling device register failed: %ld\n",
+            PTR_ERR(data->cooling_dev));
+   else
+       thermal_cdev_update(data->cooling_dev);
+#endif
+   return 0;
+}
+
+static int max6650_remove(struct i2c_client *client)
+{
+   struct max6650_data *data = i2c_get_clientdata(client);
+
+   if (!IS_ERR(data->cooling_dev))
+       thermal_cooling_device_unregister(data->cooling_dev);
+
+   return 0;
 }

 static const struct i2c_device_id max6650_id[] = {
@@ -743,6 +830,7 @@ static int max6650_probe(struct i2c_client *client,
        .of_match_table = of_match_ptr(max6650_dt_match),
    },
    .probe      = max6650_probe,
+   .remove     = max6650_remove,
    .id_table   = max6650_id,
 };

Leave a Reply

Your email address will not be published. Required fields are marked *