801b7f851be7a47e31cbc66cf568f97e973c2e7f
[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 struct ns2_led_of_one {
34         const char      *name;
35         const char      *default_trigger;
36         struct gpio_desc *cmd;
37         struct gpio_desc *slow;
38         int             num_modes;
39         struct ns2_led_modval *modval;
40 };
41
42 struct ns2_led_of {
43         int                     num_leds;
44         struct ns2_led_of_one   *leds;
45 };
46
47 /*
48  * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
49  * modes are available: off, on and SATA activity blinking. The LED modes are
50  * controlled through two GPIOs (command and slow): each combination of values
51  * for the command/slow GPIOs corresponds to a LED mode.
52  */
53
54 struct ns2_led {
55         struct led_classdev     cdev;
56         struct gpio_desc        *cmd;
57         struct gpio_desc        *slow;
58         bool                    can_sleep;
59         unsigned char           sata; /* True when SATA mode active. */
60         rwlock_t                rw_lock; /* Lock GPIOs. */
61         int                     num_modes;
62         struct ns2_led_modval   *modval;
63 };
64
65 static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
66 {
67         int i;
68         int cmd_level;
69         int slow_level;
70
71         cmd_level = gpiod_get_value_cansleep(led->cmd);
72         slow_level = gpiod_get_value_cansleep(led->slow);
73
74         for (i = 0; i < led->num_modes; i++) {
75                 if (cmd_level == led->modval[i].cmd_level &&
76                     slow_level == led->modval[i].slow_level) {
77                         *mode = led->modval[i].mode;
78                         return 0;
79                 }
80         }
81
82         return -EINVAL;
83 }
84
85 static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
86 {
87         int i;
88         bool found = false;
89         unsigned long flags;
90
91         for (i = 0; i < led->num_modes; i++)
92                 if (mode == led->modval[i].mode) {
93                         found = true;
94                         break;
95                 }
96
97         if (!found)
98                 return;
99
100         write_lock_irqsave(&led->rw_lock, flags);
101
102         if (!led->can_sleep) {
103                 gpiod_set_value(led->cmd, led->modval[i].cmd_level);
104                 gpiod_set_value(led->slow, led->modval[i].slow_level);
105                 goto exit_unlock;
106         }
107
108         gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
109         gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
110
111 exit_unlock:
112         write_unlock_irqrestore(&led->rw_lock, flags);
113 }
114
115 static void ns2_led_set(struct led_classdev *led_cdev,
116                         enum led_brightness value)
117 {
118         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
119         enum ns2_led_modes mode;
120
121         if (value == LED_OFF)
122                 mode = NS_V2_LED_OFF;
123         else if (led->sata)
124                 mode = NS_V2_LED_SATA;
125         else
126                 mode = NS_V2_LED_ON;
127
128         ns2_led_set_mode(led, mode);
129 }
130
131 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
132                         enum led_brightness value)
133 {
134         ns2_led_set(led_cdev, value);
135         return 0;
136 }
137
138 static ssize_t ns2_led_sata_store(struct device *dev,
139                                   struct device_attribute *attr,
140                                   const char *buff, size_t count)
141 {
142         struct led_classdev *led_cdev = dev_get_drvdata(dev);
143         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
144         int ret;
145         unsigned long enable;
146
147         ret = kstrtoul(buff, 10, &enable);
148         if (ret < 0)
149                 return ret;
150
151         enable = !!enable;
152
153         if (led->sata == enable)
154                 goto exit;
155
156         led->sata = enable;
157
158         if (!led_get_brightness(led_cdev))
159                 goto exit;
160
161         if (enable)
162                 ns2_led_set_mode(led, NS_V2_LED_SATA);
163         else
164                 ns2_led_set_mode(led, NS_V2_LED_ON);
165
166 exit:
167         return count;
168 }
169
170 static ssize_t ns2_led_sata_show(struct device *dev,
171                                  struct device_attribute *attr, char *buf)
172 {
173         struct led_classdev *led_cdev = dev_get_drvdata(dev);
174         struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
175
176         return sprintf(buf, "%d\n", led->sata);
177 }
178
179 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
180
181 static struct attribute *ns2_led_attrs[] = {
182         &dev_attr_sata.attr,
183         NULL
184 };
185 ATTRIBUTE_GROUPS(ns2_led);
186
187 static int
188 create_ns2_led(struct device *dev, struct ns2_led *led,
189                const struct ns2_led_of_one *template)
190 {
191         int ret;
192         enum ns2_led_modes mode;
193
194         rwlock_init(&led->rw_lock);
195
196         led->cdev.name = template->name;
197         led->cdev.default_trigger = template->default_trigger;
198         led->cdev.blink_set = NULL;
199         led->cdev.flags |= LED_CORE_SUSPENDRESUME;
200         led->cdev.groups = ns2_led_groups;
201         led->cmd = template->cmd;
202         led->slow = template->slow;
203         led->can_sleep = gpiod_cansleep(led->cmd) | gpiod_cansleep(led->slow);
204         if (led->can_sleep)
205                 led->cdev.brightness_set_blocking = ns2_led_set_blocking;
206         else
207                 led->cdev.brightness_set = ns2_led_set;
208         led->modval = template->modval;
209         led->num_modes = template->num_modes;
210
211         ret = ns2_led_get_mode(led, &mode);
212         if (ret < 0)
213                 return ret;
214
215         /* Set LED initial state. */
216         led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
217         led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
218
219         return devm_led_classdev_register(dev, &led->cdev);
220 }
221
222 static int ns2_leds_parse_one(struct device *dev, struct device_node *np,
223                               struct ns2_led_of_one *led)
224 {
225         struct ns2_led_modval *modval;
226         int nmodes, ret, i;
227
228         ret = of_property_read_string(np, "label", &led->name);
229         if (ret)
230                 led->name = np->name;
231
232         led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0,
233                                                GPIOD_ASIS, led->name);
234         if (IS_ERR(led->cmd))
235                 return PTR_ERR(led->cmd);
236
237         led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0,
238                                                 GPIOD_ASIS, led->name);
239         if (IS_ERR(led->slow))
240                 return PTR_ERR(led->slow);
241
242         of_property_read_string(np, "linux,default-trigger",
243                                 &led->default_trigger);
244
245         ret = of_property_count_u32_elems(np, "modes-map");
246         if (ret < 0 || ret % 3) {
247                 dev_err(dev, "Missing or malformed modes-map for %pOF\n", np);
248                 return -EINVAL;
249         }
250
251         nmodes = ret / 3;
252         modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
253         if (!modval)
254                 return -ENOMEM;
255
256         for (i = 0; i < nmodes; i++) {
257                 u32 val;
258
259                 of_property_read_u32_index(np, "modes-map", 3 * i, &val);
260                 modval[i].mode = val;
261                 of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val);
262                 modval[i].cmd_level = val;
263                 of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val);
264                 modval[i].slow_level = val;
265         }
266
267         led->num_modes = nmodes;
268         led->modval = modval;
269
270         return 0;
271 }
272
273 /*
274  * Translate OpenFirmware node properties into platform_data.
275  */
276 static int
277 ns2_leds_parse_of(struct device *dev, struct ns2_led_of *ofdata)
278 {
279         struct device_node *np = dev_of_node(dev);
280         struct device_node *child;
281         struct ns2_led_of_one *led, *leds;
282         int ret, num_leds = 0;
283
284         num_leds = of_get_available_child_count(np);
285         if (!num_leds)
286                 return -ENODEV;
287
288         leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
289                             GFP_KERNEL);
290         if (!leds)
291                 return -ENOMEM;
292
293         led = leds;
294         for_each_available_child_of_node(np, child) {
295                 ret = ns2_leds_parse_one(dev, child, led++);
296                 if (ret < 0) {
297                         of_node_put(child);
298                         return ret;
299                 }
300         }
301
302         ofdata->leds = leds;
303         ofdata->num_leds = num_leds;
304
305         return 0;
306 }
307
308 static const struct of_device_id of_ns2_leds_match[] = {
309         { .compatible = "lacie,ns2-leds", },
310         {},
311 };
312 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
313
314 static int ns2_led_probe(struct platform_device *pdev)
315 {
316         struct device *dev = &pdev->dev;
317         struct ns2_led_of *ofdata;
318         struct ns2_led *leds;
319         int i;
320         int ret;
321
322         ofdata = devm_kzalloc(dev, sizeof(struct ns2_led_of), GFP_KERNEL);
323         if (!ofdata)
324                 return -ENOMEM;
325
326         ret = ns2_leds_parse_of(dev, ofdata);
327         if (ret)
328                 return ret;
329
330         leds = devm_kzalloc(dev, array_size(sizeof(*leds), ofdata->num_leds),
331                             GFP_KERNEL);
332         if (!leds)
333                 return -ENOMEM;
334
335         for (i = 0; i < ofdata->num_leds; i++) {
336                 ret = create_ns2_led(dev, &leds[i], &ofdata->leds[i]);
337                 if (ret < 0)
338                         return ret;
339         }
340
341         return 0;
342 }
343
344 static struct platform_driver ns2_led_driver = {
345         .probe          = ns2_led_probe,
346         .driver         = {
347                 .name           = "leds-ns2",
348                 .of_match_table = of_match_ptr(of_ns2_leds_match),
349         },
350 };
351
352 module_platform_driver(ns2_led_driver);
353
354 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
355 MODULE_DESCRIPTION("Network Space v2 LED driver");
356 MODULE_LICENSE("GPL");
357 MODULE_ALIAS("platform:leds-ns2");