Merge tag 'for-netdev' of https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
[sfrench/cifs-2.6.git] / drivers / hwmon / oxp-sensors.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose
4  * fan reading and control via hwmon sysfs.
5  *
6  * Old OXP boards have the same DMI strings and they are told apart by
7  * the boot cpu vendor (Intel/AMD). Currently only AMD boards are
8  * supported but the code is made to be simple to add other handheld
9  * boards in the future.
10  * Fan control is provided via pwm interface in the range [0-255].
11  * Old AMD boards use [0-100] as range in the EC, the written value is
12  * scaled to accommodate for that. Newer boards like the mini PRO and
13  * AOK ZOE are not scaled but have the same EC layout.
14  *
15  * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
16  */
17
18 #include <linux/acpi.h>
19 #include <linux/dmi.h>
20 #include <linux/hwmon.h>
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/processor.h>
26
27 /* Handle ACPI lock mechanism */
28 static u32 oxp_mutex;
29
30 #define ACPI_LOCK_DELAY_MS      500
31
32 static bool lock_global_acpi_lock(void)
33 {
34         return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
35 }
36
37 static bool unlock_global_acpi_lock(void)
38 {
39         return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
40 }
41
42 enum oxp_board {
43         aok_zoe_a1 = 1,
44         aya_neo_2,
45         aya_neo_air,
46         aya_neo_air_plus_mendo,
47         aya_neo_air_pro,
48         aya_neo_geek,
49         oxp_mini_amd,
50         oxp_mini_amd_a07,
51         oxp_mini_amd_pro,
52 };
53
54 static enum oxp_board board;
55
56 /* Fan reading and PWM */
57 #define OXP_SENSOR_FAN_REG              0x76 /* Fan reading is 2 registers long */
58 #define OXP_SENSOR_PWM_ENABLE_REG       0x4A /* PWM enable is 1 register long */
59 #define OXP_SENSOR_PWM_REG              0x4B /* PWM reading is 1 register long */
60
61 /* Turbo button takeover function
62  * Older boards have different values and EC registers
63  * for the same function
64  */
65 #define OXP_OLD_TURBO_SWITCH_REG        0x1E
66 #define OXP_OLD_TURBO_TAKE_VAL          0x01
67 #define OXP_OLD_TURBO_RETURN_VAL        0x00
68
69 #define OXP_TURBO_SWITCH_REG            0xF1
70 #define OXP_TURBO_TAKE_VAL              0x40
71 #define OXP_TURBO_RETURN_VAL            0x00
72
73 static const struct dmi_system_id dmi_table[] = {
74         {
75                 .matches = {
76                         DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
77                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
78                 },
79                 .driver_data = (void *)aok_zoe_a1,
80         },
81         {
82                 .matches = {
83                         DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
84                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
85                 },
86                 .driver_data = (void *)aok_zoe_a1,
87         },
88         {
89                 .matches = {
90                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
91                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
92                 },
93                 .driver_data = (void *)aya_neo_2,
94         },
95         {
96                 .matches = {
97                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
98                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
99                 },
100                 .driver_data = (void *)aya_neo_air,
101         },
102         {
103                 .matches = {
104                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
105                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
106                 },
107                 .driver_data = (void *)aya_neo_air_plus_mendo,
108         },
109         {
110                 .matches = {
111                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
112                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
113                 },
114                 .driver_data = (void *)aya_neo_air_pro,
115         },
116         {
117                 .matches = {
118                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
119                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"),
120                 },
121                 .driver_data = (void *)aya_neo_geek,
122         },
123         {
124                 .matches = {
125                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
126                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
127                 },
128                 .driver_data = (void *)oxp_mini_amd,
129         },
130         {
131                 .matches = {
132                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
133                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
134                 },
135                 .driver_data = (void *)oxp_mini_amd_a07,
136         },
137         {
138                 .matches = {
139                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
140                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
141                 },
142                 .driver_data = (void *)oxp_mini_amd_pro,
143         },
144         {},
145 };
146
147 /* Helper functions to handle EC read/write */
148 static int read_from_ec(u8 reg, int size, long *val)
149 {
150         int i;
151         int ret;
152         u8 buffer;
153
154         if (!lock_global_acpi_lock())
155                 return -EBUSY;
156
157         *val = 0;
158         for (i = 0; i < size; i++) {
159                 ret = ec_read(reg + i, &buffer);
160                 if (ret)
161                         return ret;
162                 *val <<= i * 8;
163                 *val += buffer;
164         }
165
166         if (!unlock_global_acpi_lock())
167                 return -EBUSY;
168
169         return 0;
170 }
171
172 static int write_to_ec(u8 reg, u8 value)
173 {
174         int ret;
175
176         if (!lock_global_acpi_lock())
177                 return -EBUSY;
178
179         ret = ec_write(reg, value);
180
181         if (!unlock_global_acpi_lock())
182                 return -EBUSY;
183
184         return ret;
185 }
186
187 /* Turbo button toggle functions */
188 static int tt_toggle_enable(void)
189 {
190         u8 reg;
191         u8 val;
192
193         switch (board) {
194         case oxp_mini_amd_a07:
195                 reg = OXP_OLD_TURBO_SWITCH_REG;
196                 val = OXP_OLD_TURBO_TAKE_VAL;
197                 break;
198         case oxp_mini_amd_pro:
199         case aok_zoe_a1:
200                 reg = OXP_TURBO_SWITCH_REG;
201                 val = OXP_TURBO_TAKE_VAL;
202                 break;
203         default:
204                 return -EINVAL;
205         }
206         return write_to_ec(reg, val);
207 }
208
209 static int tt_toggle_disable(void)
210 {
211         u8 reg;
212         u8 val;
213
214         switch (board) {
215         case oxp_mini_amd_a07:
216                 reg = OXP_OLD_TURBO_SWITCH_REG;
217                 val = OXP_OLD_TURBO_RETURN_VAL;
218                 break;
219         case oxp_mini_amd_pro:
220         case aok_zoe_a1:
221                 reg = OXP_TURBO_SWITCH_REG;
222                 val = OXP_TURBO_RETURN_VAL;
223                 break;
224         default:
225                 return -EINVAL;
226         }
227         return write_to_ec(reg, val);
228 }
229
230 /* Callbacks for turbo toggle attribute */
231 static umode_t tt_toggle_is_visible(struct kobject *kobj,
232                                     struct attribute *attr, int n)
233 {
234         switch (board) {
235         case aok_zoe_a1:
236         case oxp_mini_amd_a07:
237         case oxp_mini_amd_pro:
238                 return attr->mode;
239         default:
240                 break;
241         }
242         return 0;
243 }
244
245 static ssize_t tt_toggle_store(struct device *dev,
246                                struct device_attribute *attr, const char *buf,
247                                size_t count)
248 {
249         int rval;
250         bool value;
251
252         rval = kstrtobool(buf, &value);
253         if (rval)
254                 return rval;
255
256         if (value) {
257                 rval = tt_toggle_enable();
258         } else {
259                 rval = tt_toggle_disable();
260         }
261         if (rval)
262                 return rval;
263
264         return count;
265 }
266
267 static ssize_t tt_toggle_show(struct device *dev,
268                               struct device_attribute *attr, char *buf)
269 {
270         int retval;
271         u8 reg;
272         long val;
273
274         switch (board) {
275         case oxp_mini_amd_a07:
276                 reg = OXP_OLD_TURBO_SWITCH_REG;
277                 break;
278         case oxp_mini_amd_pro:
279         case aok_zoe_a1:
280                 reg = OXP_TURBO_SWITCH_REG;
281                 break;
282         default:
283                 return -EINVAL;
284         }
285
286         retval = read_from_ec(reg, 1, &val);
287         if (retval)
288                 return retval;
289
290         return sysfs_emit(buf, "%d\n", !!val);
291 }
292
293 static DEVICE_ATTR_RW(tt_toggle);
294
295 /* PWM enable/disable functions */
296 static int oxp_pwm_enable(void)
297 {
298         return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01);
299 }
300
301 static int oxp_pwm_disable(void)
302 {
303         return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00);
304 }
305
306 /* Callbacks for hwmon interface */
307 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
308                                        enum hwmon_sensor_types type, u32 attr, int channel)
309 {
310         switch (type) {
311         case hwmon_fan:
312                 return 0444;
313         case hwmon_pwm:
314                 return 0644;
315         default:
316                 return 0;
317         }
318 }
319
320 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
321                              u32 attr, int channel, long *val)
322 {
323         int ret;
324
325         switch (type) {
326         case hwmon_fan:
327                 switch (attr) {
328                 case hwmon_fan_input:
329                         return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
330                 default:
331                         break;
332                 }
333                 break;
334         case hwmon_pwm:
335                 switch (attr) {
336                 case hwmon_pwm_input:
337                         ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
338                         if (ret)
339                                 return ret;
340                         switch (board) {
341                         case aya_neo_2:
342                         case aya_neo_air:
343                         case aya_neo_air_plus_mendo:
344                         case aya_neo_air_pro:
345                         case aya_neo_geek:
346                         case oxp_mini_amd:
347                         case oxp_mini_amd_a07:
348                                 *val = (*val * 255) / 100;
349                                 break;
350                         case oxp_mini_amd_pro:
351                         case aok_zoe_a1:
352                         default:
353                                 break;
354                         }
355                         return 0;
356                 case hwmon_pwm_enable:
357                         return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
358                 default:
359                         break;
360                 }
361                 break;
362         default:
363                 break;
364         }
365         return -EOPNOTSUPP;
366 }
367
368 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
369                               u32 attr, int channel, long val)
370 {
371         switch (type) {
372         case hwmon_pwm:
373                 switch (attr) {
374                 case hwmon_pwm_enable:
375                         if (val == 1)
376                                 return oxp_pwm_enable();
377                         else if (val == 0)
378                                 return oxp_pwm_disable();
379                         return -EINVAL;
380                 case hwmon_pwm_input:
381                         if (val < 0 || val > 255)
382                                 return -EINVAL;
383                         switch (board) {
384                         case aya_neo_2:
385                         case aya_neo_air:
386                         case aya_neo_air_plus_mendo:
387                         case aya_neo_air_pro:
388                         case aya_neo_geek:
389                         case oxp_mini_amd:
390                         case oxp_mini_amd_a07:
391                                 val = (val * 100) / 255;
392                                 break;
393                         case aok_zoe_a1:
394                         case oxp_mini_amd_pro:
395                         default:
396                                 break;
397                         }
398                         return write_to_ec(OXP_SENSOR_PWM_REG, val);
399                 default:
400                         break;
401                 }
402                 break;
403         default:
404                 break;
405         }
406         return -EOPNOTSUPP;
407 }
408
409 /* Known sensors in the OXP EC controllers */
410 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
411         HWMON_CHANNEL_INFO(fan,
412                            HWMON_F_INPUT),
413         HWMON_CHANNEL_INFO(pwm,
414                            HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
415         NULL,
416 };
417
418 static struct attribute *oxp_ec_attrs[] = {
419         &dev_attr_tt_toggle.attr,
420         NULL
421 };
422
423 static struct attribute_group oxp_ec_attribute_group = {
424         .is_visible = tt_toggle_is_visible,
425         .attrs = oxp_ec_attrs,
426 };
427
428 static const struct attribute_group *oxp_ec_groups[] = {
429         &oxp_ec_attribute_group,
430         NULL
431 };
432
433 static const struct hwmon_ops oxp_ec_hwmon_ops = {
434         .is_visible = oxp_ec_hwmon_is_visible,
435         .read = oxp_platform_read,
436         .write = oxp_platform_write,
437 };
438
439 static const struct hwmon_chip_info oxp_ec_chip_info = {
440         .ops = &oxp_ec_hwmon_ops,
441         .info = oxp_platform_sensors,
442 };
443
444 /* Initialization logic */
445 static int oxp_platform_probe(struct platform_device *pdev)
446 {
447         struct device *dev = &pdev->dev;
448         struct device *hwdev;
449
450         hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
451                                                      &oxp_ec_chip_info, NULL);
452
453         return PTR_ERR_OR_ZERO(hwdev);
454 }
455
456 static struct platform_driver oxp_platform_driver = {
457         .driver = {
458                 .name = "oxp-platform",
459                 .dev_groups = oxp_ec_groups,
460         },
461         .probe = oxp_platform_probe,
462 };
463
464 static struct platform_device *oxp_platform_device;
465
466 static int __init oxp_platform_init(void)
467 {
468         const struct dmi_system_id *dmi_entry;
469
470         /*
471          * Have to check for AMD processor here because DMI strings are the
472          * same between Intel and AMD boards, the only way to tell them apart
473          * is the CPU.
474          * Intel boards seem to have different EC registers and values to
475          * read/write.
476          */
477         dmi_entry = dmi_first_match(dmi_table);
478         if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
479                 return -ENODEV;
480
481         board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
482
483         oxp_platform_device =
484                 platform_create_bundle(&oxp_platform_driver,
485                                        oxp_platform_probe, NULL, 0, NULL, 0);
486
487         return PTR_ERR_OR_ZERO(oxp_platform_device);
488 }
489
490 static void __exit oxp_platform_exit(void)
491 {
492         platform_device_unregister(oxp_platform_device);
493         platform_driver_unregister(&oxp_platform_driver);
494 }
495
496 MODULE_DEVICE_TABLE(dmi, dmi_table);
497
498 module_init(oxp_platform_init);
499 module_exit(oxp_platform_exit);
500
501 MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
502 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
503 MODULE_LICENSE("GPL");