mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
f1197343f0
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>
138 lines
4.1 KiB
C
138 lines
4.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* fan_attr.c - Create extra attributes for ACPI Fan driver
|
|
*
|
|
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
* Copyright (C) 2022 Intel Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/acpi.h>
|
|
|
|
#include "fan.h"
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr);
|
|
int count;
|
|
|
|
if (fps->control == 0xFFFFFFFF || fps->control > 100)
|
|
count = scnprintf(buf, PAGE_SIZE, "not-defined:");
|
|
else
|
|
count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control);
|
|
|
|
if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9)
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
|
|
else
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point);
|
|
|
|
if (fps->speed == 0xFFFFFFFF)
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
|
|
else
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed);
|
|
|
|
if (fps->noise_level == 0xFFFFFFFF)
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:");
|
|
else
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100);
|
|
|
|
if (fps->power == 0xFFFFFFFF)
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n");
|
|
else
|
|
count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power);
|
|
|
|
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;
|
|
|
|
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];
|
|
|
|
snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i);
|
|
sysfs_attr_init(&fps->dev_attr.attr);
|
|
fps->dev_attr.show = show_state;
|
|
fps->dev_attr.store = NULL;
|
|
fps->dev_attr.attr.name = fps->name;
|
|
fps->dev_attr.attr.mode = 0444;
|
|
status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr);
|
|
if (status) {
|
|
int j;
|
|
|
|
for (j = 0; j < i; ++j)
|
|
sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr);
|
|
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;
|
|
}
|
|
|
|
void acpi_fan_delete_attributes(struct acpi_device *device)
|
|
{
|
|
struct acpi_fan *fan = acpi_driver_data(device);
|
|
int i;
|
|
|
|
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);
|
|
}
|