Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0003 
0004 #include <linux/module.h>
0005 
0006 #include <linux/platform_device.h>
0007 #include <linux/err.h>
0008 #include <linux/leds.h>
0009 
0010 #include <linux/io.h>
0011 #include <linux/dmi.h>
0012 
0013 #include <linux/i8042.h>
0014 
0015 #define CLEVO_MAIL_LED_OFF      0x0084
0016 #define CLEVO_MAIL_LED_BLINK_1HZ    0x008A
0017 #define CLEVO_MAIL_LED_BLINK_0_5HZ  0x0083
0018 
0019 MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
0020 MODULE_DESCRIPTION("Clevo mail LED driver");
0021 MODULE_LICENSE("GPL");
0022 
0023 static bool nodetect;
0024 module_param_named(nodetect, nodetect, bool, 0);
0025 MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection");
0026 
0027 static struct platform_device *pdev;
0028 
0029 static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
0030 {
0031     pr_info("'%s' found\n", id->ident);
0032     return 1;
0033 }
0034 
0035 /*
0036  * struct clevo_mail_led_dmi_table - List of known good models
0037  *
0038  * Contains the known good models this driver is compatible with.
0039  * When adding a new model try to be as strict as possible. This
0040  * makes it possible to keep the false positives (the model is
0041  * detected as working, but in reality it is not) as low as
0042  * possible.
0043  */
0044 static const struct dmi_system_id clevo_mail_led_dmi_table[] __initconst = {
0045     {
0046         .callback = clevo_mail_led_dmi_callback,
0047         .ident = "Clevo D410J",
0048         .matches = {
0049             DMI_MATCH(DMI_SYS_VENDOR, "VIA"),
0050             DMI_MATCH(DMI_PRODUCT_NAME, "K8N800"),
0051             DMI_MATCH(DMI_PRODUCT_VERSION, "VT8204B")
0052         }
0053     },
0054     {
0055         .callback = clevo_mail_led_dmi_callback,
0056         .ident = "Clevo M5x0N",
0057         .matches = {
0058             DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
0059             DMI_MATCH(DMI_PRODUCT_NAME, "M5x0N")
0060         }
0061     },
0062     {
0063         .callback = clevo_mail_led_dmi_callback,
0064         .ident = "Clevo M5x0V",
0065         .matches = {
0066             DMI_MATCH(DMI_BOARD_VENDOR, "CLEVO Co. "),
0067             DMI_MATCH(DMI_BOARD_NAME, "M5X0V "),
0068             DMI_MATCH(DMI_PRODUCT_VERSION, "VT6198")
0069         }
0070     },
0071     {
0072         .callback = clevo_mail_led_dmi_callback,
0073         .ident = "Clevo D400P",
0074         .matches = {
0075             DMI_MATCH(DMI_BOARD_VENDOR, "Clevo"),
0076             DMI_MATCH(DMI_BOARD_NAME, "D400P"),
0077             DMI_MATCH(DMI_BOARD_VERSION, "Rev.A"),
0078             DMI_MATCH(DMI_PRODUCT_VERSION, "0106")
0079         }
0080     },
0081     {
0082         .callback = clevo_mail_led_dmi_callback,
0083         .ident = "Clevo D410V",
0084         .matches = {
0085             DMI_MATCH(DMI_BOARD_VENDOR, "Clevo, Co."),
0086             DMI_MATCH(DMI_BOARD_NAME, "D400V/D470V"),
0087             DMI_MATCH(DMI_BOARD_VERSION, "SS78B"),
0088             DMI_MATCH(DMI_PRODUCT_VERSION, "Rev. A1")
0089         }
0090     },
0091     { }
0092 };
0093 MODULE_DEVICE_TABLE(dmi, clevo_mail_led_dmi_table);
0094 
0095 static void clevo_mail_led_set(struct led_classdev *led_cdev,
0096                 enum led_brightness value)
0097 {
0098     i8042_lock_chip();
0099 
0100     if (value == LED_OFF)
0101         i8042_command(NULL, CLEVO_MAIL_LED_OFF);
0102     else if (value <= LED_HALF)
0103         i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0104     else
0105         i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
0106 
0107     i8042_unlock_chip();
0108 
0109 }
0110 
0111 static int clevo_mail_led_blink(struct led_classdev *led_cdev,
0112                 unsigned long *delay_on,
0113                 unsigned long *delay_off)
0114 {
0115     int status = -EINVAL;
0116 
0117     i8042_lock_chip();
0118 
0119     if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
0120         /* Special case: the leds subsystem requested us to
0121          * chose one user friendly blinking of the LED, and
0122          * start it. Let's blink the led slowly (0.5Hz).
0123          */
0124         *delay_on = 1000; /* ms */
0125         *delay_off = 1000; /* ms */
0126         i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0127         status = 0;
0128 
0129     } else if (*delay_on == 500 /* ms */ && *delay_off == 500 /* ms */) {
0130         /* blink the led with 1Hz */
0131         i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
0132         status = 0;
0133 
0134     } else if (*delay_on == 1000 /* ms */ && *delay_off == 1000 /* ms */) {
0135         /* blink the led with 0.5Hz */
0136         i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ);
0137         status = 0;
0138 
0139     } else {
0140         pr_debug("clevo_mail_led_blink(..., %lu, %lu),"
0141                " returning -EINVAL (unsupported)\n",
0142                *delay_on, *delay_off);
0143     }
0144 
0145     i8042_unlock_chip();
0146 
0147     return status;
0148 }
0149 
0150 static struct led_classdev clevo_mail_led = {
0151     .name           = "clevo::mail",
0152     .brightness_set     = clevo_mail_led_set,
0153     .blink_set      = clevo_mail_led_blink,
0154     .flags          = LED_CORE_SUSPENDRESUME,
0155 };
0156 
0157 static int __init clevo_mail_led_probe(struct platform_device *pdev)
0158 {
0159     return led_classdev_register(&pdev->dev, &clevo_mail_led);
0160 }
0161 
0162 static int clevo_mail_led_remove(struct platform_device *pdev)
0163 {
0164     led_classdev_unregister(&clevo_mail_led);
0165     return 0;
0166 }
0167 
0168 static struct platform_driver clevo_mail_led_driver = {
0169     .remove     = clevo_mail_led_remove,
0170     .driver     = {
0171         .name       = KBUILD_MODNAME,
0172     },
0173 };
0174 
0175 static int __init clevo_mail_led_init(void)
0176 {
0177     int error = 0;
0178     int count = 0;
0179 
0180     /* Check with the help of DMI if we are running on supported hardware */
0181     if (!nodetect) {
0182         count = dmi_check_system(clevo_mail_led_dmi_table);
0183     } else {
0184         count = 1;
0185         pr_err("Skipping DMI detection. "
0186                "If the driver works on your hardware please "
0187                "report model and the output of dmidecode in tracker "
0188                "at http://sourceforge.net/projects/clevo-mailled/\n");
0189     }
0190 
0191     if (!count)
0192         return -ENODEV;
0193 
0194     pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
0195     if (!IS_ERR(pdev)) {
0196         error = platform_driver_probe(&clevo_mail_led_driver,
0197                           clevo_mail_led_probe);
0198         if (error) {
0199             pr_err("Can't probe platform driver\n");
0200             platform_device_unregister(pdev);
0201         }
0202     } else
0203         error = PTR_ERR(pdev);
0204 
0205     return error;
0206 }
0207 
0208 static void __exit clevo_mail_led_exit(void)
0209 {
0210     platform_device_unregister(pdev);
0211     platform_driver_unregister(&clevo_mail_led_driver);
0212 
0213     clevo_mail_led_set(NULL, LED_OFF);
0214 }
0215 
0216 module_init(clevo_mail_led_init);
0217 module_exit(clevo_mail_led_exit);