leds: ns2: register LED immediately after parsing DT properties
[linux-2.6-microblaze.git] / drivers / leds / leds-ns2.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED
4  *
5  * Copyright (C) 2010 LaCie
6  *
7  * Author: Simon Guinot <sguinot@lacie.com>
8  *
9  * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/leds.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include "leds.h"
20
21 enum ns2_led_modes {
22         NS_V2_LED_OFF,
23         NS_V2_LED_ON,
24         NS_V2_LED_SATA,
25 };
26
27 struct ns2_led_modval {
28         enum ns2_led_modes      mode;
29         int                     cmd_level;
30         int                     slow_level;
31 };
32
33 /*
34  * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
35  * modes are available: off, on and SATA activity blinking. The LED modes are
36  * controlled through two GPIOs (command and slow): each combination of values
37  * for the command/slow GPIOs corresponds to a LED mode.
38  */
39
40 struct ns2_led {
41         struct led_classdev     cdev;
42         struct gpio_desc        *cmd;
43         struct gpio_desc        *slow;
44         bool                    can_sleep;
45         unsigned char           sata; /* True when SATA mode active. */
46         rwlock_t                rw_lock; /* Lock GPIOs. */
47         int                     num_modes;
48         struct ns2_led_modval   *modval;
49 };
50
51 static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
52 {
53         int i;
54         int cmd_level;
55         int slow_level;
56
57         cmd_level = gpiod_get_value_cansleep(led->cmd);
58         slow_level = gpiod_get_value_cansleep(led->slow);
59
60         for (i = 0; i < led->num_modes; i++) {
61                 if (cmd_level == led->modval[i].cmd_level &&
62                     slow_level == led->modval[i].slow_level) {
63                         *mode = led->modval[i].mode;
64                         return 0;
65                 }
66         }
67
68         return -EINVAL;
69 }
70
71 static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
72 {
73         int i;
74         bool found = false;
75         unsigned long flags;
76
77         for (i = 0; i < led->num_modes; i++)
78                 if (mode == led->modval[i].mode) {
79                         found = true;
80                         break;
81                 }
82
83         if (!found)
84                 return;
85
86         write_lock_irqsave(&led->rw_lock, flags);
87
88         if (!led->can_sleep) {
89                 gpiod_set_value(led->cmd, led->modval[i].cmd_level);
90                 gpiod_set_value(led->slow, led->modval[i].slow_level);
91                 goto exit_unlock;
92         }
93
94         gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
95         gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
96
97 exit_unlock:
98         write_unlock_irqrestore(&led->rw_lock, flags);
99 }
100
101 static void ns2_led_set(struct led_classdev *led_cdev,
102                         enum led_brightness value)
103 {
104         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
105         enum ns2_led_modes mode;
106
107         if (value == LED_OFF)
108                 mode = NS_V2_LED_OFF;
109         else if (led->sata)
110                 mode = NS_V2_LED_SATA;
111         else
112                 mode = NS_V2_LED_ON;
113
114         ns2_led_set_mode(led, mode);
115 }
116
117 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
118                         enum led_brightness value)
119 {
120         ns2_led_set(led_cdev, value);
121         return 0;
122 }
123
124 static ssize_t ns2_led_sata_store(struct device *dev,
125                                   struct device_attribute *attr,
126                                   const char *buff, size_t count)
127 {
128         struct led_classdev *led_cdev = dev_get_drvdata(dev);
129         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
130         int ret;
131         unsigned long enable;
132
133         ret = kstrtoul(buff, 10, &enable);
134         if (ret < 0)
135                 return ret;
136
137         enable = !!enable;
138
139         if (led->sata == enable)
140                 goto exit;
141
142         led->sata = enable;
143
144         if (!led_get_brightness(led_cdev))
145                 goto exit;
146
147         if (enable)
148                 ns2_led_set_mode(led, NS_V2_LED_SATA);
149         else
150                 ns2_led_set_mode(led, NS_V2_LED_ON);
151
152 exit:
153         return count;
154 }
155
156 static ssize_t ns2_led_sata_show(struct device *dev,
157                                  struct device_attribute *attr, char *buf)
158 {
159         struct led_classdev *led_cdev = dev_get_drvdata(dev);
160         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
161
162         return sprintf(buf, "%d\n", led->sata);
163 }
164
165 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
166
167 static struct attribute *ns2_led_attrs[] = {
168         &dev_attr_sata.attr,
169         NULL
170 };
171 ATTRIBUTE_GROUPS(ns2_led);
172
173 static int ns2_led_register(struct device *dev, struct device_node *np,
174                             struct ns2_led *led)
175 {
176         struct ns2_led_modval *modval;
177         enum ns2_led_modes mode;
178         int nmodes, ret, i;
179
180         ret = of_property_read_string(np, "label", &led->cdev.name);
181         if (ret)
182                 led->cdev.name = np->name;
183
184         led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0,
185                                                GPIOD_ASIS, np->name);
186         if (IS_ERR(led->cmd))
187                 return PTR_ERR(led->cmd);
188
189         led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0,
190                                                 GPIOD_ASIS, np->name);
191         if (IS_ERR(led->slow))
192                 return PTR_ERR(led->slow);
193
194         of_property_read_string(np, "linux,default-trigger",
195                                 &led->cdev.default_trigger);
196
197         ret = of_property_count_u32_elems(np, "modes-map");
198         if (ret < 0 || ret % 3) {
199                 dev_err(dev, "Missing or malformed modes-map for %pOF\n", np);
200                 return -EINVAL;
201         }
202
203         nmodes = ret / 3;
204         modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
205         if (!modval)
206                 return -ENOMEM;
207
208         for (i = 0; i < nmodes; i++) {
209                 u32 val;
210
211                 of_property_read_u32_index(np, "modes-map", 3 * i, &val);
212                 modval[i].mode = val;
213                 of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val);
214                 modval[i].cmd_level = val;
215                 of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val);
216                 modval[i].slow_level = val;
217         }
218
219         rwlock_init(&led->rw_lock);
220
221         led->cdev.blink_set = NULL;
222         led->cdev.flags |= LED_CORE_SUSPENDRESUME;
223         led->cdev.groups = ns2_led_groups;
224         led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
225         if (led->can_sleep)
226                 led->cdev.brightness_set_blocking = ns2_led_set_blocking;
227         else
228                 led->cdev.brightness_set = ns2_led_set;
229         led->num_modes = nmodes;
230         led->modval = modval;
231
232         ret = ns2_led_get_mode(led, &mode);
233         if (ret < 0)
234                 return ret;
235
236         /* Set LED initial state. */
237         led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
238         led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
239
240         ret = devm_led_classdev_register(dev, &led->cdev);
241         if (ret)
242                 dev_err(dev, "Failed to register LED for node %pOF\n", np);
243
244         return ret;
245 }
246
247 static const struct of_device_id of_ns2_leds_match[] = {
248         { .compatible = "lacie,ns2-leds", },
249         {},
250 };
251 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
252
253 static int ns2_led_probe(struct platform_device *pdev)
254 {
255         struct device *dev = &pdev->dev;
256         struct device_node *np, *child;
257         struct ns2_led *leds;
258         int count;
259         int ret;
260
261         np = dev_of_node(dev);
262         count = of_get_available_child_count(np);
263         if (!count)
264                 return -ENODEV;
265
266         leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
267         if (!leds)
268                 return -ENOMEM;
269
270         for_each_available_child_of_node(np, child) {
271                 ret = ns2_led_register(dev, child, leds++);
272                 if (ret) {
273                         of_node_put(child);
274                         return ret;
275                 }
276         }
277
278         return 0;
279 }
280
281 static struct platform_driver ns2_led_driver = {
282         .probe          = ns2_led_probe,
283         .driver         = {
284                 .name           = "leds-ns2",
285                 .of_match_table = of_match_ptr(of_ns2_leds_match),
286         },
287 };
288
289 module_platform_driver(ns2_led_driver);
290
291 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
292 MODULE_DESCRIPTION("Network Space v2 LED driver");
293 MODULE_LICENSE("GPL");
294 MODULE_ALIAS("platform:leds-ns2");