]> Git Repo - linux.git/blob - drivers/hwmon/oxp-sensors.c
Merge tag 'i2c-for-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux.git] / drivers / hwmon / oxp-sensors.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds
4  * that expose 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). Of these older models only AMD is
8  * supported.
9  *
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  * AOKZOE are not scaled but have the same EC layout. Newer models
14  * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi
15  * are [1-244] and scaled to 0-255.
16  *
17  * Copyright (C) 2022 Joaquín I. Aramendía <[email protected]>
18  * Copyright (C) 2024 Derek J. Clark <[email protected]>
19  */
20
21 #include <linux/acpi.h>
22 #include <linux/dmi.h>
23 #include <linux/hwmon.h>
24 #include <linux/init.h>
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/platform_device.h>
28 #include <linux/processor.h>
29
30 /* Handle ACPI lock mechanism */
31 static u32 oxp_mutex;
32
33 #define ACPI_LOCK_DELAY_MS      500
34
35 static bool lock_global_acpi_lock(void)
36 {
37         return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
38 }
39
40 static bool unlock_global_acpi_lock(void)
41 {
42         return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
43 }
44
45 enum oxp_board {
46         aok_zoe_a1 = 1,
47         aya_neo_2,
48         aya_neo_air,
49         aya_neo_air_1s,
50         aya_neo_air_plus_mendo,
51         aya_neo_air_pro,
52         aya_neo_flip,
53         aya_neo_geek,
54         aya_neo_kun,
55         orange_pi_neo,
56         oxp_2,
57         oxp_fly,
58         oxp_mini_amd,
59         oxp_mini_amd_a07,
60         oxp_mini_amd_pro,
61         oxp_x1,
62 };
63
64 static enum oxp_board board;
65
66 /* Fan reading and PWM */
67 #define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */
68 #define OXP_2_SENSOR_FAN_REG           0x58 /* Fan reading is 2 registers long */
69 #define OXP_SENSOR_PWM_ENABLE_REG      0x4A /* PWM enable is 1 register long */
70 #define OXP_SENSOR_PWM_REG             0x4B /* PWM reading is 1 register long */
71 #define PWM_MODE_AUTO                  0x00
72 #define PWM_MODE_MANUAL                0x01
73
74 /* OrangePi fan reading and PWM */
75 #define ORANGEPI_SENSOR_FAN_REG        0x78 /* Fan reading is 2 registers long */
76 #define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */
77 #define ORANGEPI_SENSOR_PWM_REG        0x38 /* PWM reading is 1 register long */
78
79 /* Turbo button takeover function
80  * Different boards have different values and EC registers
81  * for the same function
82  */
83 #define OXP_TURBO_SWITCH_REG           0xF1 /* Mini Pro, OneXFly, AOKZOE */
84 #define OXP_2_TURBO_SWITCH_REG         0xEB /* OXP2 and X1 */
85 #define OXP_MINI_TURBO_SWITCH_REG      0x1E /* Mini AO7 */
86
87 #define OXP_MINI_TURBO_TAKE_VAL        0x01 /* Mini AO7 */
88 #define OXP_TURBO_TAKE_VAL             0x40 /* All other models */
89
90 #define OXP_TURBO_RETURN_VAL           0x00 /* Common return val */
91
92 static const struct dmi_system_id dmi_table[] = {
93         {
94                 .matches = {
95                         DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
96                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
97                 },
98                 .driver_data = (void *)aok_zoe_a1,
99         },
100         {
101                 .matches = {
102                         DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
103                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
104                 },
105                 .driver_data = (void *)aok_zoe_a1,
106         },
107         {
108                 .matches = {
109                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
110                         DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
111                 },
112                 .driver_data = (void *)aya_neo_2,
113         },
114         {
115                 .matches = {
116                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
117                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
118                 },
119                 .driver_data = (void *)aya_neo_air,
120         },
121         {
122                 .matches = {
123                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
124                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
125                 },
126                 .driver_data = (void *)aya_neo_air_1s,
127         },
128         {
129                 .matches = {
130                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
131                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
132                 },
133                 .driver_data = (void *)aya_neo_air_plus_mendo,
134         },
135         {
136                 .matches = {
137                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
138                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
139                 },
140                 .driver_data = (void *)aya_neo_air_pro,
141         },
142         {
143                 .matches = {
144                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
145                         DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
146                 },
147                 .driver_data = (void *)aya_neo_flip,
148         },
149         {
150                 .matches = {
151                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
152                         DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
153                 },
154                 .driver_data = (void *)aya_neo_geek,
155         },
156         {
157                 .matches = {
158                         DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
159                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
160                 },
161                 .driver_data = (void *)aya_neo_kun,
162         },
163         {
164                 .matches = {
165                         DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
166                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),
167                 },
168                 .driver_data = (void *)orange_pi_neo,
169         },
170         {
171                 .matches = {
172                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
173                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
174                 },
175                 .driver_data = (void *)oxp_mini_amd,
176         },
177         {
178                 .matches = {
179                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
180                         DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),
181                 },
182                 .driver_data = (void *)oxp_2,
183         },
184         {
185                 .matches = {
186                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
187                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),
188                 },
189                 .driver_data = (void *)oxp_fly,
190         },
191         {
192                 .matches = {
193                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
194                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
195                 },
196                 .driver_data = (void *)oxp_mini_amd_a07,
197         },
198         {
199                 .matches = {
200                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
201                         DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
202                 },
203                 .driver_data = (void *)oxp_mini_amd_pro,
204         },
205         {
206                 .matches = {
207                         DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
208                         DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"),
209                 },
210                 .driver_data = (void *)oxp_x1,
211         },
212         {},
213 };
214
215 /* Helper functions to handle EC read/write */
216 static int read_from_ec(u8 reg, int size, long *val)
217 {
218         int i;
219         int ret;
220         u8 buffer;
221
222         if (!lock_global_acpi_lock())
223                 return -EBUSY;
224
225         *val = 0;
226         for (i = 0; i < size; i++) {
227                 ret = ec_read(reg + i, &buffer);
228                 if (ret)
229                         return ret;
230                 *val <<= i * 8;
231                 *val += buffer;
232         }
233
234         if (!unlock_global_acpi_lock())
235                 return -EBUSY;
236
237         return 0;
238 }
239
240 static int write_to_ec(u8 reg, u8 value)
241 {
242         int ret;
243
244         if (!lock_global_acpi_lock())
245                 return -EBUSY;
246
247         ret = ec_write(reg, value);
248
249         if (!unlock_global_acpi_lock())
250                 return -EBUSY;
251
252         return ret;
253 }
254
255 /* Turbo button toggle functions */
256 static int tt_toggle_enable(void)
257 {
258         u8 reg;
259         u8 val;
260
261         switch (board) {
262         case oxp_mini_amd_a07:
263                 reg = OXP_MINI_TURBO_SWITCH_REG;
264                 val = OXP_MINI_TURBO_TAKE_VAL;
265                 break;
266         case aok_zoe_a1:
267         case oxp_fly:
268         case oxp_mini_amd_pro:
269                 reg = OXP_TURBO_SWITCH_REG;
270                 val = OXP_TURBO_TAKE_VAL;
271                 break;
272         case oxp_2:
273         case oxp_x1:
274                 reg = OXP_2_TURBO_SWITCH_REG;
275                 val = OXP_TURBO_TAKE_VAL;
276                 break;
277         default:
278                 return -EINVAL;
279         }
280         return write_to_ec(reg, val);
281 }
282
283 static int tt_toggle_disable(void)
284 {
285         u8 reg;
286         u8 val;
287
288         switch (board) {
289         case oxp_mini_amd_a07:
290                 reg = OXP_MINI_TURBO_SWITCH_REG;
291                 val = OXP_TURBO_RETURN_VAL;
292                 break;
293         case aok_zoe_a1:
294         case oxp_fly:
295         case oxp_mini_amd_pro:
296                 reg = OXP_TURBO_SWITCH_REG;
297                 val = OXP_TURBO_RETURN_VAL;
298                 break;
299         case oxp_2:
300         case oxp_x1:
301                 reg = OXP_2_TURBO_SWITCH_REG;
302                 val = OXP_TURBO_RETURN_VAL;
303                 break;
304         default:
305                 return -EINVAL;
306         }
307         return write_to_ec(reg, val);
308 }
309
310 /* Callbacks for turbo toggle attribute */
311 static umode_t tt_toggle_is_visible(struct kobject *kobj,
312                                     struct attribute *attr, int n)
313 {
314         switch (board) {
315         case aok_zoe_a1:
316         case oxp_2:
317         case oxp_fly:
318         case oxp_mini_amd_a07:
319         case oxp_mini_amd_pro:
320         case oxp_x1:
321                 return attr->mode;
322         default:
323                 break;
324         }
325         return 0;
326 }
327
328 static ssize_t tt_toggle_store(struct device *dev,
329                                struct device_attribute *attr, const char *buf,
330                                size_t count)
331 {
332         int rval;
333         bool value;
334
335         rval = kstrtobool(buf, &value);
336         if (rval)
337                 return rval;
338
339         if (value) {
340                 rval = tt_toggle_enable();
341         } else {
342                 rval = tt_toggle_disable();
343         }
344         if (rval)
345                 return rval;
346
347         return count;
348 }
349
350 static ssize_t tt_toggle_show(struct device *dev,
351                               struct device_attribute *attr, char *buf)
352 {
353         int retval;
354         u8 reg;
355         long val;
356
357         switch (board) {
358         case oxp_mini_amd_a07:
359                 reg = OXP_MINI_TURBO_SWITCH_REG;
360                 break;
361         case aok_zoe_a1:
362         case oxp_fly:
363         case oxp_mini_amd_pro:
364                 reg = OXP_TURBO_SWITCH_REG;
365                 break;
366         case oxp_2:
367         case oxp_x1:
368                 reg = OXP_2_TURBO_SWITCH_REG;
369                 break;
370         default:
371                 return -EINVAL;
372         }
373
374         retval = read_from_ec(reg, 1, &val);
375         if (retval)
376                 return retval;
377
378         return sysfs_emit(buf, "%d\n", !!val);
379 }
380
381 static DEVICE_ATTR_RW(tt_toggle);
382
383 /* PWM enable/disable functions */
384 static int oxp_pwm_enable(void)
385 {
386         switch (board) {
387         case orange_pi_neo:
388                 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
389         case aok_zoe_a1:
390         case aya_neo_2:
391         case aya_neo_air:
392         case aya_neo_air_plus_mendo:
393         case aya_neo_air_pro:
394         case aya_neo_flip:
395         case aya_neo_geek:
396         case aya_neo_kun:
397         case oxp_2:
398         case oxp_fly:
399         case oxp_mini_amd:
400         case oxp_mini_amd_a07:
401         case oxp_mini_amd_pro:
402         case oxp_x1:
403                 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
404         default:
405                 return -EINVAL;
406         }
407 }
408
409 static int oxp_pwm_disable(void)
410 {
411         switch (board) {
412         case orange_pi_neo:
413                 return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
414         case aok_zoe_a1:
415         case aya_neo_2:
416         case aya_neo_air:
417         case aya_neo_air_1s:
418         case aya_neo_air_plus_mendo:
419         case aya_neo_air_pro:
420         case aya_neo_flip:
421         case aya_neo_geek:
422         case aya_neo_kun:
423         case oxp_2:
424         case oxp_fly:
425         case oxp_mini_amd:
426         case oxp_mini_amd_a07:
427         case oxp_mini_amd_pro:
428         case oxp_x1:
429                 return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
430         default:
431                 return -EINVAL;
432         }
433 }
434
435 /* Callbacks for hwmon interface */
436 static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
437                                        enum hwmon_sensor_types type, u32 attr, int channel)
438 {
439         switch (type) {
440         case hwmon_fan:
441                 return 0444;
442         case hwmon_pwm:
443                 return 0644;
444         default:
445                 return 0;
446         }
447 }
448
449 static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
450                              u32 attr, int channel, long *val)
451 {
452         int ret;
453
454         switch (type) {
455         case hwmon_fan:
456                 switch (attr) {
457                 case hwmon_fan_input:
458                         switch (board) {
459                         case orange_pi_neo:
460                                 return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
461                         case oxp_2:
462                         case oxp_x1:
463                                 return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
464                         case aok_zoe_a1:
465                         case aya_neo_2:
466                         case aya_neo_air:
467                         case aya_neo_air_1s:
468                         case aya_neo_air_plus_mendo:
469                         case aya_neo_air_pro:
470                         case aya_neo_flip:
471                         case aya_neo_geek:
472                         case aya_neo_kun:
473                         case oxp_fly:
474                         case oxp_mini_amd:
475                         case oxp_mini_amd_a07:
476                         case oxp_mini_amd_pro:
477                                 return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
478                         default:
479                                 break;
480                         }
481                         break;
482                 default:
483                         break;
484                 }
485                 break;
486         case hwmon_pwm:
487                 switch (attr) {
488                 case hwmon_pwm_input:
489                         switch (board) {
490                         case orange_pi_neo:
491                                 ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
492                                 if (ret)
493                                         return ret;
494                                 /* scale from range [1-244] */
495                                 *val = ((*val - 1) * 254 / 243) + 1;
496                                 break;
497                         case oxp_2:
498                         case oxp_x1:
499                                 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
500                                 if (ret)
501                                         return ret;
502                                 /* scale from range [0-184] */
503                                 *val = (*val * 255) / 184;
504                                 break;
505                         case aya_neo_2:
506                         case aya_neo_air:
507                         case aya_neo_air_1s:
508                         case aya_neo_air_plus_mendo:
509                         case aya_neo_air_pro:
510                         case aya_neo_flip:
511                         case aya_neo_geek:
512                         case aya_neo_kun:
513                         case oxp_mini_amd:
514                         case oxp_mini_amd_a07:
515                                 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
516                                 if (ret)
517                                         return ret;
518                                 /* scale from range [0-100] */
519                                 *val = (*val * 255) / 100;
520                                 break;
521                         case aok_zoe_a1:
522                         case oxp_fly:
523                         case oxp_mini_amd_pro:
524                         default:
525                                 ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
526                                 if (ret)
527                                         return ret;
528                                 break;
529                         }
530                         return 0;
531                 case hwmon_pwm_enable:
532                         switch (board) {
533                         case orange_pi_neo:
534                                 return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
535                         case aok_zoe_a1:
536                         case aya_neo_2:
537                         case aya_neo_air:
538                         case aya_neo_air_1s:
539                         case aya_neo_air_plus_mendo:
540                         case aya_neo_air_pro:
541                         case aya_neo_flip:
542                         case aya_neo_geek:
543                         case aya_neo_kun:
544                         case oxp_2:
545                         case oxp_fly:
546                         case oxp_mini_amd:
547                         case oxp_mini_amd_a07:
548                         case oxp_mini_amd_pro:
549                         case oxp_x1:
550                                 return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
551                         default:
552                                 break;
553                         }
554                         break;
555                 default:
556                         break;
557                 }
558                 break;
559         default:
560                 break;
561         }
562         return -EOPNOTSUPP;
563 }
564
565 static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
566                               u32 attr, int channel, long val)
567 {
568         switch (type) {
569         case hwmon_pwm:
570                 switch (attr) {
571                 case hwmon_pwm_enable:
572                         if (val == 1)
573                                 return oxp_pwm_enable();
574                         else if (val == 0)
575                                 return oxp_pwm_disable();
576                         return -EINVAL;
577                 case hwmon_pwm_input:
578                         if (val < 0 || val > 255)
579                                 return -EINVAL;
580                         switch (board) {
581                         case orange_pi_neo:
582                                 /* scale to range [1-244] */
583                                 val = ((val - 1) * 243 / 254) + 1;
584                                 return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
585                         case oxp_2:
586                         case oxp_x1:
587                                 /* scale to range [0-184] */
588                                 val = (val * 184) / 255;
589                                 return write_to_ec(OXP_SENSOR_PWM_REG, val);
590                         case aya_neo_2:
591                         case aya_neo_air:
592                         case aya_neo_air_1s:
593                         case aya_neo_air_plus_mendo:
594                         case aya_neo_air_pro:
595                         case aya_neo_flip:
596                         case aya_neo_geek:
597                         case aya_neo_kun:
598                         case oxp_mini_amd:
599                         case oxp_mini_amd_a07:
600                                 /* scale to range [0-100] */
601                                 val = (val * 100) / 255;
602                                 return write_to_ec(OXP_SENSOR_PWM_REG, val);
603                         case aok_zoe_a1:
604                         case oxp_fly:
605                         case oxp_mini_amd_pro:
606                                 return write_to_ec(OXP_SENSOR_PWM_REG, val);
607                         default:
608                                 break;
609                         }
610                         break;
611                 default:
612                         break;
613                 }
614                 break;
615         default:
616                 break;
617         }
618         return -EOPNOTSUPP;
619 }
620
621 /* Known sensors in the OXP EC controllers */
622 static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
623         HWMON_CHANNEL_INFO(fan,
624                            HWMON_F_INPUT),
625         HWMON_CHANNEL_INFO(pwm,
626                            HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
627         NULL,
628 };
629
630 static struct attribute *oxp_ec_attrs[] = {
631         &dev_attr_tt_toggle.attr,
632         NULL
633 };
634
635 static struct attribute_group oxp_ec_attribute_group = {
636         .is_visible = tt_toggle_is_visible,
637         .attrs = oxp_ec_attrs,
638 };
639
640 static const struct attribute_group *oxp_ec_groups[] = {
641         &oxp_ec_attribute_group,
642         NULL
643 };
644
645 static const struct hwmon_ops oxp_ec_hwmon_ops = {
646         .is_visible = oxp_ec_hwmon_is_visible,
647         .read = oxp_platform_read,
648         .write = oxp_platform_write,
649 };
650
651 static const struct hwmon_chip_info oxp_ec_chip_info = {
652         .ops = &oxp_ec_hwmon_ops,
653         .info = oxp_platform_sensors,
654 };
655
656 /* Initialization logic */
657 static int oxp_platform_probe(struct platform_device *pdev)
658 {
659         struct device *dev = &pdev->dev;
660         struct device *hwdev;
661
662         hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
663                                                      &oxp_ec_chip_info, NULL);
664
665         return PTR_ERR_OR_ZERO(hwdev);
666 }
667
668 static struct platform_driver oxp_platform_driver = {
669         .driver = {
670                 .name = "oxp-platform",
671                 .dev_groups = oxp_ec_groups,
672         },
673         .probe = oxp_platform_probe,
674 };
675
676 static struct platform_device *oxp_platform_device;
677
678 static int __init oxp_platform_init(void)
679 {
680         const struct dmi_system_id *dmi_entry;
681
682         dmi_entry = dmi_first_match(dmi_table);
683         if (!dmi_entry)
684                 return -ENODEV;
685
686         board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
687
688         /*
689          * Have to check for AMD processor here because DMI strings are the same
690          * between Intel and AMD boards on older OneXPlayer devices, the only way
691          * to tell them apart is the CPU. Old Intel boards have an unsupported EC.
692          */
693         if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
694                 return -ENODEV;
695
696         oxp_platform_device =
697                 platform_create_bundle(&oxp_platform_driver,
698                                        oxp_platform_probe, NULL, 0, NULL, 0);
699
700         return PTR_ERR_OR_ZERO(oxp_platform_device);
701 }
702
703 static void __exit oxp_platform_exit(void)
704 {
705         platform_device_unregister(oxp_platform_device);
706         platform_driver_unregister(&oxp_platform_driver);
707 }
708
709 MODULE_DEVICE_TABLE(dmi, dmi_table);
710
711 module_init(oxp_platform_init);
712 module_exit(oxp_platform_exit);
713
714 MODULE_AUTHOR("Joaquín Ignacio Aramendía <[email protected]>");
715 MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
716 MODULE_LICENSE("GPL");
This page took 0.10553 seconds and 4 git commands to generate.