1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * LED Flash class interface 4 * 5 * Copyright (C) 2015 Samsung Electronics Co., Ltd. 6 * Author: Jacek Anaszewski <j.anaszewski@samsung.com> 7 */ 8 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> 15 #include "leds.h" 16 17 #define has_flash_op(fled_cdev, op) \ 18 (fled_cdev && fled_cdev->ops->op) 19 20 #define call_flash_op(fled_cdev, op, args...) \ 21 ((has_flash_op(fled_cdev, op)) ? \ 22 (fled_cdev->ops->op(fled_cdev, args)) : \ 23 -EINVAL) 24 25 static const char * const led_flash_fault_names[] = { 26 "led-over-voltage", 27 "flash-timeout-exceeded", 28 "controller-over-temperature", 29 "controller-short-circuit", 30 "led-power-supply-over-current", 31 "indicator-led-fault", 32 "led-under-voltage", 33 "controller-under-voltage", 34 "led-over-temperature", 35 }; 36 37 static ssize_t flash_brightness_store(struct device *dev, 38 struct device_attribute *attr, const char *buf, size_t size) 39 { 40 struct led_classdev *led_cdev = dev_get_drvdata(dev); 41 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 42 unsigned long state; 43 ssize_t ret; 44 45 mutex_lock(&led_cdev->led_access); 46 47 if (led_sysfs_is_disabled(led_cdev)) { 48 ret = -EBUSY; 49 goto unlock; 50 } 51 52 ret = kstrtoul(buf, 10, &state); 53 if (ret) 54 goto unlock; 55 56 ret = led_set_flash_brightness(fled_cdev, state); 57 if (ret < 0) 58 goto unlock; 59 60 ret = size; 61 unlock: 62 mutex_unlock(&led_cdev->led_access); 63 return ret; 64 } 65 66 static ssize_t flash_brightness_show(struct device *dev, 67 struct device_attribute *attr, char *buf) 68 { 69 struct led_classdev *led_cdev = dev_get_drvdata(dev); 70 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 71 72 /* no lock needed for this */ 73 led_update_flash_brightness(fled_cdev); 74 75 return sprintf(buf, "%u\n", fled_cdev->brightness.val); 76 } 77 static DEVICE_ATTR_RW(flash_brightness); 78 79 static ssize_t max_flash_brightness_show(struct device *dev, 80 struct device_attribute *attr, char *buf) 81 { 82 struct led_classdev *led_cdev = dev_get_drvdata(dev); 83 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 84 85 return sprintf(buf, "%u\n", fled_cdev->brightness.max); 86 } 87 static DEVICE_ATTR_RO(max_flash_brightness); 88 89 static ssize_t flash_strobe_store(struct device *dev, 90 struct device_attribute *attr, const char *buf, size_t size) 91 { 92 struct led_classdev *led_cdev = dev_get_drvdata(dev); 93 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 94 unsigned long state; 95 ssize_t ret = -EBUSY; 96 97 mutex_lock(&led_cdev->led_access); 98 99 if (led_sysfs_is_disabled(led_cdev)) 100 goto unlock; 101 102 ret = kstrtoul(buf, 10, &state); 103 if (ret) 104 goto unlock; 105 106 if (state > 1) { 107 ret = -EINVAL; 108 goto unlock; 109 } 110 111 ret = led_set_flash_strobe(fled_cdev, state); 112 if (ret < 0) 113 goto unlock; 114 ret = size; 115 unlock: 116 mutex_unlock(&led_cdev->led_access); 117 return ret; 118 } 119 120 static ssize_t flash_strobe_show(struct device *dev, 121 struct device_attribute *attr, char *buf) 122 { 123 struct led_classdev *led_cdev = dev_get_drvdata(dev); 124 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 125 bool state; 126 int ret; 127 128 /* no lock needed for this */ 129 ret = led_get_flash_strobe(fled_cdev, &state); 130 if (ret < 0) 131 return ret; 132 133 return sprintf(buf, "%u\n", state); 134 } 135 static DEVICE_ATTR_RW(flash_strobe); 136 137 static ssize_t flash_timeout_store(struct device *dev, 138 struct device_attribute *attr, const char *buf, size_t size) 139 { 140 struct led_classdev *led_cdev = dev_get_drvdata(dev); 141 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 142 unsigned long flash_timeout; 143 ssize_t ret; 144 145 mutex_lock(&led_cdev->led_access); 146 147 if (led_sysfs_is_disabled(led_cdev)) { 148 ret = -EBUSY; 149 goto unlock; 150 } 151 152 ret = kstrtoul(buf, 10, &flash_timeout); 153 if (ret) 154 goto unlock; 155 156 ret = led_set_flash_timeout(fled_cdev, flash_timeout); 157 if (ret < 0) 158 goto unlock; 159 160 ret = size; 161 unlock: 162 mutex_unlock(&led_cdev->led_access); 163 return ret; 164 } 165 166 static ssize_t flash_timeout_show(struct device *dev, 167 struct device_attribute *attr, char *buf) 168 { 169 struct led_classdev *led_cdev = dev_get_drvdata(dev); 170 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 171 172 return sprintf(buf, "%u\n", fled_cdev->timeout.val); 173 } 174 static DEVICE_ATTR_RW(flash_timeout); 175 176 static ssize_t max_flash_timeout_show(struct device *dev, 177 struct device_attribute *attr, char *buf) 178 { 179 struct led_classdev *led_cdev = dev_get_drvdata(dev); 180 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 181 182 return sprintf(buf, "%u\n", fled_cdev->timeout.max); 183 } 184 static DEVICE_ATTR_RO(max_flash_timeout); 185 186 static ssize_t flash_fault_show(struct device *dev, 187 struct device_attribute *attr, char *buf) 188 { 189 struct led_classdev *led_cdev = dev_get_drvdata(dev); 190 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 191 u32 fault, mask = 0x1; 192 char *pbuf = buf; 193 int i, ret, buf_len; 194 195 ret = led_get_flash_fault(fled_cdev, &fault); 196 if (ret < 0) 197 return -EINVAL; 198 199 *buf = '\0'; 200 201 for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) { 202 if (fault & mask) { 203 buf_len = sprintf(pbuf, "%s ", 204 led_flash_fault_names[i]); 205 pbuf += buf_len; 206 } 207 mask <<= 1; 208 } 209 210 return strlen(strcat(buf, "\n")); 211 } 212 static DEVICE_ATTR_RO(flash_fault); 213 214 static struct attribute *led_flash_strobe_attrs[] = { 215 &dev_attr_flash_strobe.attr, 216 NULL, 217 }; 218 219 static struct attribute *led_flash_timeout_attrs[] = { 220 &dev_attr_flash_timeout.attr, 221 &dev_attr_max_flash_timeout.attr, 222 NULL, 223 }; 224 225 static struct attribute *led_flash_brightness_attrs[] = { 226 &dev_attr_flash_brightness.attr, 227 &dev_attr_max_flash_brightness.attr, 228 NULL, 229 }; 230 231 static struct attribute *led_flash_fault_attrs[] = { 232 &dev_attr_flash_fault.attr, 233 NULL, 234 }; 235 236 static const struct attribute_group led_flash_strobe_group = { 237 .attrs = led_flash_strobe_attrs, 238 }; 239 240 static const struct attribute_group led_flash_timeout_group = { 241 .attrs = led_flash_timeout_attrs, 242 }; 243 244 static const struct attribute_group led_flash_brightness_group = { 245 .attrs = led_flash_brightness_attrs, 246 }; 247 248 static const struct attribute_group led_flash_fault_group = { 249 .attrs = led_flash_fault_attrs, 250 }; 251 252 static void led_flash_resume(struct led_classdev *led_cdev) 253 { 254 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 255 256 call_flash_op(fled_cdev, flash_brightness_set, 257 fled_cdev->brightness.val); 258 call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val); 259 } 260 261 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) 262 { 263 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 264 const struct led_flash_ops *ops = fled_cdev->ops; 265 const struct attribute_group **flash_groups = fled_cdev->sysfs_groups; 266 267 int num_sysfs_groups = 0; 268 269 flash_groups[num_sysfs_groups++] = &led_flash_strobe_group; 270 271 if (ops->flash_brightness_set) 272 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group; 273 274 if (ops->timeout_set) 275 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group; 276 277 if (ops->fault_get) 278 flash_groups[num_sysfs_groups++] = &led_flash_fault_group; 279 280 led_cdev->groups = flash_groups; 281 } 282 283 int led_classdev_flash_register_ext(struct device *parent, 284 struct led_classdev_flash *fled_cdev, 285 struct led_init_data *init_data) 286 { 287 struct led_classdev *led_cdev; 288 const struct led_flash_ops *ops; 289 int ret; 290 291 if (!fled_cdev) 292 return -EINVAL; 293 294 led_cdev = &fled_cdev->led_cdev; 295 296 if (led_cdev->flags & LED_DEV_CAP_FLASH) { 297 if (!led_cdev->brightness_set_blocking) 298 return -EINVAL; 299 300 ops = fled_cdev->ops; 301 if (!ops || !ops->strobe_set) 302 return -EINVAL; 303 304 led_cdev->flash_resume = led_flash_resume; 305 306 /* Select the sysfs attributes to be created for the device */ 307 led_flash_init_sysfs_groups(fled_cdev); 308 } 309 310 /* Register led class device */ 311 ret = led_classdev_register_ext(parent, led_cdev, init_data); 312 if (ret < 0) 313 return ret; 314 315 return 0; 316 } 317 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext); 318 319 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) 320 { 321 if (!fled_cdev) 322 return; 323 324 led_classdev_unregister(&fled_cdev->led_cdev); 325 } 326 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); 327 328 static void devm_led_classdev_flash_release(struct device *dev, void *res) 329 { 330 led_classdev_flash_unregister(*(struct led_classdev_flash **)res); 331 } 332 333 int devm_led_classdev_flash_register_ext(struct device *parent, 334 struct led_classdev_flash *fled_cdev, 335 struct led_init_data *init_data) 336 { 337 struct led_classdev_flash **dr; 338 int ret; 339 340 dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr), 341 GFP_KERNEL); 342 if (!dr) 343 return -ENOMEM; 344 345 ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data); 346 if (ret) { 347 devres_free(dr); 348 return ret; 349 } 350 351 *dr = fled_cdev; 352 devres_add(parent, dr); 353 354 return 0; 355 } 356 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext); 357 358 static int devm_led_classdev_flash_match(struct device *dev, 359 void *res, void *data) 360 { 361 struct led_classdev_flash **p = res; 362 363 if (WARN_ON(!p || !*p)) 364 return 0; 365 366 return *p == data; 367 } 368 369 void devm_led_classdev_flash_unregister(struct device *dev, 370 struct led_classdev_flash *fled_cdev) 371 { 372 WARN_ON(devres_release(dev, 373 devm_led_classdev_flash_release, 374 devm_led_classdev_flash_match, fled_cdev)); 375 } 376 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister); 377 378 static void led_clamp_align(struct led_flash_setting *s) 379 { 380 u32 v, offset; 381 382 v = s->val + s->step / 2; 383 v = clamp(v, s->min, s->max); 384 offset = v - s->min; 385 offset = s->step * (offset / s->step); 386 s->val = s->min + offset; 387 } 388 389 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout) 390 { 391 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 392 struct led_flash_setting *s = &fled_cdev->timeout; 393 394 s->val = timeout; 395 led_clamp_align(s); 396 397 if (!(led_cdev->flags & LED_SUSPENDED)) 398 return call_flash_op(fled_cdev, timeout_set, s->val); 399 400 return 0; 401 } 402 EXPORT_SYMBOL_GPL(led_set_flash_timeout); 403 404 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault) 405 { 406 return call_flash_op(fled_cdev, fault_get, fault); 407 } 408 EXPORT_SYMBOL_GPL(led_get_flash_fault); 409 410 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, 411 u32 brightness) 412 { 413 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 414 struct led_flash_setting *s = &fled_cdev->brightness; 415 416 s->val = brightness; 417 led_clamp_align(s); 418 419 if (!(led_cdev->flags & LED_SUSPENDED)) 420 return call_flash_op(fled_cdev, flash_brightness_set, s->val); 421 422 return 0; 423 } 424 EXPORT_SYMBOL_GPL(led_set_flash_brightness); 425 426 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev) 427 { 428 struct led_flash_setting *s = &fled_cdev->brightness; 429 u32 brightness; 430 431 if (has_flash_op(fled_cdev, flash_brightness_get)) { 432 int ret = call_flash_op(fled_cdev, flash_brightness_get, 433 &brightness); 434 if (ret < 0) 435 return ret; 436 437 s->val = brightness; 438 } 439 440 return 0; 441 } 442 EXPORT_SYMBOL_GPL(led_update_flash_brightness); 443 444 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 445 MODULE_DESCRIPTION("LED Flash class interface"); 446 MODULE_LICENSE("GPL v2"); 447