leds: ns2: cosmetic structure rename
[linux-block.git] / drivers / leds / leds-ns2.c
CommitLineData
1a59d1b8 1// SPDX-License-Identifier: GPL-2.0-or-later
11efe71f
SG
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>
11efe71f
SG
10 */
11
12#include <linux/kernel.h>
11efe71f
SG
13#include <linux/platform_device.h>
14#include <linux/slab.h>
c7896490 15#include <linux/gpio/consumer.h>
11efe71f 16#include <linux/leds.h>
54f4dedb 17#include <linux/module.h>
c68f46dd 18#include <linux/of.h>
4b90432d 19#include "leds.h"
11efe71f 20
c7896490
LW
21enum ns2_led_modes {
22 NS_V2_LED_OFF,
23 NS_V2_LED_ON,
24 NS_V2_LED_SATA,
25};
26
27struct ns2_led_modval {
28 enum ns2_led_modes mode;
29 int cmd_level;
30 int slow_level;
31};
32
01026cec 33struct ns2_led_of_one {
c7896490
LW
34 const char *name;
35 const char *default_trigger;
ccbbb117
LW
36 struct gpio_desc *cmd;
37 struct gpio_desc *slow;
c7896490
LW
38 int num_modes;
39 struct ns2_led_modval *modval;
40};
41
01d0b14d 42struct ns2_led_of {
01026cec
MB
43 int num_leds;
44 struct ns2_led_of_one *leds;
c7896490
LW
45};
46
11efe71f 47/*
f7fafd08
VD
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.
11efe71f
SG
52 */
53
01026cec 54struct ns2_led {
11efe71f 55 struct led_classdev cdev;
ccbbb117
LW
56 struct gpio_desc *cmd;
57 struct gpio_desc *slow;
4b90432d 58 bool can_sleep;
11efe71f
SG
59 unsigned char sata; /* True when SATA mode active. */
60 rwlock_t rw_lock; /* Lock GPIOs. */
f7fafd08
VD
61 int num_modes;
62 struct ns2_led_modval *modval;
11efe71f
SG
63};
64
01026cec 65static int ns2_led_get_mode(struct ns2_led *led_dat,
11efe71f
SG
66 enum ns2_led_modes *mode)
67{
68 int i;
69 int ret = -EINVAL;
70 int cmd_level;
71 int slow_level;
72
ccbbb117
LW
73 cmd_level = gpiod_get_value_cansleep(led_dat->cmd);
74 slow_level = gpiod_get_value_cansleep(led_dat->slow);
11efe71f 75
f7fafd08
VD
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;
11efe71f
SG
80 ret = 0;
81 break;
82 }
83 }
84
11efe71f
SG
85 return ret;
86}
87
01026cec 88static void ns2_led_set_mode(struct ns2_led *led_dat,
11efe71f
SG
89 enum ns2_led_modes mode)
90{
91 int i;
4b90432d 92 bool found = false;
f539dfed 93 unsigned long flags;
11efe71f 94
4b90432d 95 for (i = 0; i < led_dat->num_modes; i++)
f7fafd08 96 if (mode == led_dat->modval[i].mode) {
4b90432d
SG
97 found = true;
98 break;
11efe71f 99 }
4b90432d
SG
100
101 if (!found)
102 return;
103
104 write_lock_irqsave(&led_dat->rw_lock, flags);
105
106 if (!led_dat->can_sleep) {
ccbbb117
LW
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);
4b90432d 111 goto exit_unlock;
11efe71f
SG
112 }
113
ccbbb117
LW
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);
4b90432d
SG
116
117exit_unlock:
f539dfed 118 write_unlock_irqrestore(&led_dat->rw_lock, flags);
11efe71f
SG
119}
120
121static void ns2_led_set(struct led_classdev *led_cdev,
122 enum led_brightness value)
123{
01026cec
MB
124 struct ns2_led *led_dat =
125 container_of(led_cdev, struct ns2_led, cdev);
11efe71f
SG
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
c29e650b
JA
138static 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
11efe71f
SG
145static ssize_t ns2_led_sata_store(struct device *dev,
146 struct device_attribute *attr,
147 const char *buff, size_t count)
148{
e5971bbc 149 struct led_classdev *led_cdev = dev_get_drvdata(dev);
01026cec
MB
150 struct ns2_led *led_dat =
151 container_of(led_cdev, struct ns2_led, cdev);
11efe71f
SG
152 int ret;
153 unsigned long enable;
11efe71f 154
3874350c 155 ret = kstrtoul(buff, 10, &enable);
11efe71f
SG
156 if (ret < 0)
157 return ret;
158
159 enable = !!enable;
160
161 if (led_dat->sata == enable)
4b90432d 162 goto exit;
11efe71f 163
4b90432d
SG
164 led_dat->sata = enable;
165
166 if (!led_get_brightness(led_cdev))
167 goto exit;
11efe71f 168
4b90432d 169 if (enable)
11efe71f 170 ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
4b90432d 171 else
11efe71f
SG
172 ns2_led_set_mode(led_dat, NS_V2_LED_ON);
173
4b90432d 174exit:
11efe71f
SG
175 return count;
176}
177
178static ssize_t ns2_led_sata_show(struct device *dev,
179 struct device_attribute *attr, char *buf)
180{
e5971bbc 181 struct led_classdev *led_cdev = dev_get_drvdata(dev);
01026cec
MB
182 struct ns2_led *led_dat =
183 container_of(led_cdev, struct ns2_led, cdev);
11efe71f
SG
184
185 return sprintf(buf, "%d\n", led_dat->sata);
186}
187
188static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
189
475f8548
JH
190static struct attribute *ns2_led_attrs[] = {
191 &dev_attr_sata.attr,
192 NULL
193};
194ATTRIBUTE_GROUPS(ns2_led);
195
98ea1ea2 196static int
01026cec
MB
197create_ns2_led(struct platform_device *pdev, struct ns2_led *led_dat,
198 const struct ns2_led_of_one *template)
11efe71f
SG
199{
200 int ret;
201 enum ns2_led_modes mode;
202
11efe71f
SG
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;
11efe71f 208 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
475f8548 209 led_dat->cdev.groups = ns2_led_groups;
11efe71f
SG
210 led_dat->cmd = template->cmd;
211 led_dat->slow = template->slow;
ccbbb117
LW
212 led_dat->can_sleep = gpiod_cansleep(led_dat->cmd) |
213 gpiod_cansleep(led_dat->slow);
c29e650b
JA
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;
f7fafd08
VD
218 led_dat->modval = template->modval;
219 led_dat->num_modes = template->num_modes;
11efe71f
SG
220
221 ret = ns2_led_get_mode(led_dat, &mode);
222 if (ret < 0)
04195823 223 return ret;
11efe71f
SG
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
40f97281 230 return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
11efe71f
SG
231}
232
f72deb71 233static int ns2_leds_parse_one(struct device *dev, struct device_node *np,
01026cec 234 struct ns2_led_of_one *led)
f72deb71
MB
235{
236 struct ns2_led_modval *modval;
237 int nmodes, ret, i;
238
239 ret = of_property_read_string(np, "label", &led->name);
240 if (ret)
241 led->name = np->name;
242
528c9515
MB
243 led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0,
244 GPIOD_ASIS, led->name);
f72deb71
MB
245 if (IS_ERR(led->cmd))
246 return PTR_ERR(led->cmd);
247
528c9515
MB
248 led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0,
249 GPIOD_ASIS, led->name);
f72deb71
MB
250 if (IS_ERR(led->slow))
251 return PTR_ERR(led->slow);
252
253 of_property_read_string(np, "linux,default-trigger",
254 &led->default_trigger);
255
256 ret = of_property_count_u32_elems(np, "modes-map");
257 if (ret < 0 || ret % 3) {
258 dev_err(dev, "Missing or malformed modes-map for %pOF\n", np);
259 return -EINVAL;
260 }
261
262 nmodes = ret / 3;
263 modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
264 if (!modval)
265 return -ENOMEM;
266
267 for (i = 0; i < nmodes; i++) {
268 u32 val;
269
270 of_property_read_u32_index(np, "modes-map", 3 * i, &val);
271 modval[i].mode = val;
272 of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val);
273 modval[i].cmd_level = val;
274 of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val);
275 modval[i].slow_level = val;
276 }
277
278 led->num_modes = nmodes;
279 led->modval = modval;
280
281 return 0;
282}
283
72052fcc
SG
284/*
285 * Translate OpenFirmware node properties into platform_data.
286 */
cf4af012 287static int
01d0b14d 288ns2_leds_parse_of(struct device *dev, struct ns2_led_of *ofdata)
72052fcc 289{
8853c95e 290 struct device_node *np = dev_of_node(dev);
72052fcc 291 struct device_node *child;
01026cec 292 struct ns2_led_of_one *led, *leds;
79937a4b 293 int ret, num_leds = 0;
72052fcc 294
99a013c8 295 num_leds = of_get_available_child_count(np);
72052fcc
SG
296 if (!num_leds)
297 return -ENODEV;
298
a86854d0 299 leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
72052fcc
SG
300 GFP_KERNEL);
301 if (!leds)
302 return -ENOMEM;
303
f7fafd08 304 led = leds;
99a013c8 305 for_each_available_child_of_node(np, child) {
f72deb71
MB
306 ret = ns2_leds_parse_one(dev, child, led++);
307 if (ret < 0) {
308 of_node_put(child);
309 return ret;
f7fafd08 310 }
72052fcc
SG
311 }
312
01d0b14d
MB
313 ofdata->leds = leds;
314 ofdata->num_leds = num_leds;
72052fcc
SG
315
316 return 0;
317}
318
319static const struct of_device_id of_ns2_leds_match[] = {
320 { .compatible = "lacie,ns2-leds", },
321 {},
322};
98f9cc7f 323MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
72052fcc 324
98ea1ea2 325static int ns2_led_probe(struct platform_device *pdev)
11efe71f 326{
01d0b14d 327 struct ns2_led_of *ofdata;
01026cec 328 struct ns2_led *leds;
11efe71f
SG
329 int i;
330 int ret;
331
01d0b14d
MB
332 ofdata = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_of),
333 GFP_KERNEL);
334 if (!ofdata)
335 return -ENOMEM;
72052fcc 336
01d0b14d
MB
337 ret = ns2_leds_parse_of(&pdev->dev, ofdata);
338 if (ret)
339 return ret;
11efe71f 340
19d4deb7 341 leds = devm_kzalloc(&pdev->dev, array_size(sizeof(*leds),
01d0b14d 342 ofdata->num_leds),
19d4deb7
MB
343 GFP_KERNEL);
344 if (!leds)
11efe71f
SG
345 return -ENOMEM;
346
01d0b14d
MB
347 for (i = 0; i < ofdata->num_leds; i++) {
348 ret = create_ns2_led(pdev, &leds[i], &ofdata->leds[i]);
40f97281 349 if (ret < 0)
a209f766 350 return ret;
11efe71f
SG
351 }
352
11efe71f 353 return 0;
11efe71f
SG
354}
355
11efe71f
SG
356static struct platform_driver ns2_led_driver = {
357 .probe = ns2_led_probe,
11efe71f 358 .driver = {
72052fcc 359 .name = "leds-ns2",
72052fcc 360 .of_match_table = of_match_ptr(of_ns2_leds_match),
11efe71f
SG
361 },
362};
11efe71f 363
892a8843 364module_platform_driver(ns2_led_driver);
11efe71f
SG
365
366MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
367MODULE_DESCRIPTION("Network Space v2 LED driver");
368MODULE_LICENSE("GPL");
892a8843 369MODULE_ALIAS("platform:leds-ns2");