brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 82da0fe Raw
217 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt3 4#include <linux/module.h>5 6#include <linux/platform_device.h>7#include <linux/err.h>8#include <linux/leds.h>9 10#include <linux/io.h>11#include <linux/dmi.h>12 13#include <linux/i8042.h>14 15#define CLEVO_MAIL_LED_OFF		0x008416#define CLEVO_MAIL_LED_BLINK_1HZ	0x008A17#define CLEVO_MAIL_LED_BLINK_0_5HZ	0x008318 19MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");20MODULE_DESCRIPTION("Clevo mail LED driver");21MODULE_LICENSE("GPL");22 23static bool nodetect;24module_param_named(nodetect, nodetect, bool, 0);25MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");26 27static struct platform_device *pdev;28 29static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)30{31	pr_info("'%s' found\n", id->ident);32	return 1;33}34 35/*36 * struct clevo_mail_led_dmi_table - List of known good models37 *38 * Contains the known good models this driver is compatible with.39 * When adding a new model try to be as strict as possible. This40 * makes it possible to keep the false positives (the model is41 * detected as working, but in reality it is not) as low as42 * possible.43 */44static const struct dmi_system_id clevo_mail_led_dmi_table[] __initconst = {45	{46		.callback = clevo_mail_led_dmi_callback,47		.ident = "Clevo D410J",48		.matches = {49			DMI_MATCH(DMI_SYS_VENDOR, "VIA"),50			DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),51			DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")52		}53	},54	{55		.callback = clevo_mail_led_dmi_callback,56		.ident = "Clevo M5x0N",57		.matches = {58			DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),59			DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")60		}61	},62	{63		.callback = clevo_mail_led_dmi_callback,64		.ident = "Clevo M5x0V",65		.matches = {66			DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),67			DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),68			DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")69		}70	},71	{72		.callback = clevo_mail_led_dmi_callback,73		.ident = "Clevo D400P",74		.matches = {75			DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),76			DMI_MATCH(DMI_BOARD_NAME, "D400P"),77			DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),78			DMI_MATCH(DMI_PRODUCT_VERSION, "0106")79		}80	},81	{82		.callback = clevo_mail_led_dmi_callback,83		.ident = "Clevo D410V",84		.matches = {85			DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),86			DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),87			DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),88			DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")89		}90	},91	{ }92};93MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table);94 95static void clevo_mail_led_set(struct led_classdev *led_cdev,96				enum led_brightness value)97{98	i8042_lock_chip();99 100	if (value == LED_OFF)101		i8042_command(NULL, CLEVO_MAIL_LED_OFF);102	else if (value <= LED_HALF)103		i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);104	else105		i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);106 107	i8042_unlock_chip();108 109}110 111static int clevo_mail_led_blink(struct led_classdev *led_cdev,112				unsigned long *delay_on,113				unsigned long *delay_off)114{115	int status = -EINVAL;116 117	i8042_lock_chip();118 119	if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {120		/* Special case: the leds subsystem requested us to121		 * chose one user friendly blinking of the LED, and122		 * start it. Let's blink the led slowly (0.5Hz).123		 */124		*delay_on = 1000; /* ms */125		*delay_off = 1000; /* ms */126		i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);127		status = 0;128 129	} else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {130		/* blink the led with 1Hz */131		i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);132		status = 0;133 134	} else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {135		/* blink the led with 0.5Hz */136		i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);137		status = 0;138 139	} else {140		pr_debug("clevo_mail_led_blink(..., %lu, %lu),"141		       " returning -EINVAL (unsupported)\n",142		       *delay_on, *delay_off);143	}144 145	i8042_unlock_chip();146 147	return status;148}149 150static struct led_classdev clevo_mail_led = {151	.name			= "clevo::mail",152	.brightness_set		= clevo_mail_led_set,153	.blink_set		= clevo_mail_led_blink,154	.flags			= LED_CORE_SUSPENDRESUME,155};156 157static int __init clevo_mail_led_probe(struct platform_device *pdev)158{159	return led_classdev_register(&pdev->dev, &clevo_mail_led);160}161 162static void clevo_mail_led_remove(struct platform_device *pdev)163{164	led_classdev_unregister(&clevo_mail_led);165}166 167static struct platform_driver clevo_mail_led_driver = {168	.remove_new	= clevo_mail_led_remove,169	.driver		= {170		.name		= KBUILD_MODNAME,171	},172};173 174static int __init clevo_mail_led_init(void)175{176	int error = 0;177	int count = 0;178 179	/* Check with the help of DMI if we are running on supported hardware */180	if (!nodetect) {181		count = dmi_check_system(clevo_mail_led_dmi_table);182	} else {183		count = 1;184		pr_err("Skipping DMI detection. "185		       "If the driver works on your hardware please "186		       "report model and the output of dmidecode in tracker "187		       "at http://sourceforge.net/projects/clevo-mailled/\n");188	}189 190	if (!count)191		return -ENODEV;192 193	pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);194	if (!IS_ERR(pdev)) {195		error = platform_driver_probe(&clevo_mail_led_driver,196					      clevo_mail_led_probe);197		if (error) {198			pr_err("Can't probe platform driver\n");199			platform_device_unregister(pdev);200		}201	} else202		error = PTR_ERR(pdev);203 204	return error;205}206 207static void __exit clevo_mail_led_exit(void)208{209	platform_device_unregister(pdev);210	platform_driver_unregister(&clevo_mail_led_driver);211 212	clevo_mail_led_set(NULL, LED_OFF);213}214 215module_init(clevo_mail_led_init);216module_exit(clevo_mail_led_exit);217