Merge tag 'acpi-5.15-rc1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-2.6-microblaze.git] / drivers / leds / leds-ipaq-micro.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  * h3xxx atmel micro companion support, notification LED subdevice
5  *
6  * Author : Linus Walleij <linus.walleij@linaro.org>
7  */
8
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/mfd/ipaq-micro.h>
12 #include <linux/leds.h>
13
14 #define LED_YELLOW      0x00
15 #define LED_GREEN       0x01
16
17 #define LED_EN       (1 << 4) /* LED ON/OFF 0:off, 1:on                       */
18 #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */
19 #define LED_ALWAYS   (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask         */
20
21 static int micro_leds_brightness_set(struct led_classdev *led_cdev,
22                                       enum led_brightness value)
23 {
24         struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent);
25         /*
26          * In this message:
27          * Byte 0 = LED color: 0 = yellow, 1 = green
28          *          yellow LED is always ~30 blinks per minute
29          * Byte 1 = duration (flags?) appears to be ignored
30          * Byte 2 = green ontime in 1/10 sec (deciseconds)
31          *          1 = 1/10 second
32          *          0 = 256/10 second
33          * Byte 3 = green offtime in 1/10 sec (deciseconds)
34          *          1 = 1/10 second
35          *          0 = 256/10 seconds
36          */
37         struct ipaq_micro_msg msg = {
38                 .id = MSG_NOTIFY_LED,
39                 .tx_len = 4,
40         };
41
42         msg.tx_data[0] = LED_GREEN;
43         msg.tx_data[1] = 0;
44         if (value) {
45                 msg.tx_data[2] = 0; /* Duty cycle 256 */
46                 msg.tx_data[3] = 1;
47         } else {
48                 msg.tx_data[2] = 1;
49                 msg.tx_data[3] = 0; /* Duty cycle 256 */
50         }
51         return ipaq_micro_tx_msg_sync(micro, &msg);
52 }
53
54 /* Maximum duty cycle in ms 256/10 sec = 25600 ms */
55 #define IPAQ_LED_MAX_DUTY 25600
56
57 static int micro_leds_blink_set(struct led_classdev *led_cdev,
58                                 unsigned long *delay_on,
59                                 unsigned long *delay_off)
60 {
61         struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent);
62         /*
63          * In this message:
64          * Byte 0 = LED color: 0 = yellow, 1 = green
65          *          yellow LED is always ~30 blinks per minute
66          * Byte 1 = duration (flags?) appears to be ignored
67          * Byte 2 = green ontime in 1/10 sec (deciseconds)
68          *          1 = 1/10 second
69          *          0 = 256/10 second
70          * Byte 3 = green offtime in 1/10 sec (deciseconds)
71          *          1 = 1/10 second
72          *          0 = 256/10 seconds
73          */
74         struct ipaq_micro_msg msg = {
75                 .id = MSG_NOTIFY_LED,
76                 .tx_len = 4,
77         };
78
79         msg.tx_data[0] = LED_GREEN;
80         if (*delay_on > IPAQ_LED_MAX_DUTY ||
81             *delay_off > IPAQ_LED_MAX_DUTY)
82                 return -EINVAL;
83
84         if (*delay_on == 0 && *delay_off == 0) {
85                 *delay_on = 100;
86                 *delay_off = 100;
87         }
88
89         msg.tx_data[1] = 0;
90         if (*delay_on >= IPAQ_LED_MAX_DUTY)
91                 msg.tx_data[2] = 0;
92         else
93                 msg.tx_data[2] = (u8) DIV_ROUND_CLOSEST(*delay_on, 100);
94         if (*delay_off >= IPAQ_LED_MAX_DUTY)
95                 msg.tx_data[3] = 0;
96         else
97                 msg.tx_data[3] = (u8) DIV_ROUND_CLOSEST(*delay_off, 100);
98         return ipaq_micro_tx_msg_sync(micro, &msg);
99 }
100
101 static struct led_classdev micro_led = {
102         .name                   = "led-ipaq-micro",
103         .brightness_set_blocking = micro_leds_brightness_set,
104         .blink_set              = micro_leds_blink_set,
105         .flags                  = LED_CORE_SUSPENDRESUME,
106 };
107
108 static int micro_leds_probe(struct platform_device *pdev)
109 {
110         int ret;
111
112         ret = devm_led_classdev_register(&pdev->dev, &micro_led);
113         if (ret) {
114                 dev_err(&pdev->dev, "registering led failed: %d\n", ret);
115                 return ret;
116         }
117         dev_info(&pdev->dev, "iPAQ micro notification LED driver\n");
118
119         return 0;
120 }
121
122 static struct platform_driver micro_leds_device_driver = {
123         .driver = {
124                 .name    = "ipaq-micro-leds",
125         },
126         .probe   = micro_leds_probe,
127 };
128 module_platform_driver(micro_leds_device_driver);
129
130 MODULE_LICENSE("GPL");
131 MODULE_DESCRIPTION("driver for iPAQ Atmel micro leds");
132 MODULE_ALIAS("platform:ipaq-micro-leds");