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(dev: 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: &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(dev: 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: &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(parent: &pdev->dev, led_cdev: µ_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" ); |
133 | |