platform/x86: acer-wmi: add fan speed monitoring for Predator PHN16-71

Support CPU and GPU fan speed monitoring through WMI for Predator
PHN16-71.

Signed-off-by: SungHwan Jung <onenowy@gmail.com>
Link: https://lore.kernel.org/r/20231124094122.100707-3-onenowy@gmail.com
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
SungHwan Jung 2023-11-24 18:41:19 +09:00 committed by Hans de Goede
parent c0ff2c397e
commit 446dd8efa9
2 changed files with 108 additions and 1 deletions

View file

@ -178,6 +178,7 @@ config ACER_WMI
depends on RFKILL || RFKILL = n
depends on ACPI_WMI
depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on HWMON
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS

View file

@ -30,6 +30,8 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <acpi/video.h>
#include <linux/hwmon.h>
#include <linux/bitfield.h>
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
@ -69,6 +71,8 @@ MODULE_LICENSE("GPL");
#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54
#define ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK GENMASK(20, 8)
/*
* Acer ACPI method GUIDs
*/
@ -96,6 +100,8 @@ enum acer_wmi_event_ids {
enum acer_wmi_predator_v4_sys_info_command {
ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02,
ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED = 0x0201,
ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED = 0x0601,
};
static const struct key_entry acer_wmi_keymap[] __initconst = {
@ -241,6 +247,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_TURBO_LED BIT(8)
#define ACER_CAP_TURBO_FAN BIT(9)
#define ACER_CAP_PLATFORM_PROFILE BIT(10)
#define ACER_CAP_FAN_SPEED_READ BIT(11)
/*
* Interface type flags
@ -352,7 +359,8 @@ static void __init set_quirks(void)
| ACER_CAP_TURBO_FAN;
if (quirks->predator_v4)
interface->capability |= ACER_CAP_PLATFORM_PROFILE;
interface->capability |= ACER_CAP_PLATFORM_PROFILE |
ACER_CAP_FAN_SPEED_READ;
}
static int __init dmi_matched(const struct dmi_system_id *dmi)
@ -1718,6 +1726,26 @@ static int acer_gsensor_event(void)
return 0;
}
static int acer_get_fan_speed(int fan)
{
if (quirks->predator_v4) {
acpi_status status;
u64 fanspeed;
status = WMI_gaming_execute_u64(
ACER_WMID_GET_GAMING_SYS_INFO_METHODID,
fan == 0 ? ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED :
ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED,
&fanspeed);
if (ACPI_FAILURE(status))
return -EIO;
return FIELD_GET(ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK, fanspeed);
}
return -EOPNOTSUPP;
}
/*
* Predator series turbo button
*/
@ -2472,6 +2500,8 @@ static u32 get_wmid_devices(void)
return devices;
}
static int acer_wmi_hwmon_init(void);
/*
* Platform device
*/
@ -2501,8 +2531,17 @@ static int acer_platform_probe(struct platform_device *device)
goto error_platform_profile;
}
if (has_cap(ACER_CAP_FAN_SPEED_READ)) {
err = acer_wmi_hwmon_init();
if (err)
goto error_hwmon;
}
return 0;
error_hwmon:
if (platform_profile_support)
platform_profile_remove();
error_platform_profile:
acer_rfkill_exit();
error_rfkill:
@ -2612,6 +2651,73 @@ static void __init create_debugfs(void)
&interface->debug.wmid_devices);
}
static umode_t acer_wmi_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
switch (type) {
case hwmon_fan:
if (acer_get_fan_speed(channel) >= 0)
return 0444;
break;
default:
return 0;
}
return 0;
}
static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
int ret;
switch (type) {
case hwmon_fan:
ret = acer_get_fan_speed(channel);
if (ret < 0)
return ret;
*val = ret;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = {
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), NULL
};
static const struct hwmon_ops acer_wmi_hwmon_ops = {
.read = acer_wmi_hwmon_read,
.is_visible = acer_wmi_hwmon_is_visible,
};
static const struct hwmon_chip_info acer_wmi_hwmon_chip_info = {
.ops = &acer_wmi_hwmon_ops,
.info = acer_wmi_hwmon_info,
};
static int acer_wmi_hwmon_init(void)
{
struct device *dev = &acer_platform_device->dev;
struct device *hwmon;
hwmon = devm_hwmon_device_register_with_info(dev, "acer",
&acer_platform_driver,
&acer_wmi_hwmon_chip_info,
NULL);
if (IS_ERR(hwmon)) {
dev_err(dev, "Could not register acer hwmon device\n");
return PTR_ERR(hwmon);
}
return 0;
}
static int __init acer_wmi_init(void)
{
int err;