gpio: rcar: Fix runtime PM imbalance on error
[linux/fpc-iii.git] / drivers / leds / leds-ns2.c
blob538ca57556025db28f17f4800fe07a76aed2e8e4
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
5 * Copyright (C) 2010 LaCie
7 * Author: Simon Guinot <sguinot@lacie.com>
9 * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
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"
21 enum ns2_led_modes {
22 NS_V2_LED_OFF,
23 NS_V2_LED_ON,
24 NS_V2_LED_SATA,
27 struct ns2_led_modval {
28 enum ns2_led_modes mode;
29 int cmd_level;
30 int slow_level;
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;
42 struct ns2_led_platform_data {
43 int num_leds;
44 struct ns2_led *leds;
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.
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;
65 static int ns2_led_get_mode(struct ns2_led_data *led_dat,
66 enum ns2_led_modes *mode)
68 int i;
69 int ret = -EINVAL;
70 int cmd_level;
71 int slow_level;
73 cmd_level = gpiod_get_value_cansleep(led_dat->cmd);
74 slow_level = gpiod_get_value_cansleep(led_dat->slow);
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;
85 return ret;
88 static void ns2_led_set_mode(struct ns2_led_data *led_dat,
89 enum ns2_led_modes mode)
91 int i;
92 bool found = false;
93 unsigned long flags;
95 for (i = 0; i < led_dat->num_modes; i++)
96 if (mode == led_dat->modval[i].mode) {
97 found = true;
98 break;
101 if (!found)
102 return;
104 write_lock_irqsave(&led_dat->rw_lock, flags);
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;
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);
117 exit_unlock:
118 write_unlock_irqrestore(&led_dat->rw_lock, flags);
121 static void ns2_led_set(struct led_classdev *led_cdev,
122 enum led_brightness value)
124 struct ns2_led_data *led_dat =
125 container_of(led_cdev, struct ns2_led_data, cdev);
126 enum ns2_led_modes mode;
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;
135 ns2_led_set_mode(led_dat, mode);
138 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
139 enum led_brightness value)
141 ns2_led_set(led_cdev, value);
142 return 0;
145 static ssize_t ns2_led_sata_store(struct device *dev,
146 struct device_attribute *attr,
147 const char *buff, size_t count)
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;
155 ret = kstrtoul(buff, 10, &enable);
156 if (ret < 0)
157 return ret;
159 enable = !!enable;
161 if (led_dat->sata == enable)
162 goto exit;
164 led_dat->sata = enable;
166 if (!led_get_brightness(led_cdev))
167 goto exit;
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);
174 exit:
175 return count;
178 static ssize_t ns2_led_sata_show(struct device *dev,
179 struct device_attribute *attr, char *buf)
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);
185 return sprintf(buf, "%d\n", led_dat->sata);
188 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
190 static struct attribute *ns2_led_attrs[] = {
191 &dev_attr_sata.attr,
192 NULL
194 ATTRIBUTE_GROUPS(ns2_led);
196 static int
197 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
198 const struct ns2_led *template)
200 int ret;
201 enum ns2_led_modes mode;
203 rwlock_init(&led_dat->rw_lock);
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;
221 ret = ns2_led_get_mode(led_dat, &mode);
222 if (ret < 0)
223 return ret;
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;
230 ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
231 if (ret < 0)
232 return ret;
234 return 0;
237 static void delete_ns2_led(struct ns2_led_data *led_dat)
239 led_classdev_unregister(&led_dat->cdev);
242 #ifdef CONFIG_OF_GPIO
244 * Translate OpenFirmware node properties into platform_data.
246 static int
247 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
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;
254 num_leds = of_get_child_count(np);
255 if (!num_leds)
256 return -ENODEV;
258 leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
259 GFP_KERNEL);
260 if (!leds)
261 return -ENOMEM;
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;
270 ret = of_property_read_string(child, "label", &string);
271 led->name = (ret == 0) ? string : child->name;
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;
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;
286 led->slow = gd;
288 ret = of_property_read_string(child, "linux,default-trigger",
289 &string);
290 if (ret == 0)
291 led->default_trigger = string;
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;
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;
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);
323 led->num_modes = num_modes;
324 led->modval = modval;
326 led++;
329 pdata->leds = leds;
330 pdata->num_leds = num_leds;
332 return 0;
334 err_node_put:
335 of_node_put(child);
336 return ret;
339 static const struct of_device_id of_ns2_leds_match[] = {
340 { .compatible = "lacie,ns2-leds", },
343 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
344 #endif /* CONFIG_OF_GPIO */
346 struct ns2_led_priv {
347 int num_leds;
348 struct ns2_led_data leds_data[];
351 static inline int sizeof_ns2_led_priv(int num_leds)
353 return sizeof(struct ns2_led_priv) +
354 (sizeof(struct ns2_led_data) * num_leds);
357 static int ns2_led_probe(struct platform_device *pdev)
359 struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
360 struct ns2_led_priv *priv;
361 int i;
362 int ret;
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;
372 ret = ns2_leds_get_of_pdata(&pdev->dev, pdata);
373 if (ret)
374 return ret;
376 #else
377 if (!pdata)
378 return -EINVAL;
379 #endif /* CONFIG_OF_GPIO */
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;
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;
397 platform_set_drvdata(pdev, priv);
399 return 0;
402 static int ns2_led_remove(struct platform_device *pdev)
404 int i;
405 struct ns2_led_priv *priv;
407 priv = platform_get_drvdata(pdev);
409 for (i = 0; i < priv->num_leds; i++)
410 delete_ns2_led(&priv->leds_data[i]);
412 return 0;
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),
424 module_platform_driver(ns2_led_driver);
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");