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