libnvdimm/altmap: Track namespace boundaries in altmap
[linux-2.6-block.git] / drivers / leds / led-class.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
c72a1d60
RP
2/*
3 * LED Class Core
4 *
5 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
f8a7c6fe 6 * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
c72a1d60
RP
7 */
8
04713306
JA
9#include <linux/ctype.h>
10#include <linux/device.h>
11#include <linux/err.h>
c72a1d60 12#include <linux/init.h>
04713306
JA
13#include <linux/kernel.h>
14#include <linux/leds.h>
c72a1d60 15#include <linux/list.h>
04713306
JA
16#include <linux/module.h>
17#include <linux/slab.h>
c72a1d60 18#include <linux/spinlock.h>
9067359f 19#include <linux/timer.h>
eb1ce746 20#include <uapi/linux/uleds.h>
c72a1d60
RP
21#include "leds.h"
22
23static struct class *leds_class;
24
5baa7503 25static ssize_t brightness_show(struct device *dev,
f8a7c6fe 26 struct device_attribute *attr, char *buf)
c72a1d60 27{
f8a7c6fe 28 struct led_classdev *led_cdev = dev_get_drvdata(dev);
c72a1d60
RP
29
30 /* no lock needed for this */
29d76dfa 31 led_update_brightness(led_cdev);
c72a1d60 32
dd8e5a20 33 return sprintf(buf, "%u\n", led_cdev->brightness);
c72a1d60
RP
34}
35
5baa7503 36static ssize_t brightness_store(struct device *dev,
f8a7c6fe 37 struct device_attribute *attr, const char *buf, size_t size)
c72a1d60 38{
f8a7c6fe 39 struct led_classdev *led_cdev = dev_get_drvdata(dev);
872b86be 40 unsigned long state;
acd899e4
JA
41 ssize_t ret;
42
43 mutex_lock(&led_cdev->led_access);
44
45 if (led_sysfs_is_disabled(led_cdev)) {
46 ret = -EBUSY;
47 goto unlock;
48 }
c72a1d60 49
872b86be
SK
50 ret = kstrtoul(buf, 10, &state);
51 if (ret)
acd899e4 52 goto unlock;
3dc7b82e 53
872b86be
SK
54 if (state == LED_OFF)
55 led_trigger_remove(led_cdev);
4d71a4a1 56 led_set_brightness(led_cdev, state);
0db37915 57 flush_work(&led_cdev->set_brightness_work);
0013b23d 58
acd899e4
JA
59 ret = size;
60unlock:
61 mutex_unlock(&led_cdev->led_access);
62 return ret;
c72a1d60 63}
5baa7503 64static DEVICE_ATTR_RW(brightness);
c72a1d60 65
38419612 66static ssize_t max_brightness_show(struct device *dev,
1bd465e6
GL
67 struct device_attribute *attr, char *buf)
68{
69 struct led_classdev *led_cdev = dev_get_drvdata(dev);
70
71 return sprintf(buf, "%u\n", led_cdev->max_brightness);
72}
38419612 73static DEVICE_ATTR_RO(max_brightness);
1bd465e6 74
c3bc9956 75#ifdef CONFIG_LEDS_TRIGGERS
5baa7503
GKH
76static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
77static struct attribute *led_trigger_attrs[] = {
78 &dev_attr_trigger.attr,
79 NULL,
80};
81static const struct attribute_group led_trigger_group = {
82 .attrs = led_trigger_attrs,
83};
84#endif
85
86static struct attribute *led_class_attrs[] = {
87 &dev_attr_brightness.attr,
88 &dev_attr_max_brightness.attr,
89 NULL,
90};
91
92static const struct attribute_group led_group = {
93 .attrs = led_class_attrs,
94};
95
96static const struct attribute_group *led_groups[] = {
97 &led_group,
98#ifdef CONFIG_LEDS_TRIGGERS
99 &led_trigger_group,
c3bc9956 100#endif
5baa7503 101 NULL,
14b5d6dd 102};
c72a1d60 103
0cb8eb30
HG
104#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
105static ssize_t brightness_hw_changed_show(struct device *dev,
106 struct device_attribute *attr, char *buf)
107{
108 struct led_classdev *led_cdev = dev_get_drvdata(dev);
109
110 if (led_cdev->brightness_hw_changed == -1)
111 return -ENODATA;
112
113 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
114}
115
116static DEVICE_ATTR_RO(brightness_hw_changed);
117
118static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
119{
120 struct device *dev = led_cdev->dev;
121 int ret;
122
123 ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
124 if (ret) {
125 dev_err(dev, "Error creating brightness_hw_changed\n");
126 return ret;
127 }
128
129 led_cdev->brightness_hw_changed_kn =
130 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
131 if (!led_cdev->brightness_hw_changed_kn) {
132 dev_err(dev, "Error getting brightness_hw_changed kn\n");
133 device_remove_file(dev, &dev_attr_brightness_hw_changed);
134 return -ENXIO;
135 }
136
137 return 0;
138}
139
140static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
141{
142 sysfs_put(led_cdev->brightness_hw_changed_kn);
143 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
144}
145
146void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
147 enum led_brightness brightness)
148{
149 if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
150 return;
151
152 led_cdev->brightness_hw_changed = brightness;
153 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
154}
155EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
156#else
157static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
158{
159 return 0;
160}
161static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
162{
163}
164#endif
165
c72a1d60
RP
166/**
167 * led_classdev_suspend - suspend an led_classdev.
168 * @led_cdev: the led_classdev to suspend.
169 */
170void led_classdev_suspend(struct led_classdev *led_cdev)
171{
172 led_cdev->flags |= LED_SUSPENDED;
81fe8e5b 173 led_set_brightness_nopm(led_cdev, 0);
c72a1d60
RP
174}
175EXPORT_SYMBOL_GPL(led_classdev_suspend);
176
177/**
178 * led_classdev_resume - resume an led_classdev.
179 * @led_cdev: the led_classdev to resume.
180 */
181void led_classdev_resume(struct led_classdev *led_cdev)
182{
81fe8e5b 183 led_set_brightness_nopm(led_cdev, led_cdev->brightness);
7aea8389
JA
184
185 if (led_cdev->flash_resume)
186 led_cdev->flash_resume(led_cdev);
187
c72a1d60
RP
188 led_cdev->flags &= ~LED_SUSPENDED;
189}
190EXPORT_SYMBOL_GPL(led_classdev_resume);
191
084609bf 192#ifdef CONFIG_PM_SLEEP
73e1ab41 193static int led_suspend(struct device *dev)
859cb7f2
RP
194{
195 struct led_classdev *led_cdev = dev_get_drvdata(dev);
196
197 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
198 led_classdev_suspend(led_cdev);
199
200 return 0;
201}
202
203static int led_resume(struct device *dev)
204{
205 struct led_classdev *led_cdev = dev_get_drvdata(dev);
206
207 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
208 led_classdev_resume(led_cdev);
209
210 return 0;
211}
084609bf 212#endif
859cb7f2 213
084609bf 214static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
73e1ab41 215
a96aa64c
RRD
216static int match_name(struct device *dev, const void *data)
217{
218 if (!dev_name(dev))
219 return 0;
220 return !strcmp(dev_name(dev), (char *)data);
221}
222
223static int led_classdev_next_name(const char *init_name, char *name,
224 size_t len)
225{
226 unsigned int i = 0;
227 int ret = 0;
e5b5a61f 228 struct device *dev;
a96aa64c
RRD
229
230 strlcpy(name, init_name, len);
231
e5b5a61f
RRD
232 while ((ret < len) &&
233 (dev = class_find_device(leds_class, NULL, name, match_name))) {
234 put_device(dev);
a96aa64c 235 ret = snprintf(name, len, "%s_%u", init_name, ++i);
e5b5a61f 236 }
a96aa64c
RRD
237
238 if (ret >= len)
239 return -ENOMEM;
240
241 return i;
242}
243
c72a1d60 244/**
442c6098
RM
245 * of_led_classdev_register - register a new object of led_classdev class.
246 *
247 * @parent: parent of LED device
c72a1d60 248 * @led_cdev: the led_classdev structure for this device.
442c6098 249 * @np: DT node describing this LED
c72a1d60 250 */
442c6098
RM
251int of_led_classdev_register(struct device *parent, struct device_node *np,
252 struct led_classdev *led_cdev)
c72a1d60 253{
eb1ce746 254 char name[LED_MAX_NAME_SIZE];
a96aa64c
RRD
255 int ret;
256
257 ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
258 if (ret < 0)
259 return ret;
260
6d71021a
LH
261 mutex_init(&led_cdev->led_access);
262 mutex_lock(&led_cdev->led_access);
d0d480cc 263 led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
ccdc4507 264 led_cdev, led_cdev->groups, "%s", name);
6d71021a
LH
265 if (IS_ERR(led_cdev->dev)) {
266 mutex_unlock(&led_cdev->led_access);
f8a7c6fe 267 return PTR_ERR(led_cdev->dev);
6d71021a 268 }
442c6098 269 led_cdev->dev->of_node = np;
c72a1d60 270
a96aa64c 271 if (ret)
6f06c7f8 272 dev_warn(parent, "Led %s renamed to %s due to name collision",
a96aa64c
RRD
273 led_cdev->name, dev_name(led_cdev->dev));
274
0cb8eb30
HG
275 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
276 ret = led_add_brightness_hw_changed(led_cdev);
277 if (ret) {
278 device_unregister(led_cdev->dev);
6d71021a 279 mutex_unlock(&led_cdev->led_access);
0cb8eb30
HG
280 return ret;
281 }
282 }
283
a9c6ce57 284 led_cdev->work_flags = 0;
270c3957
RP
285#ifdef CONFIG_LEDS_TRIGGERS
286 init_rwsem(&led_cdev->trigger_lock);
0cb8eb30
HG
287#endif
288#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
289 led_cdev->brightness_hw_changed = -1;
270c3957 290#endif
c72a1d60 291 /* add to the list of leds */
72f8da32 292 down_write(&leds_list_lock);
c72a1d60 293 list_add_tail(&led_cdev->node, &leds_list);
72f8da32 294 up_write(&leds_list_lock);
c72a1d60 295
1bd465e6
GL
296 if (!led_cdev->max_brightness)
297 led_cdev->max_brightness = LED_FULL;
298
29d76dfa
HMH
299 led_update_brightness(led_cdev);
300
757b06ae 301 led_init_core(led_cdev);
5ada28bf 302
c3bc9956 303#ifdef CONFIG_LEDS_TRIGGERS
12fda168 304 led_trigger_set_default(led_cdev);
c3bc9956
RP
305#endif
306
6d71021a
LH
307 mutex_unlock(&led_cdev->led_access);
308
d23e7b8b 309 dev_dbg(parent, "Registered led device: %s\n",
f8a7c6fe 310 led_cdev->name);
c72a1d60
RP
311
312 return 0;
313}
442c6098 314EXPORT_SYMBOL_GPL(of_led_classdev_register);
c72a1d60
RP
315
316/**
0266a458 317 * led_classdev_unregister - unregisters a object of led_properties class.
70d63ccc 318 * @led_cdev: the led device to unregister
c72a1d60
RP
319 *
320 * Unregisters a previously registered via led_classdev_register object.
321 */
b844eba2 322void led_classdev_unregister(struct led_classdev *led_cdev)
c72a1d60 323{
c3bc9956 324#ifdef CONFIG_LEDS_TRIGGERS
dc47206e 325 down_write(&led_cdev->trigger_lock);
c3bc9956
RP
326 if (led_cdev->trigger)
327 led_trigger_set(led_cdev, NULL);
dc47206e 328 up_write(&led_cdev->trigger_lock);
c3bc9956 329#endif
c72a1d60 330
d84d80f3
HK
331 led_cdev->flags |= LED_UNREGISTERING;
332
5ada28bf 333 /* Stop blinking */
d23a22a7 334 led_stop_software_blink(led_cdev);
d1aa577f 335
19cd67e2 336 led_set_brightness(led_cdev, LED_OFF);
5ada28bf 337
d1aa577f
MK
338 flush_work(&led_cdev->set_brightness_work);
339
0cb8eb30
HG
340 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
341 led_remove_brightness_hw_changed(led_cdev);
342
b844eba2 343 device_unregister(led_cdev->dev);
c72a1d60 344
72f8da32 345 down_write(&leds_list_lock);
c72a1d60 346 list_del(&led_cdev->node);
72f8da32 347 up_write(&leds_list_lock);
acd899e4
JA
348
349 mutex_destroy(&led_cdev->led_access);
c72a1d60 350}
b844eba2 351EXPORT_SYMBOL_GPL(led_classdev_unregister);
c72a1d60 352
ca1bb4ee
BA
353static void devm_led_classdev_release(struct device *dev, void *res)
354{
355 led_classdev_unregister(*(struct led_classdev **)res);
356}
357
358/**
442c6098
RM
359 * devm_of_led_classdev_register - resource managed led_classdev_register()
360 *
361 * @parent: parent of LED device
ca1bb4ee
BA
362 * @led_cdev: the led_classdev structure for this device.
363 */
442c6098
RM
364int devm_of_led_classdev_register(struct device *parent,
365 struct device_node *np,
366 struct led_classdev *led_cdev)
ca1bb4ee
BA
367{
368 struct led_classdev **dr;
369 int rc;
370
371 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
372 if (!dr)
373 return -ENOMEM;
374
442c6098 375 rc = of_led_classdev_register(parent, np, led_cdev);
ca1bb4ee
BA
376 if (rc) {
377 devres_free(dr);
378 return rc;
379 }
380
381 *dr = led_cdev;
382 devres_add(parent, dr);
383
384 return 0;
385}
442c6098 386EXPORT_SYMBOL_GPL(devm_of_led_classdev_register);
ca1bb4ee
BA
387
388static int devm_led_classdev_match(struct device *dev, void *res, void *data)
389{
390 struct led_cdev **p = res;
391
392 if (WARN_ON(!p || !*p))
393 return 0;
394
395 return *p == data;
396}
397
398/**
399 * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
400 * @parent: The device to unregister.
401 * @led_cdev: the led_classdev structure for this device.
402 */
403void devm_led_classdev_unregister(struct device *dev,
404 struct led_classdev *led_cdev)
405{
406 WARN_ON(devres_release(dev,
407 devm_led_classdev_release,
408 devm_led_classdev_match, led_cdev));
409}
410EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
411
c72a1d60
RP
412static int __init leds_init(void)
413{
414 leds_class = class_create(THIS_MODULE, "leds");
415 if (IS_ERR(leds_class))
416 return PTR_ERR(leds_class);
73e1ab41 417 leds_class->pm = &leds_class_dev_pm_ops;
5baa7503 418 leds_class->dev_groups = led_groups;
c72a1d60
RP
419 return 0;
420}
421
422static void __exit leds_exit(void)
423{
424 class_destroy(leds_class);
425}
426
427subsys_initcall(leds_init);
428module_exit(leds_exit);
429
430MODULE_AUTHOR("John Lenz, Richard Purdie");
431MODULE_LICENSE("GPL");
432MODULE_DESCRIPTION("LED Class Interface");