From: "Filip Gołaś" <filip.golas@3mdeb.com>
To: "Michał Kopeć" <michal.kopec@3mdeb.com>,
"Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: platform-driver-x86@vger.kernel.org, linux-hwmon@vger.kernel.org,
linux-kernel@vger.kernel.org,
"Guenter Roeck" <linux@roeck-us.net>,
"Filip Gołaś" <filip.golas@3mdeb.com>
Subject: [PATCH 3/3] platform/x86: dasharo-acpi: Add EC fan curve support
Date: Fri, 13 Nov 2026 08:43:47 +0100 [thread overview]
Message-ID: <20261113074347.2301465-4-filip.golas@3mdeb.com> (raw)
In-Reply-To: <20261113074347.2301465-1-filip.golas@3mdeb.com>
Expose the four-point fan curve run by the EC as pwmN_auto_pointM_temp
and pwmN_auto_pointM_pwm, so that userspace can read and change it.
The EC validates the curve on every write and keeps running the
previous one if the new curve is invalid.
On shutdown, ask the firmware to store the current curve, so that it is
restored on the next boot.
Fan curve control is only exposed for fans on which the firmware reports
support for it.
Signed-off-by: Filip Gołaś <filip.golas@3mdeb.com>
---
drivers/platform/x86/dasharo-acpi.c | 160 +++++++++++++++++++++++++++-
1 file changed, 159 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/dasharo-acpi.c b/drivers/platform/x86/dasharo-acpi.c
index 29afcf78564c..7b21577158cb 100644
--- a/drivers/platform/x86/dasharo-acpi.c
+++ b/drivers/platform/x86/dasharo-acpi.c
@@ -6,6 +6,8 @@
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
@@ -378,6 +380,150 @@ static const struct hwmon_chip_info dasharo_hwmon_chip_info = {
.info = dasharo_hwmon_info,
};
+static int dasharo_curve_point_store(struct dasharo_data *data, int channel, const char *method,
+ unsigned int point, u64 value)
+{
+ u64 args[2] = { point, value };
+ u64 ret;
+ int err;
+
+ // set of temp/duty
+ err = dasharo_call(data, method, DASHARO_FEATURE_FAN_POINTS, channel, args, 2, &ret);
+ if (err)
+ return err;
+ if (ret)
+ return -EPERM;
+
+ // commit
+ err = dasharo_call(data, "CFCV", DASHARO_FEATURE_FAN_POINTS, channel, NULL, 0, &ret);
+ if (err)
+ return err;
+
+ // The EC rejected the curve
+ return ret ? -EINVAL : 0;
+}
+
+static ssize_t curve_point_temp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct dasharo_data *data = dev_get_drvdata(dev);
+ u64 point = sattr->index;
+ u64 val;
+ int err;
+
+ err = dasharo_call(data, "GFPT", DASHARO_FEATURE_FAN_POINTS, sattr->nr, &point, 1, &val);
+ if (err)
+ return err;
+
+ return sysfs_emit(buf, "%llu\n", val * MILLIDEGREE_PER_DEGREE);
+}
+
+static ssize_t curve_point_temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct dasharo_data *data = dev_get_drvdata(dev);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ val /= MILLIDEGREE_PER_DEGREE;
+ if (val < 0 || val > U8_MAX)
+ return -EINVAL;
+
+ err = dasharo_curve_point_store(data, sattr->nr, "SFPT", sattr->index, val);
+ return err ?: count;
+}
+
+static ssize_t curve_point_pwm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct dasharo_data *data = dev_get_drvdata(dev);
+ u64 point = sattr->index;
+ u64 val;
+ int err;
+
+ err = dasharo_call(data, "GFPD", DASHARO_FEATURE_FAN_POINTS, sattr->nr, &point, 1, &val);
+ if (err)
+ return err;
+
+ return sysfs_emit(buf, "%llu\n", DIV_ROUND_CLOSEST(val * 255, 100));
+}
+
+static ssize_t curve_point_pwm_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct dasharo_data *data = dev_get_drvdata(dev);
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err)
+ return err;
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ err = dasharo_curve_point_store(data, sattr->nr, "SFPD", sattr->index,
+ DIV_ROUND_CLOSEST(val * 100, 255));
+ return err ?: count;
+}
+
+#define DASHARO_POINT_ATTRS(ch, pt) \
+ SENSOR_DEVICE_ATTR_2_RW(pwm##ch##_auto_point##pt##_temp, curve_point_temp, \
+ (ch) - 1, (pt) - 1); \
+ SENSOR_DEVICE_ATTR_2_RW(pwm##ch##_auto_point##pt##_pwm, curve_point_pwm, \
+ ch - 1, pt - 1)
+
+#define DASHARO_POINT_ATTR_PTRS(ch, pt) \
+ &sensor_dev_attr_pwm##ch##_auto_point##pt##_temp.dev_attr.attr, \
+ &sensor_dev_attr_pwm##ch##_auto_point##pt##_pwm.dev_attr.attr
+
+DASHARO_POINT_ATTRS(1, 1);
+DASHARO_POINT_ATTRS(1, 2);
+DASHARO_POINT_ATTRS(1, 3);
+DASHARO_POINT_ATTRS(1, 4);
+DASHARO_POINT_ATTRS(2, 1);
+DASHARO_POINT_ATTRS(2, 2);
+DASHARO_POINT_ATTRS(2, 3);
+DASHARO_POINT_ATTRS(2, 4);
+
+static struct attribute *dasharo_point_attrs[] = {
+ DASHARO_POINT_ATTR_PTRS(1, 1),
+ DASHARO_POINT_ATTR_PTRS(1, 2),
+ DASHARO_POINT_ATTR_PTRS(1, 3),
+ DASHARO_POINT_ATTR_PTRS(1, 4),
+ DASHARO_POINT_ATTR_PTRS(2, 1),
+ DASHARO_POINT_ATTR_PTRS(2, 2),
+ DASHARO_POINT_ATTR_PTRS(2, 3),
+ DASHARO_POINT_ATTR_PTRS(2, 4),
+ NULL
+};
+
+static umode_t dasharo_curve_point_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct dasharo_data *data = dev_get_drvdata(dev);
+ struct device_attribute *dattr = container_of(attr, struct device_attribute, attr);
+ int channel = to_sensor_dev_attr_2(dattr)->nr;
+
+ return channel < data->caps_found[DASHARO_FEATURE_FAN_POINTS] ? attr->mode : 0;
+}
+
+static const struct attribute_group dasharo_point_group = {
+ .attrs = dasharo_point_attrs,
+ .is_visible = dasharo_curve_point_is_visible,
+};
+
+static const struct attribute_group *dasharo_groups[] = {
+ &dasharo_point_group,
+ NULL
+};
+
static void dasharo_fill_feature_caps(struct dasharo_data *data, enum dasharo_feature feat)
{
struct dasharo_capability *cap;
@@ -413,16 +559,27 @@ static int dasharo_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
data->pdev = pdev;
+ platform_set_drvdata(pdev, data);
for (unsigned int i = 0; i < DASHARO_FEATURE_MAX; ++i)
dasharo_fill_feature_caps(data, i);
hwmon = devm_hwmon_device_register_with_info(&pdev->dev, "dasharo_acpi", data,
- &dasharo_hwmon_chip_info, NULL);
+ &dasharo_hwmon_chip_info, dasharo_groups);
return PTR_ERR_OR_ZERO(hwmon);
}
+static void dasharo_shutdown(struct platform_device *pdev)
+{
+ struct dasharo_data *data = platform_get_drvdata(pdev);
+ u64 ret;
+
+ /* Let the firmware persist the fan curve the EC runs */
+ if (data->caps_found[DASHARO_FEATURE_FAN_POINTS])
+ dasharo_call(data, "SFCS", DASHARO_FEATURE_FAN_POINTS, 0, NULL, 0, &ret);
+}
+
static const struct acpi_device_id dasharo_device_ids[] = {
{"DSHR0001", 0},
{}
@@ -435,6 +592,7 @@ static struct platform_driver dasharo_driver = {
.acpi_match_table = dasharo_device_ids,
},
.probe = dasharo_probe,
+ .shutdown = dasharo_shutdown,
};
module_platform_driver(dasharo_driver);
--
2.56.0
prev parent reply other threads:[~2026-11-13 7:44 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 7:43 [PATCH 0/3] platform/x86: dasharo-acpi: Add fan control Filip Gołaś
2026-11-13 7:43 ` [PATCH 1/3] platform/x86: dasharo-acpi: Extract dasharo_call() from dasharo_read_channel() Filip Gołaś
2026-11-13 7:43 ` [PATCH 2/3] platform/x86: dasharo-acpi: Add fan mode and PWM write support Filip Gołaś
2026-11-13 7:43 ` Filip Gołaś [this message]
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=20261113074347.2301465-4-filip.golas@3mdeb.com \
--to=filip.golas@3mdeb.com \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=michal.kopec@3mdeb.com \
--cc=platform-driver-x86@vger.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®