Commit 446dd8ef authored by SungHwan Jung's avatar SungHwan Jung Committed by Hans de Goede
Browse files

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: default avatarSungHwan Jung <onenowy@gmail.com>
Link: https://lore.kernel.org/r/20231124094122.100707-3-onenowy@gmail.com


Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent c0ff2c39
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+107 −1
Original line number Diff line number Diff line
@@ -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;