Merge tag 'topic/phy-compliance-2020-04-08' of git://anongit.freedesktop.org/drm...
[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 {
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_platform_data {
43         int             num_leds;
44         struct ns2_led  *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_data {
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_data *led_dat,
66                             enum ns2_led_modes *mode)
67 {
68         int i;
69         int ret = -EINVAL;
70         int cmd_level;
71         int slow_level;
72
73         cmd_level = gpiod_get_value_cansleep(led_dat->cmd);
74         slow_level = gpiod_get_value_cansleep(led_dat->slow);
75
76         for (i = 0; i < led_dat->num_modes; i++) {
77                 if (cmd_level == led_dat->modval[i].cmd_level &&
78                     slow_level == led_dat->modval[i].slow_level) {
79                         *mode = led_dat->modval[i].mode;
80                         ret = 0;
81                         break;
82                 }
83         }
84
85         return ret;
86 }
87
88 static void ns2_led_set_mode(struct ns2_led_data *led_dat,
89                              enum ns2_led_modes mode)
90 {
91         int i;
92         bool found = false;
93         unsigned long flags;
94
95         for (i = 0; i < led_dat->num_modes; i++)
96                 if (mode == led_dat->modval[i].mode) {
97                         found = true;
98                         break;
99                 }
100
101         if (!found)
102                 return;
103
104         write_lock_irqsave(&led_dat->rw_lock, flags);
105
106         if (!led_dat->can_sleep) {
107                 gpiod_set_value(led_dat->cmd,
108                                 led_dat->modval[i].cmd_level);
109                 gpiod_set_value(led_dat->slow,
110                                 led_dat->modval[i].slow_level);
111                 goto exit_unlock;
112         }
113
114         gpiod_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
115         gpiod_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
116
117 exit_unlock:
118         write_unlock_irqrestore(&led_dat->rw_lock, flags);
119 }
120
121 static void ns2_led_set(struct led_classdev *led_cdev,
122                         enum led_brightness value)
123 {
124         struct ns2_led_data *led_dat =
125                 container_of(led_cdev, struct ns2_led_data, cdev);
126         enum ns2_led_modes mode;
127
128         if (value == LED_OFF)
129                 mode = NS_V2_LED_OFF;
130         else if (led_dat->sata)
131                 mode = NS_V2_LED_SATA;
132         else
133                 mode = NS_V2_LED_ON;
134
135         ns2_led_set_mode(led_dat, mode);
136 }
137
138 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
139                         enum led_brightness value)
140 {
141         ns2_led_set(led_cdev, value);
142         return 0;
143 }
144
145 static ssize_t ns2_led_sata_store(struct device *dev,
146                                   struct device_attribute *attr,
147                                   const char *buff, size_t count)
148 {
149         struct led_classdev *led_cdev = dev_get_drvdata(dev);
150         struct ns2_led_data *led_dat =
151                 container_of(led_cdev, struct ns2_led_data, cdev);
152         int ret;
153         unsigned long enable;
154
155         ret = kstrtoul(buff, 10, &enable);
156         if (ret < 0)
157                 return ret;
158
159         enable = !!enable;
160
161         if (led_dat->sata == enable)
162                 goto exit;
163
164         led_dat->sata = enable;
165
166         if (!led_get_brightness(led_cdev))
167                 goto exit;
168
169         if (enable)
170                 ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
171         else
172                 ns2_led_set_mode(led_dat, NS_V2_LED_ON);
173
174 exit:
175         return count;
176 }
177
178 static ssize_t ns2_led_sata_show(struct device *dev,
179                                  struct device_attribute *attr, char *buf)
180 {
181         struct led_classdev *led_cdev = dev_get_drvdata(dev);
182         struct ns2_led_data *led_dat =
183                 container_of(led_cdev, struct ns2_led_data, cdev);
184
185         return sprintf(buf, "%d\n", led_dat->sata);
186 }
187
188 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
189
190 static struct attribute *ns2_led_attrs[] = {
191         &dev_attr_sata.attr,
192         NULL
193 };
194 ATTRIBUTE_GROUPS(ns2_led);
195
196 static int
197 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
198                const struct ns2_led *template)
199 {
200         int ret;
201         enum ns2_led_modes mode;
202
203         rwlock_init(&led_dat->rw_lock);
204
205         led_dat->cdev.name = template->name;
206         led_dat->cdev.default_trigger = template->default_trigger;
207         led_dat->cdev.blink_set = NULL;
208         led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
209         led_dat->cdev.groups = ns2_led_groups;
210         led_dat->cmd = template->cmd;
211         led_dat->slow = template->slow;
212         led_dat->can_sleep = gpiod_cansleep(led_dat->cmd) |
213                                 gpiod_cansleep(led_dat->slow);
214         if (led_dat->can_sleep)
215                 led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
216         else
217                 led_dat->cdev.brightness_set = ns2_led_set;
218         led_dat->modval = template->modval;
219         led_dat->num_modes = template->num_modes;
220
221         ret = ns2_led_get_mode(led_dat, &mode);
222         if (ret < 0)
223                 return ret;
224
225         /* Set LED initial state. */
226         led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
227         led_dat->cdev.brightness =
228                 (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
229
230         ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
231         if (ret < 0)
232                 return ret;
233
234         return 0;
235 }
236
237 static void delete_ns2_led(struct ns2_led_data *led_dat)
238 {
239         led_classdev_unregister(&led_dat->cdev);
240 }
241
242 #ifdef CONFIG_OF_GPIO
243 /*
244  * Translate OpenFirmware node properties into platform_data.
245  */
246 static int
247 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
248 {
249         struct device_node *np = dev->of_node;
250         struct device_node *child;
251         struct ns2_led *led, *leds;
252         int ret, num_leds = 0;
253
254         num_leds = of_get_child_count(np);
255         if (!num_leds)
256                 return -ENODEV;
257
258         leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
259                             GFP_KERNEL);
260         if (!leds)
261                 return -ENOMEM;
262
263         led = leds;
264         for_each_child_of_node(np, child) {
265                 const char *string;
266                 int i, num_modes;
267                 struct ns2_led_modval *modval;
268                 struct gpio_desc *gd;
269
270                 ret = of_property_read_string(child, "label", &string);
271                 led->name = (ret == 0) ? string : child->name;
272
273                 gd = gpiod_get_from_of_node(child, "cmd-gpio", 0,
274                                             GPIOD_ASIS, led->name);
275                 if (IS_ERR(gd)) {
276                         ret = PTR_ERR(gd);
277                         goto err_node_put;
278                 }
279                 led->cmd = gd;
280                 gd = gpiod_get_from_of_node(child, "slow-gpio", 0,
281                                             GPIOD_ASIS, led->name);
282                 if (IS_ERR(gd)) {
283                         ret = PTR_ERR(gd);
284                         goto err_node_put;
285                 }
286                 led->slow = gd;
287
288                 ret = of_property_read_string(child, "linux,default-trigger",
289                                               &string);
290                 if (ret == 0)
291                         led->default_trigger = string;
292
293                 ret = of_property_count_u32_elems(child, "modes-map");
294                 if (ret < 0 || ret % 3) {
295                         dev_err(dev,
296                                 "Missing or malformed modes-map property\n");
297                         ret = -EINVAL;
298                         goto err_node_put;
299                 }
300
301                 num_modes = ret / 3;
302                 modval = devm_kcalloc(dev,
303                                       num_modes,
304                                       sizeof(struct ns2_led_modval),
305                                       GFP_KERNEL);
306                 if (!modval) {
307                         ret = -ENOMEM;
308                         goto err_node_put;
309                 }
310
311                 for (i = 0; i < num_modes; i++) {
312                         of_property_read_u32_index(child,
313                                                 "modes-map", 3 * i,
314                                                 (u32 *) &modval[i].mode);
315                         of_property_read_u32_index(child,
316                                                 "modes-map", 3 * i + 1,
317                                                 (u32 *) &modval[i].cmd_level);
318                         of_property_read_u32_index(child,
319                                                 "modes-map", 3 * i + 2,
320                                                 (u32 *) &modval[i].slow_level);
321                 }
322
323                 led->num_modes = num_modes;
324                 led->modval = modval;
325
326                 led++;
327         }
328
329         pdata->leds = leds;
330         pdata->num_leds = num_leds;
331
332         return 0;
333
334 err_node_put:
335         of_node_put(child);
336         return ret;
337 }
338
339 static const struct of_device_id of_ns2_leds_match[] = {
340         { .compatible = "lacie,ns2-leds", },
341         {},
342 };
343 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
344 #endif /* CONFIG_OF_GPIO */
345
346 struct ns2_led_priv {
347         int num_leds;
348         struct ns2_led_data leds_data[];
349 };
350
351 static inline int sizeof_ns2_led_priv(int num_leds)
352 {
353         return sizeof(struct ns2_led_priv) +
354                       (sizeof(struct ns2_led_data) * num_leds);
355 }
356
357 static int ns2_led_probe(struct platform_device *pdev)
358 {
359         struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
360         struct ns2_led_priv *priv;
361         int i;
362         int ret;
363
364 #ifdef CONFIG_OF_GPIO
365         if (!pdata) {
366                 pdata = devm_kzalloc(&pdev->dev,
367                                      sizeof(struct ns2_led_platform_data),
368                                      GFP_KERNEL);
369                 if (!pdata)
370                         return -ENOMEM;
371
372                 ret = ns2_leds_get_of_pdata(&pdev->dev, pdata);
373                 if (ret)
374                         return ret;
375         }
376 #else
377         if (!pdata)
378                 return -EINVAL;
379 #endif /* CONFIG_OF_GPIO */
380
381         priv = devm_kzalloc(&pdev->dev,
382                             sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL);
383         if (!priv)
384                 return -ENOMEM;
385         priv->num_leds = pdata->num_leds;
386
387         for (i = 0; i < priv->num_leds; i++) {
388                 ret = create_ns2_led(pdev, &priv->leds_data[i],
389                                      &pdata->leds[i]);
390                 if (ret < 0) {
391                         for (i = i - 1; i >= 0; i--)
392                                 delete_ns2_led(&priv->leds_data[i]);
393                         return ret;
394                 }
395         }
396
397         platform_set_drvdata(pdev, priv);
398
399         return 0;
400 }
401
402 static int ns2_led_remove(struct platform_device *pdev)
403 {
404         int i;
405         struct ns2_led_priv *priv;
406
407         priv = platform_get_drvdata(pdev);
408
409         for (i = 0; i < priv->num_leds; i++)
410                 delete_ns2_led(&priv->leds_data[i]);
411
412         return 0;
413 }
414
415 static struct platform_driver ns2_led_driver = {
416         .probe          = ns2_led_probe,
417         .remove         = ns2_led_remove,
418         .driver         = {
419                 .name           = "leds-ns2",
420                 .of_match_table = of_match_ptr(of_ns2_leds_match),
421         },
422 };
423
424 module_platform_driver(ns2_led_driver);
425
426 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
427 MODULE_DESCRIPTION("Network Space v2 LED driver");
428 MODULE_LICENSE("GPL");
429 MODULE_ALIAS("platform:leds-ns2");