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+
1726struct ayaneo_ec_quirk {
27+ bool has_fan_control ;
1828};
1929
2030struct ayaneo_ec_platform_data {
@@ -23,6 +33,7 @@ struct ayaneo_ec_platform_data {
2333};
2434
2535static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
36+ .has_fan_control = true,
2637};
2738
2839static 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+
39167static 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