Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4da498fc | 2 | /* |
4da498fc LW |
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 | ||
95281b5b MFW |
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 */ | |
4da498fc | 20 | |
ba1c8179 | 21 | static int micro_leds_brightness_set(struct led_classdev *led_cdev, |
4da498fc LW |
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 | } | |
ba1c8179 | 51 | return ipaq_micro_tx_msg_sync(micro, &msg); |
4da498fc LW |
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; | |
95281b5b | 80 | if (*delay_on > IPAQ_LED_MAX_DUTY || |
4da498fc | 81 | *delay_off > IPAQ_LED_MAX_DUTY) |
95281b5b | 82 | return -EINVAL; |
4da498fc | 83 | |
95281b5b MFW |
84 | if (*delay_on == 0 && *delay_off == 0) { |
85 | *delay_on = 100; | |
86 | *delay_off = 100; | |
87 | } | |
4da498fc LW |
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", | |
ba1c8179 | 103 | .brightness_set_blocking = micro_leds_brightness_set, |
4da498fc LW |
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 | ||
431557f4 | 112 | ret = devm_led_classdev_register(&pdev->dev, µ_led); |
4da498fc LW |
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 | ||
e661c897 | 122 | static struct platform_driver micro_leds_device_driver = { |
4da498fc LW |
123 | .driver = { |
124 | .name = "ipaq-micro-leds", | |
125 | }, | |
126 | .probe = micro_leds_probe, | |
4da498fc LW |
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"); |