From: Antheas Kapenekakis Date: Wed, 19 Nov 2025 17:45:01 +0000 (+0100) Subject: platform/x86: ayaneo-ec: Add hwmon support X-Git-Tag: v6.19-rc1~42^2~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=536522f0355cffe8478502ffbb041769e2f61bfe;p=thirdparty%2Fkernel%2Flinux.git platform/x86: ayaneo-ec: Add hwmon support Add hwmon single fan sensor reads and control for Ayaneo devices. The register and method of access is the same for all devices. Reviewed-by: Armin-Wolf Signed-off-by: Antheas Kapenekakis Link: https://patch.msgid.link/20251119174505.597218-3-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index fa3d393c2c1be..11850e1060204 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -314,6 +314,8 @@ config ASUS_TF103C_DOCK config AYANEO_EC tristate "Ayaneo EC platform control" depends on DMI + depends on ACPI_EC + depends on HWMON help Enables support for the platform EC of Ayaneo devices. This includes fan control, fan speed, charge limit, magic diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c index 2fe66c8a89f49..86d4eed49f420 100644 --- a/drivers/platform/x86/ayaneo-ec.c +++ b/drivers/platform/x86/ayaneo-ec.c @@ -7,14 +7,24 @@ * Copyright (C) 2025 Antheas Kapenekakis */ +#include #include #include +#include #include #include #include #include +#define AYANEO_PWM_ENABLE_REG 0x4A +#define AYANEO_PWM_REG 0x4B +#define AYANEO_PWM_MODE_AUTO 0x00 +#define AYANEO_PWM_MODE_MANUAL 0x01 + +#define AYANEO_FAN_REG 0x76 + struct ayaneo_ec_quirk { + bool has_fan_control; }; struct ayaneo_ec_platform_data { @@ -23,6 +33,7 @@ struct ayaneo_ec_platform_data { }; static const struct ayaneo_ec_quirk quirk_ayaneo3 = { + .has_fan_control = true, }; static const struct dmi_system_id dmi_table[] = { @@ -36,10 +47,128 @@ static const struct dmi_system_id dmi_table[] = { {}, }; +/* Callbacks for hwmon interface */ +static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) { + case hwmon_fan: + return 0444; + case hwmon_pwm: + return 0644; + default: + return 0; + } +} + +static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + u8 tmp; + int ret; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = ec_read(AYANEO_FAN_REG, &tmp); + if (ret) + return ret; + *val = tmp << 8; + ret = ec_read(AYANEO_FAN_REG + 1, &tmp); + if (ret) + return ret; + *val |= tmp; + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = ec_read(AYANEO_PWM_REG, &tmp); + if (ret) + return ret; + if (tmp > 100) + return -EIO; + *val = (255 * tmp) / 100; + return 0; + case hwmon_pwm_enable: + ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp); + if (ret) + return ret; + if (tmp == AYANEO_PWM_MODE_MANUAL) + *val = 1; + else if (tmp == AYANEO_PWM_MODE_AUTO) + *val = 2; + else + return -EIO; + return 0; + default: + break; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + switch (val) { + case 1: + return ec_write(AYANEO_PWM_ENABLE_REG, + AYANEO_PWM_MODE_MANUAL); + case 2: + return ec_write(AYANEO_PWM_ENABLE_REG, + AYANEO_PWM_MODE_AUTO); + default: + return -EINVAL; + } + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + return ec_write(AYANEO_PWM_REG, (val * 100) / 255); + default: + break; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static const struct hwmon_ops ayaneo_ec_hwmon_ops = { + .is_visible = ayaneo_ec_hwmon_is_visible, + .read = ayaneo_ec_read, + .write = ayaneo_ec_write, +}; + +static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL, +}; + +static const struct hwmon_chip_info ayaneo_ec_chip_info = { + .ops = &ayaneo_ec_hwmon_ops, + .info = ayaneo_ec_sensors, +}; + static int ayaneo_ec_probe(struct platform_device *pdev) { const struct dmi_system_id *dmi_entry; struct ayaneo_ec_platform_data *data; + struct device *hwdev; dmi_entry = dmi_first_match(dmi_table); if (!dmi_entry) @@ -53,6 +182,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev) data->quirks = dmi_entry->driver_data; platform_set_drvdata(pdev, data); + if (data->quirks->has_fan_control) { + hwdev = devm_hwmon_device_register_with_info(&pdev->dev, + "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL); + if (IS_ERR(hwdev)) + return PTR_ERR(hwdev); + } + return 0; }