]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ACPI: fan: Add additional attributes for fine grain control
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Fri, 11 Feb 2022 16:09:31 +0000 (08:09 -0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 25 Feb 2022 19:49:30 +0000 (20:49 +0100)
Add additional attributes, which helps in implementing algorithm in
the user space to optimize fan control. These attributes are presented
in the same directory as the existing performance state attributes.

Additional attributes:

1. Support of fine grain control
Publish support of presence of fine grain control so that fan speed
can be tuned correctly. This attribute is called "fine_grain_control".

2. fan speed
Publish the actual fan rpm in sysfs. Knowing fan rpm is helpful to
reduce noise level and use passive control instead. Also fan performance
may not be same over time, so the same control value may not be enough
to run the fan at a speed. So a feedback value of speed is helpful. This
sysfs attribute is called "fan_speed_rpm".

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/fan.h
drivers/acpi/fan_attr.c
drivers/acpi/fan_core.c

index 4c01be2e3b779aa9b0c3192fbe4c2979c0522660..44728529a5b6bca99c19367b156cbadbea71433d 100644 (file)
@@ -48,8 +48,11 @@ struct acpi_fan {
        struct acpi_fan_fps *fps;
        int fps_count;
        struct thermal_cooling_device *cdev;
+       struct device_attribute fst_speed;
+       struct device_attribute fine_grain_control;
 };
 
+int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst);
 int acpi_fan_create_attributes(struct acpi_device *device);
 void acpi_fan_delete_attributes(struct acpi_device *device);
 #endif
index 7b109022108b40090260648332811187efbab444..f15157d40713efd98f39eb9ba550b8ed77c7d843 100644 (file)
@@ -49,10 +49,50 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha
        return count;
 }
 
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
+       struct acpi_fan_fst fst;
+       int status;
+
+       status = acpi_fan_get_fst(acpi_dev, &fst);
+       if (status)
+               return status;
+
+       return sprintf(buf, "%lld\n", fst.speed);
+}
+
+static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev);
+       struct acpi_fan *fan = acpi_driver_data(acpi_dev);
+
+       return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl);
+}
+
 int acpi_fan_create_attributes(struct acpi_device *device)
 {
        struct acpi_fan *fan = acpi_driver_data(device);
-       int i, status = 0;
+       int i, status;
+
+       sysfs_attr_init(&fan->fine_grain_control.attr);
+       fan->fine_grain_control.show = show_fine_grain_control;
+       fan->fine_grain_control.store = NULL;
+       fan->fine_grain_control.attr.name = "fine_grain_control";
+       fan->fine_grain_control.attr.mode = 0444;
+       status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr);
+       if (status)
+               return status;
+
+       /* _FST is present if we are here */
+       sysfs_attr_init(&fan->fst_speed.attr);
+       fan->fst_speed.show = show_fan_speed;
+       fan->fst_speed.store = NULL;
+       fan->fst_speed.attr.name = "fan_speed_rpm";
+       fan->fst_speed.attr.mode = 0444;
+       status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr);
+       if (status)
+               goto rem_fine_grain_attr;
 
        for (i = 0; i < fan->fps_count; ++i) {
                struct acpi_fan_fps *fps = &fan->fps[i];
@@ -69,10 +109,18 @@ int acpi_fan_create_attributes(struct acpi_device *device)
 
                        for (j = 0; j < i; ++j)
                                sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
-                       break;
+                       goto rem_fst_attr;
                }
        }
 
+       return 0;
+
+rem_fst_attr:
+       sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
+
+rem_fine_grain_attr:
+       sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
+
        return status;
 }
 
@@ -83,4 +131,7 @@ void acpi_fan_delete_attributes(struct acpi_device *device)
 
        for (i = 0; i < fan->fps_count; ++i)
                sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr);
+
+       sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
+       sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
 }
index 01616ec2e9ac017b7b6e724e5011892f7cdbff49..b9a9a59ddcc1081efa1c40a68f17eadd3288c6dc 100644 (file)
@@ -75,7 +75,7 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
        return 0;
 }
 
-static int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
+int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
 {
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
        union acpi_object *obj;