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