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 = -EINVAL; 96 97 mutex_lock(&led_cdev->led_access); 98 99 if (led_sysfs_is_disabled(led_cdev)) { 100 ret = -EBUSY; 101 goto unlock; 102 } 103 104 ret = kstrtoul(buf, 10, &state); 105 if (ret) 106 goto unlock; 107 108 if (state > 1) { 109 ret = -EINVAL; 110 goto unlock; 111 } 112 113 ret = led_set_flash_strobe(fled_cdev, state); 114 if (ret < 0) 115 goto unlock; 116 ret = size; 117 unlock: 118 mutex_unlock(&led_cdev->led_access); 119 return ret; 120 } 121 122 static ssize_t flash_strobe_show(struct device *dev, 123 struct device_attribute *attr, char *buf) 124 { 125 struct led_classdev *led_cdev = dev_get_drvdata(dev); 126 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 127 bool state; 128 int ret; 129 130 /* no lock needed for this */ 131 ret = led_get_flash_strobe(fled_cdev, &state); 132 if (ret < 0) 133 return ret; 134 135 return sprintf(buf, "%u\n", state); 136 } 137 static DEVICE_ATTR_RW(flash_strobe); 138 139 static ssize_t flash_timeout_store(struct device *dev, 140 struct device_attribute *attr, const char *buf, size_t size) 141 { 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; 145 ssize_t ret; 146 147 mutex_lock(&led_cdev->led_access); 148 149 if (led_sysfs_is_disabled(led_cdev)) { 150 ret = -EBUSY; 151 goto unlock; 152 } 153 154 ret = kstrtoul(buf, 10, &flash_timeout); 155 if (ret) 156 goto unlock; 157 158 ret = led_set_flash_timeout(fled_cdev, flash_timeout); 159 if (ret < 0) 160 goto unlock; 161 162 ret = size; 163 unlock: 164 mutex_unlock(&led_cdev->led_access); 165 return ret; 166 } 167 168 static ssize_t flash_timeout_show(struct device *dev, 169 struct device_attribute *attr, char *buf) 170 { 171 struct led_classdev *led_cdev = dev_get_drvdata(dev); 172 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 173 174 return sprintf(buf, "%u\n", fled_cdev->timeout.val); 175 } 176 static DEVICE_ATTR_RW(flash_timeout); 177 178 static ssize_t max_flash_timeout_show(struct device *dev, 179 struct device_attribute *attr, char *buf) 180 { 181 struct led_classdev *led_cdev = dev_get_drvdata(dev); 182 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 183 184 return sprintf(buf, "%u\n", fled_cdev->timeout.max); 185 } 186 static DEVICE_ATTR_RO(max_flash_timeout); 187 188 static ssize_t flash_fault_show(struct device *dev, 189 struct device_attribute *attr, char *buf) 190 { 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; 194 char *pbuf = buf; 195 int i, ret, buf_len; 196 197 ret = led_get_flash_fault(fled_cdev, &fault); 198 if (ret < 0) 199 return -EINVAL; 200 201 *buf = '\0'; 202 203 for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) { 204 if (fault & mask) { 205 buf_len = sprintf(pbuf, "%s ", 206 led_flash_fault_names[i]); 207 pbuf += buf_len; 208 } 209 mask <<= 1; 210 } 211 212 return sprintf(buf, "%s\n", buf); 213 } 214 static DEVICE_ATTR_RO(flash_fault); 215 216 static struct attribute *led_flash_strobe_attrs[] = { 217 &dev_attr_flash_strobe.attr, 218 NULL, 219 }; 220 221 static struct attribute *led_flash_timeout_attrs[] = { 222 &dev_attr_flash_timeout.attr, 223 &dev_attr_max_flash_timeout.attr, 224 NULL, 225 }; 226 227 static struct attribute *led_flash_brightness_attrs[] = { 228 &dev_attr_flash_brightness.attr, 229 &dev_attr_max_flash_brightness.attr, 230 NULL, 231 }; 232 233 static struct attribute *led_flash_fault_attrs[] = { 234 &dev_attr_flash_fault.attr, 235 NULL, 236 }; 237 238 static const struct attribute_group led_flash_strobe_group = { 239 .attrs = led_flash_strobe_attrs, 240 }; 241 242 static const struct attribute_group led_flash_timeout_group = { 243 .attrs = led_flash_timeout_attrs, 244 }; 245 246 static const struct attribute_group led_flash_brightness_group = { 247 .attrs = led_flash_brightness_attrs, 248 }; 249 250 static const struct attribute_group led_flash_fault_group = { 251 .attrs = led_flash_fault_attrs, 252 }; 253 254 static void led_flash_resume(struct led_classdev *led_cdev) 255 { 256 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 257 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); 261 } 262 263 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) 264 { 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; 268 269 int num_sysfs_groups = 0; 270 271 flash_groups[num_sysfs_groups++] = &led_flash_strobe_group; 272 273 if (ops->flash_brightness_set) 274 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group; 275 276 if (ops->timeout_set) 277 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group; 278 279 if (ops->fault_get) 280 flash_groups[num_sysfs_groups++] = &led_flash_fault_group; 281 282 led_cdev->groups = flash_groups; 283 } 284 285 int led_classdev_flash_register_ext(struct device *parent, 286 struct led_classdev_flash *fled_cdev, 287 struct led_init_data *init_data) 288 { 289 struct led_classdev *led_cdev; 290 const struct led_flash_ops *ops; 291 int ret; 292 293 if (!fled_cdev) 294 return -EINVAL; 295 296 led_cdev = &fled_cdev->led_cdev; 297 298 if (led_cdev->flags & LED_DEV_CAP_FLASH) { 299 if (!led_cdev->brightness_set_blocking) 300 return -EINVAL; 301 302 ops = fled_cdev->ops; 303 if (!ops || !ops->strobe_set) 304 return -EINVAL; 305 306 led_cdev->flash_resume = led_flash_resume; 307 308 /* Select the sysfs attributes to be created for the device */ 309 led_flash_init_sysfs_groups(fled_cdev); 310 } 311 312 /* Register led class device */ 313 ret = led_classdev_register_ext(parent, led_cdev, init_data); 314 if (ret < 0) 315 return ret; 316 317 return 0; 318 } 319 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext); 320 321 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) 322 { 323 if (!fled_cdev) 324 return; 325 326 led_classdev_unregister(&fled_cdev->led_cdev); 327 } 328 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); 329 330 static void devm_led_classdev_flash_release(struct device *dev, void *res) 331 { 332 led_classdev_flash_unregister(*(struct led_classdev_flash **)res); 333 } 334 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) 338 { 339 struct led_classdev_flash **dr; 340 int ret; 341 342 dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr), 343 GFP_KERNEL); 344 if (!dr) 345 return -ENOMEM; 346 347 ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data); 348 if (ret) { 349 devres_free(dr); 350 return ret; 351 } 352 353 *dr = fled_cdev; 354 devres_add(parent, dr); 355 356 return 0; 357 } 358 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext); 359 360 static int devm_led_classdev_flash_match(struct device *dev, 361 void *res, void *data) 362 { 363 struct led_classdev_flash **p = res; 364 365 if (WARN_ON(!p || !*p)) 366 return 0; 367 368 return *p == data; 369 } 370 371 void devm_led_classdev_flash_unregister(struct device *dev, 372 struct led_classdev_flash *fled_cdev) 373 { 374 WARN_ON(devres_release(dev, 375 devm_led_classdev_flash_release, 376 devm_led_classdev_flash_match, fled_cdev)); 377 } 378 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister); 379 380 static void led_clamp_align(struct led_flash_setting *s) 381 { 382 u32 v, offset; 383 384 v = s->val + s->step / 2; 385 v = clamp(v, s->min, s->max); 386 offset = v - s->min; 387 offset = s->step * (offset / s->step); 388 s->val = s->min + offset; 389 } 390 391 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout) 392 { 393 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 394 struct led_flash_setting *s = &fled_cdev->timeout; 395 396 s->val = timeout; 397 led_clamp_align(s); 398 399 if (!(led_cdev->flags & LED_SUSPENDED)) 400 return call_flash_op(fled_cdev, timeout_set, s->val); 401 402 return 0; 403 } 404 EXPORT_SYMBOL_GPL(led_set_flash_timeout); 405 406 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault) 407 { 408 return call_flash_op(fled_cdev, fault_get, fault); 409 } 410 EXPORT_SYMBOL_GPL(led_get_flash_fault); 411 412 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, 413 u32 brightness) 414 { 415 struct led_classdev *led_cdev = &fled_cdev->led_cdev; 416 struct led_flash_setting *s = &fled_cdev->brightness; 417 418 s->val = brightness; 419 led_clamp_align(s); 420 421 if (!(led_cdev->flags & LED_SUSPENDED)) 422 return call_flash_op(fled_cdev, flash_brightness_set, s->val); 423 424 return 0; 425 } 426 EXPORT_SYMBOL_GPL(led_set_flash_brightness); 427 428 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev) 429 { 430 struct led_flash_setting *s = &fled_cdev->brightness; 431 u32 brightness; 432 433 if (has_flash_op(fled_cdev, flash_brightness_get)) { 434 int ret = call_flash_op(fled_cdev, flash_brightness_get, 435 &brightness); 436 if (ret < 0) 437 return ret; 438 439 s->val = brightness; 440 } 441 442 return 0; 443 } 444 EXPORT_SYMBOL_GPL(led_update_flash_brightness); 445 446 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); 447 MODULE_DESCRIPTION("LED Flash class interface"); 448 MODULE_LICENSE("GPL v2"); 449