From: Luiz Angelo Daros de Luca <luizluca@gmail.com>
To: Guenter Roeck <linux@roeck-us.net>, Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>, de Luca <luizluca@gmail.com>
Cc: linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 3/4] hwmon: (adt7470) Add thermal cooling device support
Date: Thu, 16 Jul 2026 18:21:40 -0300 [thread overview]
Message-ID: <20260716-adt7470_thermalzone-v1-3-cc55ef35edde@gmail.com> (raw)
In-Reply-To: <20260716-adt7470_thermalzone-v1-0-cc55ef35edde@gmail.com>
The ADT7470 has four PWM outputs that can be used to control fans.
Register each PWM output as an optional thermal cooling device via
Device Tree so it can be referenced by thermal zones.
When the thermal subsystem sets a cooling state, the driver switches
the corresponding PWM channel to manual mode so that the requested duty
cycle takes effect.
Registration is optional: when the "#cooling-cells" property is absent,
devm_thermal_of_child_cooling_device_register() returns -ENODEV and the
driver silently skips that PWM channel.
The PWM cache is populated with an explicit adt7470_update_sensors()
call before the cooling devices are registered, so that an early
get_cur_state() call from the thermal core does not observe stale
(zeroed) values.
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
drivers/hwmon/adt7470.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 4ac292cd7f47..b865f4bd50f8 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -22,6 +22,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
+#include <linux/thermal.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -142,6 +143,13 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_FREQ_MASK 0x70
#define ADT7470_FREQ_SHIFT 4
+struct adt7470_data;
+
+struct adt7470_cooling_device {
+ struct adt7470_data *data;
+ int pwm_index;
+};
+
struct adt7470_data {
struct regmap *regmap;
struct mutex lock;
@@ -171,6 +179,8 @@ struct adt7470_data {
struct task_struct *auto_update;
unsigned int auto_update_interval;
+
+ struct adt7470_cooling_device cooling_devices[ADT7470_PWM_COUNT];
};
/*
@@ -846,6 +856,107 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
return err;
}
+static int adt7470_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = ADT7470_PWM_MAX;
+ return 0;
+}
+
+static int adt7470_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ struct adt7470_cooling_device *cooling = cdev->devdata;
+ struct adt7470_data *data = cooling->data;
+
+ mutex_lock(&data->lock);
+ *state = data->pwm[cooling->pwm_index];
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int adt7470_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ struct adt7470_cooling_device *cooling = cdev->devdata;
+ struct adt7470_data *data = cooling->data;
+ unsigned int pwm_auto_reg_mask;
+ int err;
+
+ if (cooling->pwm_index & 1)
+ pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK;
+ else
+ pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK;
+
+ state = clamp_val(state, 0, ADT7470_PWM_MAX);
+
+ mutex_lock(&data->lock);
+
+ if (data->pwm[cooling->pwm_index] == state &&
+ data->pwm_automatic[cooling->pwm_index] == 0) {
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+
+ /* Put the PWM channel in manual mode before updating it. */
+ err = regmap_update_bits(data->regmap,
+ ADT7470_REG_PWM_CFG(cooling->pwm_index),
+ pwm_auto_reg_mask, 0);
+ if (err < 0)
+ goto out;
+
+ data->pwm_automatic[cooling->pwm_index] = 0;
+
+ err = regmap_write(data->regmap,
+ ADT7470_REG_PWM(cooling->pwm_index), state);
+
+ if (err < 0)
+ goto out;
+
+ data->pwm[cooling->pwm_index] = state;
+out:
+ mutex_unlock(&data->lock);
+
+ return err;
+}
+
+static const struct thermal_cooling_device_ops adt7470_cooling_ops = {
+ .get_max_state = adt7470_get_max_state,
+ .get_cur_state = adt7470_get_cur_state,
+ .set_cur_state = adt7470_set_cur_state,
+};
+
+static int adt7470_register_cooling_devices(struct device *dev,
+ struct adt7470_data *data)
+{
+ int i;
+
+ for (i = 0; i < ADT7470_PWM_COUNT; i++) {
+ struct thermal_cooling_device *cdev;
+ char cdev_name[THERMAL_NAME_LENGTH];
+
+ data->cooling_devices[i].data = data;
+ data->cooling_devices[i].pwm_index = i;
+
+ snprintf(cdev_name, sizeof(cdev_name), "adt7470-pwm%d", i);
+ cdev = devm_thermal_of_child_cooling_device_register(dev,
+ dev->of_node, cdev_name,
+ &data->cooling_devices[i],
+ &adt7470_cooling_ops);
+
+ if (IS_ERR(cdev)) {
+ if (PTR_ERR(cdev) == -ENODEV)
+ continue;
+ return dev_warn_probe(dev, PTR_ERR(cdev),
+ "failed to register cooling device %s\n",
+ cdev_name);
+ }
+ }
+
+ return 0;
+}
+
static ssize_t pwm_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
@@ -1281,6 +1392,17 @@ static int adt7470_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+ /* fill the cache before registering the cooling devices */
+ err = adt7470_update_sensors(data);
+ if (err)
+ return err;
+
+ err = adt7470_register_cooling_devices(dev, data);
+ if (err)
+ return err;
+ }
+
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update))
--
2.55.0
next prev parent reply other threads:[~2026-07-16 21:22 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-16 21:21 [PATCH 0/4] hwmon: (adt7470) Add thermal framework support Luiz Angelo Daros de Luca
2026-07-16 21:21 ` [PATCH 1/4] dt-bindings: hwmon: add binding for adi,adt7470 Luiz Angelo Daros de Luca
2026-07-17 2:33 ` Luiz Angelo Daros de Luca
2026-07-22 17:56 ` Rob Herring
2026-07-22 18:08 ` Guenter Roeck
2026-07-22 17:58 ` Rob Herring
2026-07-16 21:21 ` [PATCH 2/4] hwmon: (adt7470) Add ADT7470_PWM_MAX macro Luiz Angelo Daros de Luca
2026-07-16 21:21 ` Luiz Angelo Daros de Luca [this message]
2026-07-16 21:21 ` [PATCH 4/4] hwmon: (adt7470) Add thermal zone sensor support Luiz Angelo Daros de Luca
2026-07-16 23:12 ` Guenter Roeck
2026-07-17 2:48 ` Luiz Angelo Daros de Luca
2026-07-16 23:14 ` [PATCH 0/4] hwmon: (adt7470) Add thermal framework support Guenter Roeck
2026-07-16 23:19 ` Luiz Angelo Daros de Luca
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260716-adt7470_thermalzone-v1-3-cc55ef35edde@gmail.com \
--to=luizluca@gmail.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®