Skip to content

Commit 536522f

Browse files
antheasij-intel
authored andcommitted
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 <W_Armin@gmx.de> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev> Link: https://patch.msgid.link/20251119174505.597218-3-lkml@antheas.dev Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
1 parent 70a4a81 commit 536522f

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

drivers/platform/x86/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ config ASUS_TF103C_DOCK
314314
config AYANEO_EC
315315
tristate "Ayaneo EC platform control"
316316
depends on DMI
317+
depends on ACPI_EC
318+
depends on HWMON
317319
help
318320
Enables support for the platform EC of Ayaneo devices. This
319321
includes fan control, fan speed, charge limit, magic

drivers/platform/x86/ayaneo-ec.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,24 @@
77
* Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
88
*/
99

10+
#include <linux/acpi.h>
1011
#include <linux/dmi.h>
1112
#include <linux/err.h>
13+
#include <linux/hwmon.h>
1214
#include <linux/init.h>
1315
#include <linux/kernel.h>
1416
#include <linux/module.h>
1517
#include <linux/platform_device.h>
1618

19+
#define AYANEO_PWM_ENABLE_REG 0x4A
20+
#define AYANEO_PWM_REG 0x4B
21+
#define AYANEO_PWM_MODE_AUTO 0x00
22+
#define AYANEO_PWM_MODE_MANUAL 0x01
23+
24+
#define AYANEO_FAN_REG 0x76
25+
1726
struct ayaneo_ec_quirk {
27+
bool has_fan_control;
1828
};
1929

2030
struct ayaneo_ec_platform_data {
@@ -23,6 +33,7 @@ struct ayaneo_ec_platform_data {
2333
};
2434

2535
static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
36+
.has_fan_control = true,
2637
};
2738

2839
static const struct dmi_system_id dmi_table[] = {
@@ -36,10 +47,128 @@ static const struct dmi_system_id dmi_table[] = {
3647
{},
3748
};
3849

50+
/* Callbacks for hwmon interface */
51+
static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata,
52+
enum hwmon_sensor_types type, u32 attr,
53+
int channel)
54+
{
55+
switch (type) {
56+
case hwmon_fan:
57+
return 0444;
58+
case hwmon_pwm:
59+
return 0644;
60+
default:
61+
return 0;
62+
}
63+
}
64+
65+
static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
66+
u32 attr, int channel, long *val)
67+
{
68+
u8 tmp;
69+
int ret;
70+
71+
switch (type) {
72+
case hwmon_fan:
73+
switch (attr) {
74+
case hwmon_fan_input:
75+
ret = ec_read(AYANEO_FAN_REG, &tmp);
76+
if (ret)
77+
return ret;
78+
*val = tmp << 8;
79+
ret = ec_read(AYANEO_FAN_REG + 1, &tmp);
80+
if (ret)
81+
return ret;
82+
*val |= tmp;
83+
return 0;
84+
default:
85+
break;
86+
}
87+
break;
88+
case hwmon_pwm:
89+
switch (attr) {
90+
case hwmon_pwm_input:
91+
ret = ec_read(AYANEO_PWM_REG, &tmp);
92+
if (ret)
93+
return ret;
94+
if (tmp > 100)
95+
return -EIO;
96+
*val = (255 * tmp) / 100;
97+
return 0;
98+
case hwmon_pwm_enable:
99+
ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
100+
if (ret)
101+
return ret;
102+
if (tmp == AYANEO_PWM_MODE_MANUAL)
103+
*val = 1;
104+
else if (tmp == AYANEO_PWM_MODE_AUTO)
105+
*val = 2;
106+
else
107+
return -EIO;
108+
return 0;
109+
default:
110+
break;
111+
}
112+
break;
113+
default:
114+
break;
115+
}
116+
return -EOPNOTSUPP;
117+
}
118+
119+
static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
120+
u32 attr, int channel, long val)
121+
{
122+
switch (type) {
123+
case hwmon_pwm:
124+
switch (attr) {
125+
case hwmon_pwm_enable:
126+
switch (val) {
127+
case 1:
128+
return ec_write(AYANEO_PWM_ENABLE_REG,
129+
AYANEO_PWM_MODE_MANUAL);
130+
case 2:
131+
return ec_write(AYANEO_PWM_ENABLE_REG,
132+
AYANEO_PWM_MODE_AUTO);
133+
default:
134+
return -EINVAL;
135+
}
136+
case hwmon_pwm_input:
137+
if (val < 0 || val > 255)
138+
return -EINVAL;
139+
return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
140+
default:
141+
break;
142+
}
143+
break;
144+
default:
145+
break;
146+
}
147+
return -EOPNOTSUPP;
148+
}
149+
150+
static const struct hwmon_ops ayaneo_ec_hwmon_ops = {
151+
.is_visible = ayaneo_ec_hwmon_is_visible,
152+
.read = ayaneo_ec_read,
153+
.write = ayaneo_ec_write,
154+
};
155+
156+
static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = {
157+
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
158+
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
159+
NULL,
160+
};
161+
162+
static const struct hwmon_chip_info ayaneo_ec_chip_info = {
163+
.ops = &ayaneo_ec_hwmon_ops,
164+
.info = ayaneo_ec_sensors,
165+
};
166+
39167
static int ayaneo_ec_probe(struct platform_device *pdev)
40168
{
41169
const struct dmi_system_id *dmi_entry;
42170
struct ayaneo_ec_platform_data *data;
171+
struct device *hwdev;
43172

44173
dmi_entry = dmi_first_match(dmi_table);
45174
if (!dmi_entry)
@@ -53,6 +182,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
53182
data->quirks = dmi_entry->driver_data;
54183
platform_set_drvdata(pdev, data);
55184

185+
if (data->quirks->has_fan_control) {
186+
hwdev = devm_hwmon_device_register_with_info(&pdev->dev,
187+
"ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
188+
if (IS_ERR(hwdev))
189+
return PTR_ERR(hwdev);
190+
}
191+
56192
return 0;
57193
}
58194

0 commit comments

Comments
 (0)