xref: /openbmc/linux/drivers/leds/led-class.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
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