1 // SPDX-License-Identifier: GPL-2.0-only
3 * LED Flash class interface
5 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
6 * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
9 #include <linux/device.h>
10 #include <linux/init.h>
11 #include <linux/led-class-flash.h>
12 #include <linux/leds.h>
13 #include <linux/module.h>
14 #include <linux/slab.h>
17 #define has_flash_op(fled_cdev, op) \
18 (fled_cdev && fled_cdev->ops->op)
20 #define call_flash_op(fled_cdev, op, args...) \
21 ((has_flash_op(fled_cdev, op)) ? \
22 (fled_cdev->ops->op(fled_cdev, args)) : \
25 static const char * const led_flash_fault_names
[] = {
27 "flash-timeout-exceeded",
28 "controller-over-temperature",
29 "controller-short-circuit",
30 "led-power-supply-over-current",
31 "indicator-led-fault",
33 "controller-under-voltage",
34 "led-over-temperature",
37 static ssize_t
flash_brightness_store(struct device
*dev
,
38 struct device_attribute
*attr
, const char *buf
, size_t size
)
40 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
41 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
45 mutex_lock(&led_cdev
->led_access
);
47 if (led_sysfs_is_disabled(led_cdev
)) {
52 ret
= kstrtoul(buf
, 10, &state
);
56 ret
= led_set_flash_brightness(fled_cdev
, state
);
62 mutex_unlock(&led_cdev
->led_access
);
66 static ssize_t
flash_brightness_show(struct device
*dev
,
67 struct device_attribute
*attr
, char *buf
)
69 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
70 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
72 /* no lock needed for this */
73 led_update_flash_brightness(fled_cdev
);
75 return sprintf(buf
, "%u\n", fled_cdev
->brightness
.val
);
77 static DEVICE_ATTR_RW(flash_brightness
);
79 static ssize_t
max_flash_brightness_show(struct device
*dev
,
80 struct device_attribute
*attr
, char *buf
)
82 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
83 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
85 return sprintf(buf
, "%u\n", fled_cdev
->brightness
.max
);
87 static DEVICE_ATTR_RO(max_flash_brightness
);
89 static ssize_t
flash_strobe_store(struct device
*dev
,
90 struct device_attribute
*attr
, const char *buf
, size_t size
)
92 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
93 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
95 ssize_t ret
= -EINVAL
;
97 mutex_lock(&led_cdev
->led_access
);
99 if (led_sysfs_is_disabled(led_cdev
)) {
104 ret
= kstrtoul(buf
, 10, &state
);
113 ret
= led_set_flash_strobe(fled_cdev
, state
);
118 mutex_unlock(&led_cdev
->led_access
);
122 static ssize_t
flash_strobe_show(struct device
*dev
,
123 struct device_attribute
*attr
, char *buf
)
125 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
126 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
130 /* no lock needed for this */
131 ret
= led_get_flash_strobe(fled_cdev
, &state
);
135 return sprintf(buf
, "%u\n", state
);
137 static DEVICE_ATTR_RW(flash_strobe
);
139 static ssize_t
flash_timeout_store(struct device
*dev
,
140 struct device_attribute
*attr
, const char *buf
, size_t size
)
142 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
143 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
144 unsigned long flash_timeout
;
147 mutex_lock(&led_cdev
->led_access
);
149 if (led_sysfs_is_disabled(led_cdev
)) {
154 ret
= kstrtoul(buf
, 10, &flash_timeout
);
158 ret
= led_set_flash_timeout(fled_cdev
, flash_timeout
);
164 mutex_unlock(&led_cdev
->led_access
);
168 static ssize_t
flash_timeout_show(struct device
*dev
,
169 struct device_attribute
*attr
, char *buf
)
171 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
172 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
174 return sprintf(buf
, "%u\n", fled_cdev
->timeout
.val
);
176 static DEVICE_ATTR_RW(flash_timeout
);
178 static ssize_t
max_flash_timeout_show(struct device
*dev
,
179 struct device_attribute
*attr
, char *buf
)
181 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
182 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
184 return sprintf(buf
, "%u\n", fled_cdev
->timeout
.max
);
186 static DEVICE_ATTR_RO(max_flash_timeout
);
188 static ssize_t
flash_fault_show(struct device
*dev
,
189 struct device_attribute
*attr
, char *buf
)
191 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
192 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
193 u32 fault
, mask
= 0x1;
197 ret
= led_get_flash_fault(fled_cdev
, &fault
);
203 for (i
= 0; i
< LED_NUM_FLASH_FAULTS
; ++i
) {
205 buf_len
= sprintf(pbuf
, "%s ",
206 led_flash_fault_names
[i
]);
212 return sprintf(buf
, "%s\n", buf
);
214 static DEVICE_ATTR_RO(flash_fault
);
216 static struct attribute
*led_flash_strobe_attrs
[] = {
217 &dev_attr_flash_strobe
.attr
,
221 static struct attribute
*led_flash_timeout_attrs
[] = {
222 &dev_attr_flash_timeout
.attr
,
223 &dev_attr_max_flash_timeout
.attr
,
227 static struct attribute
*led_flash_brightness_attrs
[] = {
228 &dev_attr_flash_brightness
.attr
,
229 &dev_attr_max_flash_brightness
.attr
,
233 static struct attribute
*led_flash_fault_attrs
[] = {
234 &dev_attr_flash_fault
.attr
,
238 static const struct attribute_group led_flash_strobe_group
= {
239 .attrs
= led_flash_strobe_attrs
,
242 static const struct attribute_group led_flash_timeout_group
= {
243 .attrs
= led_flash_timeout_attrs
,
246 static const struct attribute_group led_flash_brightness_group
= {
247 .attrs
= led_flash_brightness_attrs
,
250 static const struct attribute_group led_flash_fault_group
= {
251 .attrs
= led_flash_fault_attrs
,
254 static void led_flash_resume(struct led_classdev
*led_cdev
)
256 struct led_classdev_flash
*fled_cdev
= lcdev_to_flcdev(led_cdev
);
258 call_flash_op(fled_cdev
, flash_brightness_set
,
259 fled_cdev
->brightness
.val
);
260 call_flash_op(fled_cdev
, timeout_set
, fled_cdev
->timeout
.val
);
263 static void led_flash_init_sysfs_groups(struct led_classdev_flash
*fled_cdev
)
265 struct led_classdev
*led_cdev
= &fled_cdev
->led_cdev
;
266 const struct led_flash_ops
*ops
= fled_cdev
->ops
;
267 const struct attribute_group
**flash_groups
= fled_cdev
->sysfs_groups
;
269 int num_sysfs_groups
= 0;
271 flash_groups
[num_sysfs_groups
++] = &led_flash_strobe_group
;
273 if (ops
->flash_brightness_set
)
274 flash_groups
[num_sysfs_groups
++] = &led_flash_brightness_group
;
276 if (ops
->timeout_set
)
277 flash_groups
[num_sysfs_groups
++] = &led_flash_timeout_group
;
280 flash_groups
[num_sysfs_groups
++] = &led_flash_fault_group
;
282 led_cdev
->groups
= flash_groups
;
285 int led_classdev_flash_register_ext(struct device
*parent
,
286 struct led_classdev_flash
*fled_cdev
,
287 struct led_init_data
*init_data
)
289 struct led_classdev
*led_cdev
;
290 const struct led_flash_ops
*ops
;
296 led_cdev
= &fled_cdev
->led_cdev
;
298 if (led_cdev
->flags
& LED_DEV_CAP_FLASH
) {
299 if (!led_cdev
->brightness_set_blocking
)
302 ops
= fled_cdev
->ops
;
303 if (!ops
|| !ops
->strobe_set
)
306 led_cdev
->flash_resume
= led_flash_resume
;
308 /* Select the sysfs attributes to be created for the device */
309 led_flash_init_sysfs_groups(fled_cdev
);
312 /* Register led class device */
313 ret
= led_classdev_register_ext(parent
, led_cdev
, init_data
);
319 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext
);
321 void led_classdev_flash_unregister(struct led_classdev_flash
*fled_cdev
)
326 led_classdev_unregister(&fled_cdev
->led_cdev
);
328 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister
);
330 static void devm_led_classdev_flash_release(struct device
*dev
, void *res
)
332 led_classdev_flash_unregister(*(struct led_classdev_flash
**)res
);
335 int devm_led_classdev_flash_register_ext(struct device
*parent
,
336 struct led_classdev_flash
*fled_cdev
,
337 struct led_init_data
*init_data
)
339 struct led_classdev_flash
**dr
;
342 dr
= devres_alloc(devm_led_classdev_flash_release
, sizeof(*dr
),
347 ret
= led_classdev_flash_register_ext(parent
, fled_cdev
, init_data
);
354 devres_add(parent
, dr
);
358 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext
);
360 static int devm_led_classdev_flash_match(struct device
*dev
,
361 void *res
, void *data
)
363 struct led_classdev_flash
**p
= res
;
365 if (WARN_ON(!p
|| !*p
))
371 void devm_led_classdev_flash_unregister(struct device
*dev
,
372 struct led_classdev_flash
*fled_cdev
)
374 WARN_ON(devres_release(dev
,
375 devm_led_classdev_flash_release
,
376 devm_led_classdev_flash_match
, fled_cdev
));
378 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister
);
380 static void led_clamp_align(struct led_flash_setting
*s
)
384 v
= s
->val
+ s
->step
/ 2;
385 v
= clamp(v
, s
->min
, s
->max
);
387 offset
= s
->step
* (offset
/ s
->step
);
388 s
->val
= s
->min
+ offset
;
391 int led_set_flash_timeout(struct led_classdev_flash
*fled_cdev
, u32 timeout
)
393 struct led_classdev
*led_cdev
= &fled_cdev
->led_cdev
;
394 struct led_flash_setting
*s
= &fled_cdev
->timeout
;
399 if (!(led_cdev
->flags
& LED_SUSPENDED
))
400 return call_flash_op(fled_cdev
, timeout_set
, s
->val
);
404 EXPORT_SYMBOL_GPL(led_set_flash_timeout
);
406 int led_get_flash_fault(struct led_classdev_flash
*fled_cdev
, u32
*fault
)
408 return call_flash_op(fled_cdev
, fault_get
, fault
);
410 EXPORT_SYMBOL_GPL(led_get_flash_fault
);
412 int led_set_flash_brightness(struct led_classdev_flash
*fled_cdev
,
415 struct led_classdev
*led_cdev
= &fled_cdev
->led_cdev
;
416 struct led_flash_setting
*s
= &fled_cdev
->brightness
;
421 if (!(led_cdev
->flags
& LED_SUSPENDED
))
422 return call_flash_op(fled_cdev
, flash_brightness_set
, s
->val
);
426 EXPORT_SYMBOL_GPL(led_set_flash_brightness
);
428 int led_update_flash_brightness(struct led_classdev_flash
*fled_cdev
)
430 struct led_flash_setting
*s
= &fled_cdev
->brightness
;
433 if (has_flash_op(fled_cdev
, flash_brightness_get
)) {
434 int ret
= call_flash_op(fled_cdev
, flash_brightness_get
,
444 EXPORT_SYMBOL_GPL(led_update_flash_brightness
);
446 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
447 MODULE_DESCRIPTION("LED Flash class interface");
448 MODULE_LICENSE("GPL v2");