109c434b8SThomas 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