1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25c6f844cSSachin Kamat #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3cec035deSMárton Németh
4cec035deSMárton Németh #include <linux/module.h>
5cec035deSMárton Németh
6cec035deSMárton Németh #include <linux/platform_device.h>
7cec035deSMárton Németh #include <linux/err.h>
8cec035deSMárton Németh #include <linux/leds.h>
9cec035deSMárton Németh
10cec035deSMárton Németh #include <linux/io.h>
11cec035deSMárton Németh #include <linux/dmi.h>
12cec035deSMárton Németh
13cec035deSMárton Németh #include <linux/i8042.h>
14cec035deSMárton Németh
15cec035deSMárton Németh #define CLEVO_MAIL_LED_OFF 0x0084
16cec035deSMárton Németh #define CLEVO_MAIL_LED_BLINK_1HZ 0x008A
17cec035deSMárton Németh #define CLEVO_MAIL_LED_BLINK_0_5HZ 0x0083
18cec035deSMárton Németh
194d404fd5SNémeth Márton MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
20cec035deSMárton Németh MODULE_DESCRIPTION("Clevo mail LED driver");
21cec035deSMárton Németh MODULE_LICENSE("GPL");
22cec035deSMárton Németh
23aad0f292SJingoo Han static bool nodetect;
24cec035deSMárton Németh module_param_named(nodetect, nodetect, bool, 0);
25cec035deSMárton Németh MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
26cec035deSMárton Németh
27cec035deSMárton Németh static struct platform_device *pdev;
28cec035deSMárton Németh
clevo_mail_led_dmi_callback(const struct dmi_system_id * id)29cec035deSMárton Németh static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
30cec035deSMárton Németh {
315c6f844cSSachin Kamat pr_info("'%s' found\n", id->ident);
32cec035deSMárton Németh return 1;
33cec035deSMárton Németh }
34cec035deSMárton Németh
35cec035deSMárton Németh /*
3696f09791SOndrej Zary * struct clevo_mail_led_dmi_table - List of known good models
37cec035deSMárton Németh *
38cec035deSMárton Németh * Contains the known good models this driver is compatible with.
39cec035deSMárton Németh * When adding a new model try to be as strict as possible. This
40cec035deSMárton Németh * makes it possible to keep the false positives (the model is
41cec035deSMárton Németh * detected as working, but in reality it is not) as low as
42cec035deSMárton Németh * possible.
43cec035deSMárton Németh */
446faadbbbSChristoph Hellwig static const struct dmi_system_id clevo_mail_led_dmi_table[] __initconst = {
45cec035deSMárton Németh {
46cec035deSMárton Németh .callback = clevo_mail_led_dmi_callback,
47cec035deSMárton Németh .ident = "Clevo D410J",
48cec035deSMárton Németh .matches = {
49cec035deSMárton Németh DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
50cec035deSMárton Németh DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
51cec035deSMárton Németh DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
52cec035deSMárton Németh }
53cec035deSMárton Németh },
54cec035deSMárton Németh {
55cec035deSMárton Németh .callback = clevo_mail_led_dmi_callback,
56cec035deSMárton Németh .ident = "Clevo M5x0N",
57cec035deSMárton Németh .matches = {
58cec035deSMárton Németh DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
59cec035deSMárton Németh DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
60cec035deSMárton Németh }
61cec035deSMárton Németh },
62cec035deSMárton Németh {
63cec035deSMárton Németh .callback = clevo_mail_led_dmi_callback,
64ee539a93SOndrej Zary .ident = "Clevo M5x0V",
65cec035deSMárton Németh .matches = {
66cec035deSMárton Németh DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
67cec035deSMárton Németh DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
68cec035deSMárton Németh DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
69cec035deSMárton Németh }
70cec035deSMárton Németh },
71cec035deSMárton Németh {
72cec035deSMárton Németh .callback = clevo_mail_led_dmi_callback,
73b3ba31f8SMrton Nmeth .ident = "Clevo D400P",
74b3ba31f8SMrton Nmeth .matches = {
75b3ba31f8SMrton Nmeth DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
76b3ba31f8SMrton Nmeth DMI_MATCH(DMI_BOARD_NAME, "D400P"),
77b3ba31f8SMrton Nmeth DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
78b3ba31f8SMrton Nmeth DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
79b3ba31f8SMrton Nmeth }
80b3ba31f8SMrton Nmeth },
81b3ba31f8SMrton Nmeth {
82b3ba31f8SMrton Nmeth .callback = clevo_mail_led_dmi_callback,
83cec035deSMárton Németh .ident = "Clevo D410V",
84cec035deSMárton Németh .matches = {
85cec035deSMárton Németh DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
86cec035deSMárton Németh DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
87cec035deSMárton Németh DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
88cec035deSMárton Németh DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
89cec035deSMárton Németh }
90cec035deSMárton Németh },
91cec035deSMárton Németh { }
92cec035deSMárton Németh };
9396f09791SOndrej Zary MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table);
94cec035deSMárton Németh
clevo_mail_led_set(struct led_classdev * led_cdev,enum led_brightness value)95cec035deSMárton Németh static void clevo_mail_led_set(struct led_classdev *led_cdev,
96cec035deSMárton Németh enum led_brightness value)
97cec035deSMárton Németh {
98181d683dSDmitry Torokhov i8042_lock_chip();
99181d683dSDmitry Torokhov
100cec035deSMárton Németh if (value == LED_OFF)
101cec035deSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_OFF);
102cec035deSMárton Németh else if (value <= LED_HALF)
103cec035deSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
104cec035deSMárton Németh else
105cec035deSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
106cec035deSMárton Németh
107181d683dSDmitry Torokhov i8042_unlock_chip();
108181d683dSDmitry Torokhov
109cec035deSMárton Németh }
110cec035deSMárton Németh
clevo_mail_led_blink(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)11192e015cbSMárton Németh static int clevo_mail_led_blink(struct led_classdev *led_cdev,
11292e015cbSMárton Németh unsigned long *delay_on,
11392e015cbSMárton Németh unsigned long *delay_off)
11492e015cbSMárton Németh {
11592e015cbSMárton Németh int status = -EINVAL;
11692e015cbSMárton Németh
117181d683dSDmitry Torokhov i8042_lock_chip();
118181d683dSDmitry Torokhov
11992e015cbSMárton Németh if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
12092e015cbSMárton Németh /* Special case: the leds subsystem requested us to
12192e015cbSMárton Németh * chose one user friendly blinking of the LED, and
12292e015cbSMárton Németh * start it. Let's blink the led slowly (0.5Hz).
12392e015cbSMárton Németh */
12492e015cbSMárton Németh *delay_on = 1000; /* ms */
12592e015cbSMárton Németh *delay_off = 1000; /* ms */
12692e015cbSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
12792e015cbSMárton Németh status = 0;
12892e015cbSMárton Németh
12992e015cbSMárton Németh } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
13092e015cbSMárton Németh /* blink the led with 1Hz */
13192e015cbSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
13292e015cbSMárton Németh status = 0;
13392e015cbSMárton Németh
13492e015cbSMárton Németh } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
13592e015cbSMárton Németh /* blink the led with 0.5Hz */
13692e015cbSMárton Németh i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
13792e015cbSMárton Németh status = 0;
13892e015cbSMárton Németh
13992e015cbSMárton Németh } else {
1405c6f844cSSachin Kamat pr_debug("clevo_mail_led_blink(..., %lu, %lu),"
14192e015cbSMárton Németh " returning -EINVAL (unsupported)\n",
14292e015cbSMárton Németh *delay_on, *delay_off);
14392e015cbSMárton Németh }
14492e015cbSMárton Németh
145181d683dSDmitry Torokhov i8042_unlock_chip();
146181d683dSDmitry Torokhov
14792e015cbSMárton Németh return status;
14892e015cbSMárton Németh }
14992e015cbSMárton Németh
150cec035deSMárton Németh static struct led_classdev clevo_mail_led = {
1516c152beeSRichard Purdie .name = "clevo::mail",
152cec035deSMárton Németh .brightness_set = clevo_mail_led_set,
15392e015cbSMárton Németh .blink_set = clevo_mail_led_blink,
154859cb7f2SRichard Purdie .flags = LED_CORE_SUSPENDRESUME,
155cec035deSMárton Németh };
156cec035deSMárton Németh
clevo_mail_led_probe(struct platform_device * pdev)1570016db26SJean Delvare static int __init clevo_mail_led_probe(struct platform_device *pdev)
158cec035deSMárton Németh {
159cec035deSMárton Németh return led_classdev_register(&pdev->dev, &clevo_mail_led);
160cec035deSMárton Németh }
161cec035deSMárton Németh
clevo_mail_led_remove(struct platform_device * pdev)162cec035deSMárton Németh static int clevo_mail_led_remove(struct platform_device *pdev)
163cec035deSMárton Németh {
164cec035deSMárton Németh led_classdev_unregister(&clevo_mail_led);
165cec035deSMárton Németh return 0;
166cec035deSMárton Németh }
167cec035deSMárton Németh
168cec035deSMárton Németh static struct platform_driver clevo_mail_led_driver = {
169cec035deSMárton Németh .remove = clevo_mail_led_remove,
170cec035deSMárton Németh .driver = {
171cec035deSMárton Németh .name = KBUILD_MODNAME,
172cec035deSMárton Németh },
173cec035deSMárton Németh };
174cec035deSMárton Németh
clevo_mail_led_init(void)175cec035deSMárton Németh static int __init clevo_mail_led_init(void)
176cec035deSMárton Németh {
177cec035deSMárton Németh int error = 0;
178cec035deSMárton Németh int count = 0;
179cec035deSMárton Németh
180cec035deSMárton Németh /* Check with the help of DMI if we are running on supported hardware */
181cec035deSMárton Németh if (!nodetect) {
18296f09791SOndrej Zary count = dmi_check_system(clevo_mail_led_dmi_table);
183cec035deSMárton Németh } else {
184cec035deSMárton Németh count = 1;
1855c6f844cSSachin Kamat pr_err("Skipping DMI detection. "
186cec035deSMárton Németh "If the driver works on your hardware please "
187cec035deSMárton Németh "report model and the output of dmidecode in tracker "
188cec035deSMárton Németh "at http://sourceforge.net/projects/clevo-mailled/\n");
189cec035deSMárton Németh }
190cec035deSMárton Németh
191cec035deSMárton Németh if (!count)
192cec035deSMárton Németh return -ENODEV;
193cec035deSMárton Németh
194cec035deSMárton Németh pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
195cec035deSMárton Németh if (!IS_ERR(pdev)) {
196cec035deSMárton Németh error = platform_driver_probe(&clevo_mail_led_driver,
197cec035deSMárton Németh clevo_mail_led_probe);
198cec035deSMárton Németh if (error) {
1995c6f844cSSachin Kamat pr_err("Can't probe platform driver\n");
200cec035deSMárton Németh platform_device_unregister(pdev);
201cec035deSMárton Németh }
202cec035deSMárton Németh } else
203cec035deSMárton Németh error = PTR_ERR(pdev);
204cec035deSMárton Németh
205cec035deSMárton Németh return error;
206cec035deSMárton Németh }
207cec035deSMárton Németh
clevo_mail_led_exit(void)208cec035deSMárton Németh static void __exit clevo_mail_led_exit(void)
209cec035deSMárton Németh {
210cec035deSMárton Németh platform_device_unregister(pdev);
211cec035deSMárton Németh platform_driver_unregister(&clevo_mail_led_driver);
212cec035deSMárton Németh
213cec035deSMárton Németh clevo_mail_led_set(NULL, LED_OFF);
214cec035deSMárton Németh }
215cec035deSMárton Németh
216cec035deSMárton Németh module_init(clevo_mail_led_init);
217cec035deSMárton Németh module_exit(clevo_mail_led_exit);
218