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