]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ACPI: fan: Add basic notification support
authorArmin Wolf <W_Armin@gmx.de>
Fri, 24 Oct 2025 18:38:22 +0000 (20:38 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 27 Oct 2025 19:56:01 +0000 (20:56 +0100)
The ACPI specification states that the platform firmware can notify
the ACPI fan device that the fan speed has changed an that the _FST
control method should be reevaluated. Add support for this mechanism
to prepare for future changes.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://patch.msgid.link/20251024183824.5656-2-W_Armin@gmx.de
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/fan_core.c

index 46e7fe7a506d5cc57977f45a7d3f3202ccffc25e..9ee4ef2d6dbcd1d79548509f272c1e7fd94cb70f 100644 (file)
@@ -19,6 +19,8 @@
 
 #include "fan.h"
 
+#define ACPI_FAN_NOTIFY_STATE_CHANGED  0x80
+
 static const struct acpi_device_id fan_device_ids[] = {
        ACPI_FAN_DEVICE_IDS,
        {"", 0},
@@ -308,6 +310,50 @@ err:
        return status;
 }
 
+static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context)
+{
+       struct device *dev = context;
+       struct acpi_fan_fst fst;
+       int ret;
+
+       switch (event) {
+       case ACPI_FAN_NOTIFY_STATE_CHANGED:
+               /*
+                * The ACPI specification says that we must evaluate _FST when we
+                * receive an ACPI event indicating that the fan state has changed.
+                */
+               ret = acpi_fan_get_fst(handle, &fst);
+               if (ret < 0)
+                       dev_err(dev, "Error retrieving current fan status: %d\n", ret);
+
+               acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0);
+               break;
+       default:
+               dev_dbg(dev, "Unsupported ACPI notification 0x%x\n", event);
+               break;
+       }
+}
+
+static void acpi_fan_notify_remove(void *data)
+{
+       struct acpi_fan *fan = data;
+
+       acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler);
+}
+
+static int devm_acpi_fan_notify_init(struct device *dev)
+{
+       struct acpi_fan *fan = dev_get_drvdata(dev);
+       acpi_status status;
+
+       status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY,
+                                            acpi_fan_notify_handler, dev);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan);
+}
+
 static int acpi_fan_probe(struct platform_device *pdev)
 {
        int result = 0;
@@ -351,6 +397,10 @@ static int acpi_fan_probe(struct platform_device *pdev)
                if (result)
                        return result;
 
+               result = devm_acpi_fan_notify_init(&pdev->dev);
+               if (result)
+                       return result;
+
                result = acpi_fan_create_attributes(device);
                if (result)
                        return result;