mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-04-03 12:05:13 +02:00
[ Upstream commit6c00f29f74] Add support for ACPI fans with _FST to report their speed even if they do not support fan control. As suggested by Armin Wolf [1] and per the Windows Thermal Management Design Guide [2], Samsung Galaxy Book series devices (and possibly many more devices where the Windows guide was strictly followed) only implement the _FST method and do not support ACPI-based fan control. Currently, these fans are not supported by the kernel driver but this patch will make some very small adjustments to allow them to be supported. This patch is tested and working for me on a Samsung Galaxy Book2 Pro whose DSDT (and several other Samsung Galaxy Book series notebooks which currently have the same issue) can be found at [3]. Link: https://lore.kernel.org/platform-driver-x86/53c5075b-1967-45d0-937f-463912dd966d@gmx.de [1] Link: https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide [2] Link:8e3087a06b/dsdt[3] Signed-off-by: Joshua Grisham <josh@joshuagrisham.com> Reviewed-by: Armin Wolf <W_Armin@gmx.de> Link: https://patch.msgid.link/20250222094407.9753-1-josh@joshuagrisham.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Stable-dep-of:d91a1d129b("ACPI: fan: Use platform device for devres-related actions") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
145 lines
4.0 KiB
C
145 lines
4.0 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 += sysfs_emit_at(buf, count, "not-defined:");
|
|
else
|
|
count += sysfs_emit_at(buf, count, "%lld:", fps->trip_point);
|
|
|
|
if (fps->speed == 0xFFFFFFFF)
|
|
count += sysfs_emit_at(buf, count, "not-defined:");
|
|
else
|
|
count += sysfs_emit_at(buf, count, "%lld:", fps->speed);
|
|
|
|
if (fps->noise_level == 0xFFFFFFFF)
|
|
count += sysfs_emit_at(buf, count, "not-defined:");
|
|
else
|
|
count += sysfs_emit_at(buf, count, "%lld:", fps->noise_level * 100);
|
|
|
|
if (fps->power == 0xFFFFFFFF)
|
|
count += sysfs_emit_at(buf, count, "not-defined\n");
|
|
else
|
|
count += sysfs_emit_at(buf, 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->handle, &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;
|
|
|
|
/* _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)
|
|
return status;
|
|
|
|
if (!fan->acpi4)
|
|
return 0;
|
|
|
|
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)
|
|
goto rem_fst_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_fine_grain_attr;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
rem_fine_grain_attr:
|
|
sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr);
|
|
|
|
rem_fst_attr:
|
|
sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
|
|
|
|
return status;
|
|
}
|
|
|
|
void acpi_fan_delete_attributes(struct acpi_device *device)
|
|
{
|
|
struct acpi_fan *fan = acpi_driver_data(device);
|
|
int i;
|
|
|
|
sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr);
|
|
|
|
if (!fan->acpi4)
|
|
return;
|
|
|
|
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->fine_grain_control.attr);
|
|
}
|