1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c72a1d60SRichard Purdie /*
3c72a1d60SRichard Purdie * LED Class Core
4c72a1d60SRichard Purdie *
5c72a1d60SRichard Purdie * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
6f8a7c6feSRichard Purdie * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
7c72a1d60SRichard Purdie */
8c72a1d60SRichard Purdie
93dc7b82eSRichard Purdie #include <linux/ctype.h>
1004713306SJacek Anaszewski #include <linux/device.h>
1104713306SJacek Anaszewski #include <linux/err.h>
1204713306SJacek Anaszewski #include <linux/init.h>
1304713306SJacek Anaszewski #include <linux/kernel.h>
14c72a1d60SRichard Purdie #include <linux/leds.h>
1504713306SJacek Anaszewski #include <linux/list.h>
1604713306SJacek Anaszewski #include <linux/module.h>
17fd81d7e9SAndy Shevchenko #include <linux/property.h>
1804713306SJacek Anaszewski #include <linux/slab.h>
1904713306SJacek Anaszewski #include <linux/spinlock.h>
2004713306SJacek Anaszewski #include <linux/timer.h>
21eb1ce746SDavid Lechner #include <uapi/linux/uleds.h>
22699a8c7cSTomi Valkeinen #include <linux/of.h>
23c72a1d60SRichard Purdie #include "leds.h"
24c72a1d60SRichard Purdie
25abc3100fSHans de Goede static DEFINE_MUTEX(leds_lookup_lock);
26abc3100fSHans de Goede static LIST_HEAD(leds_lookup_list);
27c72a1d60SRichard Purdie
brightness_show(struct device * dev,struct device_attribute * attr,char * buf)285baa7503SGreg Kroah-Hartman static ssize_t brightness_show(struct device *dev,
29f8a7c6feSRichard Purdie struct device_attribute *attr, char *buf)
30c72a1d60SRichard Purdie {
31f8a7c6feSRichard Purdie struct led_classdev *led_cdev = dev_get_drvdata(dev);
32*f6d6fb56SMukesh Ojha unsigned int brightness;
33c72a1d60SRichard Purdie
34*f6d6fb56SMukesh Ojha mutex_lock(&led_cdev->led_access);
3529d76dfaSHenrique de Moraes Holschuh led_update_brightness(led_cdev);
36*f6d6fb56SMukesh Ojha brightness = led_cdev->brightness;
37*f6d6fb56SMukesh Ojha mutex_unlock(&led_cdev->led_access);
38c72a1d60SRichard Purdie
39*f6d6fb56SMukesh Ojha return sprintf(buf, "%u\n", brightness);
40c72a1d60SRichard Purdie }
41c72a1d60SRichard Purdie
brightness_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)425baa7503SGreg Kroah-Hartman static ssize_t brightness_store(struct device *dev,
43f8a7c6feSRichard Purdie struct device_attribute *attr, const char *buf, size_t size)
44c72a1d60SRichard Purdie {
45f8a7c6feSRichard Purdie struct led_classdev *led_cdev = dev_get_drvdata(dev);
46872b86beSShuah Khan unsigned long state;
47acd899e4SJacek Anaszewski ssize_t ret;
48acd899e4SJacek Anaszewski
49acd899e4SJacek Anaszewski mutex_lock(&led_cdev->led_access);
50acd899e4SJacek Anaszewski
51acd899e4SJacek Anaszewski if (led_sysfs_is_disabled(led_cdev)) {
52acd899e4SJacek Anaszewski ret = -EBUSY;
53acd899e4SJacek Anaszewski goto unlock;
54acd899e4SJacek Anaszewski }
55c72a1d60SRichard Purdie
56872b86beSShuah Khan ret = kstrtoul(buf, 10, &state);
57872b86beSShuah Khan if (ret)
58acd899e4SJacek Anaszewski goto unlock;
590013b23dSNémeth Márton
600013b23dSNémeth Márton if (state == LED_OFF)
610013b23dSNémeth Márton led_trigger_remove(led_cdev);
627ae6f137SEddie James /* flush out any request to disable blinking */
637ae6f137SEddie James flush_work(&led_cdev->set_brightness_work);
644d71a4a1SJacek Anaszewski led_set_brightness(led_cdev, state);
650db37915SPavel Machek flush_work(&led_cdev->set_brightness_work);
66c72a1d60SRichard Purdie
67acd899e4SJacek Anaszewski ret = size;
68acd899e4SJacek Anaszewski unlock:
69acd899e4SJacek Anaszewski mutex_unlock(&led_cdev->led_access);
70acd899e4SJacek Anaszewski return ret;
71c72a1d60SRichard Purdie }
725baa7503SGreg Kroah-Hartman static DEVICE_ATTR_RW(brightness);
73c72a1d60SRichard Purdie
max_brightness_show(struct device * dev,struct device_attribute * attr,char * buf)7438419612SJacek Anaszewski static ssize_t max_brightness_show(struct device *dev,
751bd465e6SGuennadi Liakhovetski struct device_attribute *attr, char *buf)
761bd465e6SGuennadi Liakhovetski {
771bd465e6SGuennadi Liakhovetski struct led_classdev *led_cdev = dev_get_drvdata(dev);
78*f6d6fb56SMukesh Ojha unsigned int max_brightness;
791bd465e6SGuennadi Liakhovetski
80*f6d6fb56SMukesh Ojha mutex_lock(&led_cdev->led_access);
81*f6d6fb56SMukesh Ojha max_brightness = led_cdev->max_brightness;
82*f6d6fb56SMukesh Ojha mutex_unlock(&led_cdev->led_access);
83*f6d6fb56SMukesh Ojha
84*f6d6fb56SMukesh Ojha return sprintf(buf, "%u\n", max_brightness);
851bd465e6SGuennadi Liakhovetski }
8638419612SJacek Anaszewski static DEVICE_ATTR_RO(max_brightness);
871bd465e6SGuennadi Liakhovetski
88c3bc9956SRichard Purdie #ifdef CONFIG_LEDS_TRIGGERS
8911f70002SAkinobu Mita static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
9011f70002SAkinobu Mita static struct bin_attribute *led_trigger_bin_attrs[] = {
9111f70002SAkinobu Mita &bin_attr_trigger,
925baa7503SGreg Kroah-Hartman NULL,
935baa7503SGreg Kroah-Hartman };
945baa7503SGreg Kroah-Hartman static const struct attribute_group led_trigger_group = {
9511f70002SAkinobu Mita .bin_attrs = led_trigger_bin_attrs,
965baa7503SGreg Kroah-Hartman };
97c3bc9956SRichard Purdie #endif
985baa7503SGreg Kroah-Hartman
995baa7503SGreg Kroah-Hartman static struct attribute *led_class_attrs[] = {
1005baa7503SGreg Kroah-Hartman &dev_attr_brightness.attr,
1015baa7503SGreg Kroah-Hartman &dev_attr_max_brightness.attr,
1025baa7503SGreg Kroah-Hartman NULL,
1035baa7503SGreg Kroah-Hartman };
1045baa7503SGreg Kroah-Hartman
1055baa7503SGreg Kroah-Hartman static const struct attribute_group led_group = {
1065baa7503SGreg Kroah-Hartman .attrs = led_class_attrs,
1075baa7503SGreg Kroah-Hartman };
1085baa7503SGreg Kroah-Hartman
1095baa7503SGreg Kroah-Hartman static const struct attribute_group *led_groups[] = {
1105baa7503SGreg Kroah-Hartman &led_group,
1115baa7503SGreg Kroah-Hartman #ifdef CONFIG_LEDS_TRIGGERS
1125baa7503SGreg Kroah-Hartman &led_trigger_group,
1135baa7503SGreg Kroah-Hartman #endif
1145baa7503SGreg Kroah-Hartman NULL,
11514b5d6ddSFlorian Fainelli };
116c72a1d60SRichard Purdie
1170cb8eb30SHans de Goede #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
brightness_hw_changed_show(struct device * dev,struct device_attribute * attr,char * buf)1180cb8eb30SHans de Goede static ssize_t brightness_hw_changed_show(struct device *dev,
1190cb8eb30SHans de Goede struct device_attribute *attr, char *buf)
1200cb8eb30SHans de Goede {
1210cb8eb30SHans de Goede struct led_classdev *led_cdev = dev_get_drvdata(dev);
1220cb8eb30SHans de Goede
1230cb8eb30SHans de Goede if (led_cdev->brightness_hw_changed == -1)
1240cb8eb30SHans de Goede return -ENODATA;
1250cb8eb30SHans de Goede
1260cb8eb30SHans de Goede return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
1270cb8eb30SHans de Goede }
1280cb8eb30SHans de Goede
1290cb8eb30SHans de Goede static DEVICE_ATTR_RO(brightness_hw_changed);
1300cb8eb30SHans de Goede
led_add_brightness_hw_changed(struct led_classdev * led_cdev)1310cb8eb30SHans de Goede static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
1320cb8eb30SHans de Goede {
1330cb8eb30SHans de Goede struct device *dev = led_cdev->dev;
1340cb8eb30SHans de Goede int ret;
1350cb8eb30SHans de Goede
1360cb8eb30SHans de Goede ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
1370cb8eb30SHans de Goede if (ret) {
1380cb8eb30SHans de Goede dev_err(dev, "Error creating brightness_hw_changed\n");
1390cb8eb30SHans de Goede return ret;
1400cb8eb30SHans de Goede }
1410cb8eb30SHans de Goede
1420cb8eb30SHans de Goede led_cdev->brightness_hw_changed_kn =
1430cb8eb30SHans de Goede sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
1440cb8eb30SHans de Goede if (!led_cdev->brightness_hw_changed_kn) {
1450cb8eb30SHans de Goede dev_err(dev, "Error getting brightness_hw_changed kn\n");
1460cb8eb30SHans de Goede device_remove_file(dev, &dev_attr_brightness_hw_changed);
1470cb8eb30SHans de Goede return -ENXIO;
1480cb8eb30SHans de Goede }
1490cb8eb30SHans de Goede
1500cb8eb30SHans de Goede return 0;
1510cb8eb30SHans de Goede }
1520cb8eb30SHans de Goede
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)1530cb8eb30SHans de Goede static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
1540cb8eb30SHans de Goede {
1550cb8eb30SHans de Goede sysfs_put(led_cdev->brightness_hw_changed_kn);
1560cb8eb30SHans de Goede device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
1570cb8eb30SHans de Goede }
1580cb8eb30SHans de Goede
led_classdev_notify_brightness_hw_changed(struct led_classdev * led_cdev,unsigned int brightness)159af0bfab9SAbanoub Sameh void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev, unsigned int brightness)
1600cb8eb30SHans de Goede {
1610cb8eb30SHans de Goede if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
1620cb8eb30SHans de Goede return;
1630cb8eb30SHans de Goede
1640cb8eb30SHans de Goede led_cdev->brightness_hw_changed = brightness;
1650cb8eb30SHans de Goede sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
1660cb8eb30SHans de Goede }
1670cb8eb30SHans de Goede EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
1680cb8eb30SHans de Goede #else
led_add_brightness_hw_changed(struct led_classdev * led_cdev)1690cb8eb30SHans de Goede static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
1700cb8eb30SHans de Goede {
1710cb8eb30SHans de Goede return 0;
1720cb8eb30SHans de Goede }
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)1730cb8eb30SHans de Goede static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
1740cb8eb30SHans de Goede {
1750cb8eb30SHans de Goede }
1760cb8eb30SHans de Goede #endif
1770cb8eb30SHans de Goede
178c72a1d60SRichard Purdie /**
179c72a1d60SRichard Purdie * led_classdev_suspend - suspend an led_classdev.
180c72a1d60SRichard Purdie * @led_cdev: the led_classdev to suspend.
181c72a1d60SRichard Purdie */
led_classdev_suspend(struct led_classdev * led_cdev)182c72a1d60SRichard Purdie void led_classdev_suspend(struct led_classdev *led_cdev)
183c72a1d60SRichard Purdie {
184c72a1d60SRichard Purdie led_cdev->flags |= LED_SUSPENDED;
18581fe8e5bSJacek Anaszewski led_set_brightness_nopm(led_cdev, 0);
186302a085cSKai-Heng Feng flush_work(&led_cdev->set_brightness_work);
187c72a1d60SRichard Purdie }
188c72a1d60SRichard Purdie EXPORT_SYMBOL_GPL(led_classdev_suspend);
189c72a1d60SRichard Purdie
190c72a1d60SRichard Purdie /**
191c72a1d60SRichard Purdie * led_classdev_resume - resume an led_classdev.
192c72a1d60SRichard Purdie * @led_cdev: the led_classdev to resume.
193c72a1d60SRichard Purdie */
led_classdev_resume(struct led_classdev * led_cdev)194c72a1d60SRichard Purdie void led_classdev_resume(struct led_classdev *led_cdev)
195c72a1d60SRichard Purdie {
19681fe8e5bSJacek Anaszewski led_set_brightness_nopm(led_cdev, led_cdev->brightness);
1977aea8389SJacek Anaszewski
1987aea8389SJacek Anaszewski if (led_cdev->flash_resume)
1997aea8389SJacek Anaszewski led_cdev->flash_resume(led_cdev);
2007aea8389SJacek Anaszewski
201c72a1d60SRichard Purdie led_cdev->flags &= ~LED_SUSPENDED;
202c72a1d60SRichard Purdie }
203c72a1d60SRichard Purdie EXPORT_SYMBOL_GPL(led_classdev_resume);
204c72a1d60SRichard Purdie
205084609bfSGrygorii Strashko #ifdef CONFIG_PM_SLEEP
led_suspend(struct device * dev)20673e1ab41SShuah Khan static int led_suspend(struct device *dev)
207859cb7f2SRichard Purdie {
208859cb7f2SRichard Purdie struct led_classdev *led_cdev = dev_get_drvdata(dev);
209859cb7f2SRichard Purdie
210859cb7f2SRichard Purdie if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
211859cb7f2SRichard Purdie led_classdev_suspend(led_cdev);
212859cb7f2SRichard Purdie
213859cb7f2SRichard Purdie return 0;
214859cb7f2SRichard Purdie }
215859cb7f2SRichard Purdie
led_resume(struct device * dev)216859cb7f2SRichard Purdie static int led_resume(struct device *dev)
217859cb7f2SRichard Purdie {
218859cb7f2SRichard Purdie struct led_classdev *led_cdev = dev_get_drvdata(dev);
219859cb7f2SRichard Purdie
220859cb7f2SRichard Purdie if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
221859cb7f2SRichard Purdie led_classdev_resume(led_cdev);
222859cb7f2SRichard Purdie
223859cb7f2SRichard Purdie return 0;
224859cb7f2SRichard Purdie }
225084609bfSGrygorii Strashko #endif
226859cb7f2SRichard Purdie
227084609bfSGrygorii Strashko static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
22873e1ab41SShuah Khan
led_module_get(struct device * led_dev)229fafef58eSHans de Goede static struct led_classdev *led_module_get(struct device *led_dev)
230699a8c7cSTomi Valkeinen {
231699a8c7cSTomi Valkeinen struct led_classdev *led_cdev;
232699a8c7cSTomi Valkeinen
233699a8c7cSTomi Valkeinen if (!led_dev)
234699a8c7cSTomi Valkeinen return ERR_PTR(-EPROBE_DEFER);
235699a8c7cSTomi Valkeinen
236699a8c7cSTomi Valkeinen led_cdev = dev_get_drvdata(led_dev);
237699a8c7cSTomi Valkeinen
23844511094SHans de Goede if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
23944511094SHans de Goede put_device(led_cdev->dev);
240699a8c7cSTomi Valkeinen return ERR_PTR(-ENODEV);
24144511094SHans de Goede }
242699a8c7cSTomi Valkeinen
243699a8c7cSTomi Valkeinen return led_cdev;
244699a8c7cSTomi Valkeinen }
245fafef58eSHans de Goede
24643a707aeSIvan Orlov static const struct class leds_class = {
24743a707aeSIvan Orlov .name = "leds",
24843a707aeSIvan Orlov .dev_groups = led_groups,
24943a707aeSIvan Orlov .pm = &leds_class_dev_pm_ops,
25043a707aeSIvan Orlov };
25143a707aeSIvan Orlov
252fafef58eSHans de Goede /**
253fafef58eSHans de Goede * of_led_get() - request a LED device via the LED framework
254fafef58eSHans de Goede * @np: device node to get the LED device from
255fafef58eSHans de Goede * @index: the index of the LED
256fafef58eSHans de Goede *
257fafef58eSHans de Goede * Returns the LED device parsed from the phandle specified in the "leds"
258fafef58eSHans de Goede * property of a device tree node or a negative error-code on failure.
259fafef58eSHans de Goede */
of_led_get(struct device_node * np,int index)260fafef58eSHans de Goede struct led_classdev *of_led_get(struct device_node *np, int index)
261fafef58eSHans de Goede {
262fafef58eSHans de Goede struct device *led_dev;
263fafef58eSHans de Goede struct device_node *led_node;
264fafef58eSHans de Goede
265fafef58eSHans de Goede led_node = of_parse_phandle(np, "leds", index);
266fafef58eSHans de Goede if (!led_node)
267fafef58eSHans de Goede return ERR_PTR(-ENOENT);
268fafef58eSHans de Goede
26943a707aeSIvan Orlov led_dev = class_find_device_by_of_node(&leds_class, led_node);
270fafef58eSHans de Goede of_node_put(led_node);
271fafef58eSHans de Goede
272fafef58eSHans de Goede return led_module_get(led_dev);
273fafef58eSHans de Goede }
274699a8c7cSTomi Valkeinen EXPORT_SYMBOL_GPL(of_led_get);
275699a8c7cSTomi Valkeinen
276699a8c7cSTomi Valkeinen /**
277699a8c7cSTomi Valkeinen * led_put() - release a LED device
278699a8c7cSTomi Valkeinen * @led_cdev: LED device
279699a8c7cSTomi Valkeinen */
led_put(struct led_classdev * led_cdev)280699a8c7cSTomi Valkeinen void led_put(struct led_classdev *led_cdev)
281699a8c7cSTomi Valkeinen {
282699a8c7cSTomi Valkeinen module_put(led_cdev->dev->parent->driver->owner);
28344511094SHans de Goede put_device(led_cdev->dev);
284699a8c7cSTomi Valkeinen }
285699a8c7cSTomi Valkeinen EXPORT_SYMBOL_GPL(led_put);
286699a8c7cSTomi Valkeinen
devm_led_release(struct device * dev,void * res)287e389240aSJean-Jacques Hiblot static void devm_led_release(struct device *dev, void *res)
288e389240aSJean-Jacques Hiblot {
289e389240aSJean-Jacques Hiblot struct led_classdev **p = res;
290e389240aSJean-Jacques Hiblot
291e389240aSJean-Jacques Hiblot led_put(*p);
292e389240aSJean-Jacques Hiblot }
293e389240aSJean-Jacques Hiblot
__devm_led_get(struct device * dev,struct led_classdev * led)294537bdca2SHans de Goede static struct led_classdev *__devm_led_get(struct device *dev, struct led_classdev *led)
295537bdca2SHans de Goede {
296537bdca2SHans de Goede struct led_classdev **dr;
297537bdca2SHans de Goede
298537bdca2SHans de Goede dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), GFP_KERNEL);
299537bdca2SHans de Goede if (!dr) {
300537bdca2SHans de Goede led_put(led);
301537bdca2SHans de Goede return ERR_PTR(-ENOMEM);
302537bdca2SHans de Goede }
303537bdca2SHans de Goede
304537bdca2SHans de Goede *dr = led;
305537bdca2SHans de Goede devres_add(dev, dr);
306537bdca2SHans de Goede
307537bdca2SHans de Goede return led;
308537bdca2SHans de Goede }
309537bdca2SHans de Goede
310e389240aSJean-Jacques Hiblot /**
311e389240aSJean-Jacques Hiblot * devm_of_led_get - Resource-managed request of a LED device
312e389240aSJean-Jacques Hiblot * @dev: LED consumer
313e389240aSJean-Jacques Hiblot * @index: index of the LED to obtain in the consumer
314e389240aSJean-Jacques Hiblot *
315e389240aSJean-Jacques Hiblot * The device node of the device is parse to find the request LED device.
316e389240aSJean-Jacques Hiblot * The LED device returned from this function is automatically released
317e389240aSJean-Jacques Hiblot * on driver detach.
318e389240aSJean-Jacques Hiblot *
319e389240aSJean-Jacques Hiblot * @return a pointer to a LED device or ERR_PTR(errno) on failure.
320e389240aSJean-Jacques Hiblot */
devm_of_led_get(struct device * dev,int index)321e389240aSJean-Jacques Hiblot struct led_classdev *__must_check devm_of_led_get(struct device *dev,
322e389240aSJean-Jacques Hiblot int index)
323e389240aSJean-Jacques Hiblot {
324e389240aSJean-Jacques Hiblot struct led_classdev *led;
325e389240aSJean-Jacques Hiblot
326e389240aSJean-Jacques Hiblot if (!dev)
327e389240aSJean-Jacques Hiblot return ERR_PTR(-EINVAL);
328e389240aSJean-Jacques Hiblot
329e389240aSJean-Jacques Hiblot led = of_led_get(dev->of_node, index);
330e389240aSJean-Jacques Hiblot if (IS_ERR(led))
331e389240aSJean-Jacques Hiblot return led;
332e389240aSJean-Jacques Hiblot
333537bdca2SHans de Goede return __devm_led_get(dev, led);
334e389240aSJean-Jacques Hiblot }
335e389240aSJean-Jacques Hiblot EXPORT_SYMBOL_GPL(devm_of_led_get);
336e389240aSJean-Jacques Hiblot
337abc3100fSHans de Goede /**
338abc3100fSHans de Goede * led_get() - request a LED device via the LED framework
339abc3100fSHans de Goede * @dev: device for which to get the LED device
340abc3100fSHans de Goede * @con_id: name of the LED from the device's point of view
341abc3100fSHans de Goede *
342abc3100fSHans de Goede * @return a pointer to a LED device or ERR_PTR(errno) on failure.
343abc3100fSHans de Goede */
led_get(struct device * dev,char * con_id)344abc3100fSHans de Goede struct led_classdev *led_get(struct device *dev, char *con_id)
345abc3100fSHans de Goede {
346abc3100fSHans de Goede struct led_lookup_data *lookup;
347abc3100fSHans de Goede const char *provider = NULL;
348abc3100fSHans de Goede struct device *led_dev;
349abc3100fSHans de Goede
350abc3100fSHans de Goede mutex_lock(&leds_lookup_lock);
351abc3100fSHans de Goede list_for_each_entry(lookup, &leds_lookup_list, list) {
352abc3100fSHans de Goede if (!strcmp(lookup->dev_id, dev_name(dev)) &&
353abc3100fSHans de Goede !strcmp(lookup->con_id, con_id)) {
354abc3100fSHans de Goede provider = kstrdup_const(lookup->provider, GFP_KERNEL);
355abc3100fSHans de Goede break;
356abc3100fSHans de Goede }
357abc3100fSHans de Goede }
358abc3100fSHans de Goede mutex_unlock(&leds_lookup_lock);
359abc3100fSHans de Goede
360abc3100fSHans de Goede if (!provider)
361abc3100fSHans de Goede return ERR_PTR(-ENOENT);
362abc3100fSHans de Goede
36343a707aeSIvan Orlov led_dev = class_find_device_by_name(&leds_class, provider);
364abc3100fSHans de Goede kfree_const(provider);
365abc3100fSHans de Goede
366abc3100fSHans de Goede return led_module_get(led_dev);
367abc3100fSHans de Goede }
368abc3100fSHans de Goede EXPORT_SYMBOL_GPL(led_get);
369abc3100fSHans de Goede
370abc3100fSHans de Goede /**
371abc3100fSHans de Goede * devm_led_get() - request a LED device via the LED framework
372abc3100fSHans de Goede * @dev: device for which to get the LED device
373abc3100fSHans de Goede * @con_id: name of the LED from the device's point of view
374abc3100fSHans de Goede *
375abc3100fSHans de Goede * The LED device returned from this function is automatically released
376abc3100fSHans de Goede * on driver detach.
377abc3100fSHans de Goede *
378abc3100fSHans de Goede * @return a pointer to a LED device or ERR_PTR(errno) on failure.
379abc3100fSHans de Goede */
devm_led_get(struct device * dev,char * con_id)380abc3100fSHans de Goede struct led_classdev *devm_led_get(struct device *dev, char *con_id)
381abc3100fSHans de Goede {
382abc3100fSHans de Goede struct led_classdev *led;
383abc3100fSHans de Goede
384abc3100fSHans de Goede led = led_get(dev, con_id);
385abc3100fSHans de Goede if (IS_ERR(led))
386abc3100fSHans de Goede return led;
387abc3100fSHans de Goede
388abc3100fSHans de Goede return __devm_led_get(dev, led);
389abc3100fSHans de Goede }
390abc3100fSHans de Goede EXPORT_SYMBOL_GPL(devm_led_get);
391abc3100fSHans de Goede
392abc3100fSHans de Goede /**
393abc3100fSHans de Goede * led_add_lookup() - Add a LED lookup table entry
394abc3100fSHans de Goede * @led_lookup: the lookup table entry to add
395abc3100fSHans de Goede *
396abc3100fSHans de Goede * Add a LED lookup table entry. On systems without devicetree the lookup table
397abc3100fSHans de Goede * is used by led_get() to find LEDs.
398abc3100fSHans de Goede */
led_add_lookup(struct led_lookup_data * led_lookup)399abc3100fSHans de Goede void led_add_lookup(struct led_lookup_data *led_lookup)
400abc3100fSHans de Goede {
401abc3100fSHans de Goede mutex_lock(&leds_lookup_lock);
402abc3100fSHans de Goede list_add_tail(&led_lookup->list, &leds_lookup_list);
403abc3100fSHans de Goede mutex_unlock(&leds_lookup_lock);
404abc3100fSHans de Goede }
405abc3100fSHans de Goede EXPORT_SYMBOL_GPL(led_add_lookup);
406abc3100fSHans de Goede
407abc3100fSHans de Goede /**
408abc3100fSHans de Goede * led_remove_lookup() - Remove a LED lookup table entry
409abc3100fSHans de Goede * @led_lookup: the lookup table entry to remove
410abc3100fSHans de Goede */
led_remove_lookup(struct led_lookup_data * led_lookup)411abc3100fSHans de Goede void led_remove_lookup(struct led_lookup_data *led_lookup)
412abc3100fSHans de Goede {
413abc3100fSHans de Goede mutex_lock(&leds_lookup_lock);
414abc3100fSHans de Goede list_del(&led_lookup->list);
415abc3100fSHans de Goede mutex_unlock(&leds_lookup_lock);
416abc3100fSHans de Goede }
417abc3100fSHans de Goede EXPORT_SYMBOL_GPL(led_remove_lookup);
418abc3100fSHans de Goede
419afb48153SJean-Jacques Hiblot /**
420afb48153SJean-Jacques Hiblot * devm_of_led_get_optional - Resource-managed request of an optional LED device
421afb48153SJean-Jacques Hiblot * @dev: LED consumer
422afb48153SJean-Jacques Hiblot * @index: index of the LED to obtain in the consumer
423afb48153SJean-Jacques Hiblot *
424afb48153SJean-Jacques Hiblot * The device node of the device is parsed to find the requested LED device.
425afb48153SJean-Jacques Hiblot * The LED device returned from this function is automatically released
426afb48153SJean-Jacques Hiblot * on driver detach.
427afb48153SJean-Jacques Hiblot *
428afb48153SJean-Jacques Hiblot * @return a pointer to a LED device, ERR_PTR(errno) on failure and NULL if the
429afb48153SJean-Jacques Hiblot * led was not found.
430afb48153SJean-Jacques Hiblot */
devm_of_led_get_optional(struct device * dev,int index)431afb48153SJean-Jacques Hiblot struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev,
432afb48153SJean-Jacques Hiblot int index)
433afb48153SJean-Jacques Hiblot {
434afb48153SJean-Jacques Hiblot struct led_classdev *led;
435afb48153SJean-Jacques Hiblot
436afb48153SJean-Jacques Hiblot led = devm_of_led_get(dev, index);
437afb48153SJean-Jacques Hiblot if (IS_ERR(led) && PTR_ERR(led) == -ENOENT)
438afb48153SJean-Jacques Hiblot return NULL;
439afb48153SJean-Jacques Hiblot
440afb48153SJean-Jacques Hiblot return led;
441afb48153SJean-Jacques Hiblot }
442afb48153SJean-Jacques Hiblot EXPORT_SYMBOL_GPL(devm_of_led_get_optional);
443afb48153SJean-Jacques Hiblot
led_classdev_next_name(const char * init_name,char * name,size_t len)444a96aa64cSRicardo Ribalda Delgado static int led_classdev_next_name(const char *init_name, char *name,
445a96aa64cSRicardo Ribalda Delgado size_t len)
446a96aa64cSRicardo Ribalda Delgado {
447a96aa64cSRicardo Ribalda Delgado unsigned int i = 0;
448a96aa64cSRicardo Ribalda Delgado int ret = 0;
449e5b5a61fSRicardo Ribalda Delgado struct device *dev;
450a96aa64cSRicardo Ribalda Delgado
451bf4a35e9SAzeem Shaikh strscpy(name, init_name, len);
452a96aa64cSRicardo Ribalda Delgado
453e5b5a61fSRicardo Ribalda Delgado while ((ret < len) &&
45443a707aeSIvan Orlov (dev = class_find_device_by_name(&leds_class, name))) {
455e5b5a61fSRicardo Ribalda Delgado put_device(dev);
456a96aa64cSRicardo Ribalda Delgado ret = snprintf(name, len, "%s_%u", init_name, ++i);
457e5b5a61fSRicardo Ribalda Delgado }
458a96aa64cSRicardo Ribalda Delgado
459a96aa64cSRicardo Ribalda Delgado if (ret >= len)
460a96aa64cSRicardo Ribalda Delgado return -ENOMEM;
461a96aa64cSRicardo Ribalda Delgado
462a96aa64cSRicardo Ribalda Delgado return i;
463a96aa64cSRicardo Ribalda Delgado }
464a96aa64cSRicardo Ribalda Delgado
465c72a1d60SRichard Purdie /**
466b2b998c0SJacek Anaszewski * led_classdev_register_ext - register a new object of led_classdev class
467b2b998c0SJacek Anaszewski * with init data.
468442c6098SRafał Miłecki *
469442c6098SRafał Miłecki * @parent: parent of LED device
470c72a1d60SRichard Purdie * @led_cdev: the led_classdev structure for this device.
471b2b998c0SJacek Anaszewski * @init_data: LED class device initialization data
472c72a1d60SRichard Purdie */
led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)473b2b998c0SJacek Anaszewski int led_classdev_register_ext(struct device *parent,
474b2b998c0SJacek Anaszewski struct led_classdev *led_cdev,
475b2b998c0SJacek Anaszewski struct led_init_data *init_data)
476c72a1d60SRichard Purdie {
477bb4e9af0SJacek Anaszewski char composed_name[LED_MAX_NAME_SIZE];
478bb4e9af0SJacek Anaszewski char final_name[LED_MAX_NAME_SIZE];
479bb4e9af0SJacek Anaszewski const char *proposed_name = composed_name;
480a96aa64cSRicardo Ribalda Delgado int ret;
481a96aa64cSRicardo Ribalda Delgado
482bb4e9af0SJacek Anaszewski if (init_data) {
483bb4e9af0SJacek Anaszewski if (init_data->devname_mandatory && !init_data->devicename) {
484bb4e9af0SJacek Anaszewski dev_err(parent, "Mandatory device name is missing");
485bb4e9af0SJacek Anaszewski return -EINVAL;
486bb4e9af0SJacek Anaszewski }
487bb4e9af0SJacek Anaszewski ret = led_compose_name(parent, init_data, composed_name);
488bb4e9af0SJacek Anaszewski if (ret < 0)
489bb4e9af0SJacek Anaszewski return ret;
490c49d6cabSMarek Behún
49141906632SEddie James if (init_data->fwnode) {
492c49d6cabSMarek Behún fwnode_property_read_string(init_data->fwnode,
493c49d6cabSMarek Behún "linux,default-trigger",
494c49d6cabSMarek Behún &led_cdev->default_trigger);
49541906632SEddie James
49641906632SEddie James if (fwnode_property_present(init_data->fwnode,
49741906632SEddie James "retain-state-shutdown"))
49841906632SEddie James led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
4997cd7a299SAstrid Rost
5007cd7a299SAstrid Rost fwnode_property_read_u32(init_data->fwnode,
5017cd7a299SAstrid Rost "max-brightness",
5027cd7a299SAstrid Rost &led_cdev->max_brightness);
503c7d80059SJean-Jacques Hiblot
504c7d80059SJean-Jacques Hiblot if (fwnode_property_present(init_data->fwnode, "color"))
505c7d80059SJean-Jacques Hiblot fwnode_property_read_u32(init_data->fwnode, "color",
506c7d80059SJean-Jacques Hiblot &led_cdev->color);
50741906632SEddie James }
508bb4e9af0SJacek Anaszewski } else {
509bb4e9af0SJacek Anaszewski proposed_name = led_cdev->name;
510bb4e9af0SJacek Anaszewski }
511bb4e9af0SJacek Anaszewski
512bb4e9af0SJacek Anaszewski ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
513a96aa64cSRicardo Ribalda Delgado if (ret < 0)
514a96aa64cSRicardo Ribalda Delgado return ret;
515a96aa64cSRicardo Ribalda Delgado
516c7d80059SJean-Jacques Hiblot if (led_cdev->color >= LED_COLOR_ID_MAX)
517c7d80059SJean-Jacques Hiblot dev_warn(parent, "LED %s color identifier out of range\n", final_name);
518c7d80059SJean-Jacques Hiblot
5196d71021aSLuis Henriques mutex_init(&led_cdev->led_access);
5206d71021aSLuis Henriques mutex_lock(&led_cdev->led_access);
52143a707aeSIvan Orlov led_cdev->dev = device_create_with_groups(&leds_class, parent, 0,
522bb4e9af0SJacek Anaszewski led_cdev, led_cdev->groups, "%s", final_name);
5236d71021aSLuis Henriques if (IS_ERR(led_cdev->dev)) {
5246d71021aSLuis Henriques mutex_unlock(&led_cdev->led_access);
525f8a7c6feSRichard Purdie return PTR_ERR(led_cdev->dev);
5266d71021aSLuis Henriques }
527495b8966SSander Vanheule if (init_data && init_data->fwnode)
528495b8966SSander Vanheule device_set_node(led_cdev->dev, init_data->fwnode);
529c72a1d60SRichard Purdie
530a96aa64cSRicardo Ribalda Delgado if (ret)
5316f06c7f8SSakari Ailus dev_warn(parent, "Led %s renamed to %s due to name collision",
53264ed6588SRicardo Ribalda Delgado proposed_name, dev_name(led_cdev->dev));
533a96aa64cSRicardo Ribalda Delgado
5340cb8eb30SHans de Goede if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
5350cb8eb30SHans de Goede ret = led_add_brightness_hw_changed(led_cdev);
5360cb8eb30SHans de Goede if (ret) {
5370cb8eb30SHans de Goede device_unregister(led_cdev->dev);
5381dbb9fb4SAndy Shevchenko led_cdev->dev = NULL;
5396d71021aSLuis Henriques mutex_unlock(&led_cdev->led_access);
5400cb8eb30SHans de Goede return ret;
5410cb8eb30SHans de Goede }
5420cb8eb30SHans de Goede }
5430cb8eb30SHans de Goede
544a9c6ce57SHans de Goede led_cdev->work_flags = 0;
545270c3957SRichard Purdie #ifdef CONFIG_LEDS_TRIGGERS
546270c3957SRichard Purdie init_rwsem(&led_cdev->trigger_lock);
547270c3957SRichard Purdie #endif
5480cb8eb30SHans de Goede #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
5490cb8eb30SHans de Goede led_cdev->brightness_hw_changed = -1;
5500cb8eb30SHans de Goede #endif
551c72a1d60SRichard Purdie /* add to the list of leds */
55272f8da32SRichard Purdie down_write(&leds_list_lock);
553c72a1d60SRichard Purdie list_add_tail(&led_cdev->node, &leds_list);
55472f8da32SRichard Purdie up_write(&leds_list_lock);
555c72a1d60SRichard Purdie
5561bd465e6SGuennadi Liakhovetski if (!led_cdev->max_brightness)
5571bd465e6SGuennadi Liakhovetski led_cdev->max_brightness = LED_FULL;
5581bd465e6SGuennadi Liakhovetski
55929d76dfaSHenrique de Moraes Holschuh led_update_brightness(led_cdev);
56029d76dfaSHenrique de Moraes Holschuh
561757b06aeSJacek Anaszewski led_init_core(led_cdev);
5625ada28bfSJohannes Berg
563c3bc9956SRichard Purdie #ifdef CONFIG_LEDS_TRIGGERS
56412fda168SJeff Garzik led_trigger_set_default(led_cdev);
565c3bc9956SRichard Purdie #endif
566c3bc9956SRichard Purdie
5676d71021aSLuis Henriques mutex_unlock(&led_cdev->led_access);
5686d71021aSLuis Henriques
569d23e7b8bSSachin Kamat dev_dbg(parent, "Registered led device: %s\n",
570f8a7c6feSRichard Purdie led_cdev->name);
571c72a1d60SRichard Purdie
572c72a1d60SRichard Purdie return 0;
573c72a1d60SRichard Purdie }
574b2b998c0SJacek Anaszewski EXPORT_SYMBOL_GPL(led_classdev_register_ext);
575c72a1d60SRichard Purdie
576c72a1d60SRichard Purdie /**
5770266a458SQinghuang Feng * led_classdev_unregister - unregisters a object of led_properties class.
57870d63cccSHenrik Kretzschmar * @led_cdev: the led device to unregister
579c72a1d60SRichard Purdie *
580c72a1d60SRichard Purdie * Unregisters a previously registered via led_classdev_register object.
581c72a1d60SRichard Purdie */
led_classdev_unregister(struct led_classdev * led_cdev)582b844eba2SRafael J. Wysocki void led_classdev_unregister(struct led_classdev *led_cdev)
583c72a1d60SRichard Purdie {
5841dbb9fb4SAndy Shevchenko if (IS_ERR_OR_NULL(led_cdev->dev))
5851dbb9fb4SAndy Shevchenko return;
5861dbb9fb4SAndy Shevchenko
587c3bc9956SRichard Purdie #ifdef CONFIG_LEDS_TRIGGERS
588dc47206eSRichard Purdie down_write(&led_cdev->trigger_lock);
589c3bc9956SRichard Purdie if (led_cdev->trigger)
590c3bc9956SRichard Purdie led_trigger_set(led_cdev, NULL);
591dc47206eSRichard Purdie up_write(&led_cdev->trigger_lock);
592c3bc9956SRichard Purdie #endif
593c72a1d60SRichard Purdie
594d84d80f3SHeiner Kallweit led_cdev->flags |= LED_UNREGISTERING;
595d84d80f3SHeiner Kallweit
5965ada28bfSJohannes Berg /* Stop blinking */
597d23a22a7SFabio Baltieri led_stop_software_blink(led_cdev);
598d1aa577fSMilo Kim
59941906632SEddie James if (!(led_cdev->flags & LED_RETAIN_AT_SHUTDOWN))
60019cd67e2SShuah Khan led_set_brightness(led_cdev, LED_OFF);
6015ada28bfSJohannes Berg
602d1aa577fSMilo Kim flush_work(&led_cdev->set_brightness_work);
603d1aa577fSMilo Kim
6040cb8eb30SHans de Goede if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
6050cb8eb30SHans de Goede led_remove_brightness_hw_changed(led_cdev);
6060cb8eb30SHans de Goede
607f8a7c6feSRichard Purdie device_unregister(led_cdev->dev);
608c72a1d60SRichard Purdie
60972f8da32SRichard Purdie down_write(&leds_list_lock);
610c72a1d60SRichard Purdie list_del(&led_cdev->node);
61172f8da32SRichard Purdie up_write(&leds_list_lock);
612acd899e4SJacek Anaszewski
613acd899e4SJacek Anaszewski mutex_destroy(&led_cdev->led_access);
614c72a1d60SRichard Purdie }
615b844eba2SRafael J. Wysocki EXPORT_SYMBOL_GPL(led_classdev_unregister);
616c72a1d60SRichard Purdie
devm_led_classdev_release(struct device * dev,void * res)617ca1bb4eeSBjorn Andersson static void devm_led_classdev_release(struct device *dev, void *res)
618ca1bb4eeSBjorn Andersson {
619ca1bb4eeSBjorn Andersson led_classdev_unregister(*(struct led_classdev **)res);
620ca1bb4eeSBjorn Andersson }
621ca1bb4eeSBjorn Andersson
622ca1bb4eeSBjorn Andersson /**
623b2b998c0SJacek Anaszewski * devm_led_classdev_register_ext - resource managed led_classdev_register_ext()
624442c6098SRafał Miłecki *
625442c6098SRafał Miłecki * @parent: parent of LED device
626ca1bb4eeSBjorn Andersson * @led_cdev: the led_classdev structure for this device.
627b2b998c0SJacek Anaszewski * @init_data: LED class device initialization data
628ca1bb4eeSBjorn Andersson */
devm_led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)629b2b998c0SJacek Anaszewski int devm_led_classdev_register_ext(struct device *parent,
630b2b998c0SJacek Anaszewski struct led_classdev *led_cdev,
631b2b998c0SJacek Anaszewski struct led_init_data *init_data)
632ca1bb4eeSBjorn Andersson {
633ca1bb4eeSBjorn Andersson struct led_classdev **dr;
634ca1bb4eeSBjorn Andersson int rc;
635ca1bb4eeSBjorn Andersson
636ca1bb4eeSBjorn Andersson dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
637ca1bb4eeSBjorn Andersson if (!dr)
638ca1bb4eeSBjorn Andersson return -ENOMEM;
639ca1bb4eeSBjorn Andersson
640b2b998c0SJacek Anaszewski rc = led_classdev_register_ext(parent, led_cdev, init_data);
641ca1bb4eeSBjorn Andersson if (rc) {
642ca1bb4eeSBjorn Andersson devres_free(dr);
643ca1bb4eeSBjorn Andersson return rc;
644ca1bb4eeSBjorn Andersson }
645ca1bb4eeSBjorn Andersson
646ca1bb4eeSBjorn Andersson *dr = led_cdev;
647ca1bb4eeSBjorn Andersson devres_add(parent, dr);
648ca1bb4eeSBjorn Andersson
649ca1bb4eeSBjorn Andersson return 0;
650ca1bb4eeSBjorn Andersson }
651b2b998c0SJacek Anaszewski EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext);
652ca1bb4eeSBjorn Andersson
devm_led_classdev_match(struct device * dev,void * res,void * data)653ca1bb4eeSBjorn Andersson static int devm_led_classdev_match(struct device *dev, void *res, void *data)
654ca1bb4eeSBjorn Andersson {
6554b83cf07SDan Murphy struct led_classdev **p = res;
656ca1bb4eeSBjorn Andersson
657ca1bb4eeSBjorn Andersson if (WARN_ON(!p || !*p))
658ca1bb4eeSBjorn Andersson return 0;
659ca1bb4eeSBjorn Andersson
660ca1bb4eeSBjorn Andersson return *p == data;
661ca1bb4eeSBjorn Andersson }
662ca1bb4eeSBjorn Andersson
663ca1bb4eeSBjorn Andersson /**
664ca1bb4eeSBjorn Andersson * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
6656a3a871bSLee Jones * @dev: The device to unregister.
666ca1bb4eeSBjorn Andersson * @led_cdev: the led_classdev structure for this device.
667ca1bb4eeSBjorn Andersson */
devm_led_classdev_unregister(struct device * dev,struct led_classdev * led_cdev)668ca1bb4eeSBjorn Andersson void devm_led_classdev_unregister(struct device *dev,
669ca1bb4eeSBjorn Andersson struct led_classdev *led_cdev)
670ca1bb4eeSBjorn Andersson {
671ca1bb4eeSBjorn Andersson WARN_ON(devres_release(dev,
672ca1bb4eeSBjorn Andersson devm_led_classdev_release,
673ca1bb4eeSBjorn Andersson devm_led_classdev_match, led_cdev));
674ca1bb4eeSBjorn Andersson }
675ca1bb4eeSBjorn Andersson EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
676ca1bb4eeSBjorn Andersson
leds_init(void)677c72a1d60SRichard Purdie static int __init leds_init(void)
678c72a1d60SRichard Purdie {
67943a707aeSIvan Orlov return class_register(&leds_class);
680c72a1d60SRichard Purdie }
681c72a1d60SRichard Purdie
leds_exit(void)682c72a1d60SRichard Purdie static void __exit leds_exit(void)
683c72a1d60SRichard Purdie {
68443a707aeSIvan Orlov class_unregister(&leds_class);
685c72a1d60SRichard Purdie }
686c72a1d60SRichard Purdie
687c72a1d60SRichard Purdie subsys_initcall(leds_init);
688c72a1d60SRichard Purdie module_exit(leds_exit);
689c72a1d60SRichard Purdie
690c72a1d60SRichard Purdie MODULE_AUTHOR("John Lenz, Richard Purdie");
691c72a1d60SRichard Purdie MODULE_LICENSE("GPL");
692c72a1d60SRichard Purdie MODULE_DESCRIPTION("LED Class Interface");
693